3

HtmlUnit に関する私の以前の質問 では、HTML ユニット で 特定の Javascript の実行をスキップし、 HtmlUnit を使用してページ ソースを取得します: URL が動かなくなった

URL が動かなくなったと言いました。また、HtmlUnit ライブラリのメソッド (parse) の 1 つが実行されないためにスタックしていることがわかりました。

これについてさらに作業を行いました。完了までに指定されたタイムアウト秒以上かかる場合にメソッドから抜け出すコードを作成しました。

import java.io.IOException;
import java.net.MalformedURLException;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import com.gargoylesoftware.htmlunit.BrowserVersion;
import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlPage;

public class HandleHtmlUnitTimeout {

public static void main(String[] args) throws FailingHttpStatusCodeException, MalformedURLException, IOException, InterruptedException, TimeoutException 
    {   
        Date start = new Date();
        String url = "http://ericaweiner.com/collections/";
        doWorkWithTimeout(url, 60);
    }

public static void doWorkWithTimeout(final String url, long timeoutSecs) throws InterruptedException, TimeoutException {
    //maintains a thread for executing the doWork method
    ExecutorService executor = Executors.newFixedThreadPool(1);
    //logger.info("Starting method with "+timeoutSecs+" seconds as timeout");
    //set the executor thread working

    final Future<?> future = executor.submit(new Runnable() {
        public void run() 
            {
            try 
                {
                getPageSource(url);
                }
            catch (Exception e) 
                {
                throw new RuntimeException(e);
                }
        }
    });

    //check the outcome of the executor thread and limit the time allowed for it to complete
    try {
        future.get(timeoutSecs, TimeUnit.SECONDS);
    } catch (Exception e) {
        //ExecutionException: deliverer threw exception
        //TimeoutException: didn't complete within downloadTimeoutSecs
        //InterruptedException: the executor thread was interrupted

        //interrupts the worker thread if necessary
        future.cancel(true);

        //logger.warn("encountered problem while doing some work", e);
        throw new TimeoutException();
    }finally{ 
    executor.shutdownNow();
    }
}

public static void getPageSource(String productPageUrl)
    {
    try {
    if(productPageUrl == null)
        {
        productPageUrl = "http://ericaweiner.com/collections/";
        }   

        WebClient wb = new WebClient(BrowserVersion.FIREFOX_3_6);
        wb.getOptions().setTimeout(120000);
        wb.getOptions().setJavaScriptEnabled(true);
        wb.getOptions().setThrowExceptionOnScriptError(true);
        wb.getOptions().setThrowExceptionOnFailingStatusCode(false);
        HtmlPage page = wb.getPage(productPageUrl);
        wb.waitForBackgroundJavaScript(4000);
        wb.closeAllWindows();
} 
catch (FailingHttpStatusCodeException e) 
    {
    e.printStackTrace();
    } 
catch (MalformedURLException e) 
    {
    e.printStackTrace();
    } 
catch (IOException e) 
    {
    e.printStackTrace();
    }
    }

}

このコードは doWorkWithTimeout(url, 60); から出てきます。方法。しかし、これでは終わりません。

次のコードで同様の実装を呼び出そうとすると:

import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import org.apache.log4j.Logger;


public class HandleScraperTimeOut {

private static Logger logger = Logger.getLogger(HandleScraperTimeOut .class);


public void doWork() throws InterruptedException {
    logger.info(new Date()+ "Starting worker method ");
    Thread.sleep(20000);
    logger.info(new Date()+ "Ending worker method ");
    //perform some long running task here...
}

public void doWorkWithTimeout(int timeoutSecs) {
    //maintains a thread for executing the doWork method
    ExecutorService executor = Executors.newFixedThreadPool(1);
    logger.info("Starting method with "+timeoutSecs+" seconds as timeout");
    //set the executor thread working

    final Future<?> future = executor.submit(new Runnable() {
        public void run() 
            {
            try 
                {
                doWork();
                }
            catch (Exception e) 
                {
                throw new RuntimeException(e);
                }
        }
    });

    //check the outcome of the executor thread and limit the time allowed for it to complete
    try {
        future.get(timeoutSecs, TimeUnit.SECONDS);
    } catch (Exception e) {
        //ExecutionException: deliverer threw exception
        //TimeoutException: didn't complete within downloadTimeoutSecs
        //InterruptedException: the executor thread was interrupted

        //interrupts the worker thread if necessary
        future.cancel(true);

        logger.warn("encountered problem while doing some work", e);
    }
    executor.shutdown();
}

public static void main(String a[])
    {
        HandleScraperTimeOut hcto = new HandleScraperTimeOut ();
        hcto.doWorkWithTimeout(30);

    }

}

誰かが見て、何が問題なのか教えてくれれば、本当に役に立ちます。

問題の詳細については、「HTML ユニットで特定の Javascript の実行をスキップする」 および 「HtmlUnit を使用してページ ソースを取得する: URL が動かなくなった」を参照してください。


更新 1 奇妙なことは: future.cancel(true); どちらの場合もTRUEを返しています。私が期待していたのは次のとおりです。

  • HtmlUnit では、プロセスがまだハングしているため、FALSE を返す必要があります。
  • 通常の Thread.sleep(); では プロセスが正常にキャンセルされたため、TRUE が返されるはずです。

更新 2 URL でのみハングしhttp://ericaweiner.com/collections/ます。それ以外の URL を指定するhttp://www.google.comと、http://www.yahoo.com、 は処理されません。これらの場合、IntruptedException をスローし、Process から抜け出します。

http://ericaweiner.com/collections/ページ ソースには、問題を引き起こしている特定の要素が含まれているようです。

4

1 に答える 1

0

Future.cancel(boolean) は以下を返します:

  • タスクをキャンセルできなかった場合は false (通常、タスクが正常に完了したため)
  • そうでなければ真

キャンセルされたということは、スレッドがキャンセル前に終了しなかったこと、キャンセルされたフラグが true に設定されたこと、および要求された場合にスレッドが中断されたことを意味します。

Thread.interrupt と呼ばれるスレッドメナンを中断します。Future.cancel(boolean) は、スレッドが実際に停止したかどうかをチェックしません。

したがって、cancel がその場合に true を返すのは正しいことです。

スレッドの中断は、できるだけ早く停止する必要があることを意味しますが、強制されません。必要なリソースのクローズを停止/失敗させることができます。私は通常、ソケットからスレッド読み取り (受信データを待機) を使用してこれを行います。ソケットを閉じて、待機を停止します。

于 2013-12-04T14:20:41.307 に答える