简体   繁体   English

Java套接字读取操作未在线程中超时

[英]Java Socket read operation not timing out in thread

I was surprised to find Java concurrency timeouts do not stop blocked socket read operation in the thread. 我很惊讶地发现Java并发超时没有停止线程中阻塞的套接字读取操作。

I was using Selenium RemoteWebDriver to simulate a load test scenario. 我正在使用Selenium RemoteWebDriver来模拟负载测试方案。 I have wrapped the execution of Selenium commands in a Callable and used get() method with the timeout parameter. 我已经将Selenium命令的执行包装在Callable ,并将get()方法与timeout参数一起使用。 It works perfectly when there are less than 3 concurrent threads executing but the situation deteriorates when there are 4 or more concurrent threads. 当执行的并发线程少于3个时,它可以完美工作,但是当并发线程数为4个或更多时,情况会恶化。 Some of the threads get stuck at socket read and are stuck for longer than the timeout setting on the Callable . 一些线程在套接字读取时被卡住,并且被卡住的时间超过Callable上的超时设置。

I did some research online, the root cause was a hard-coded socket timeout of 3 hours in Selenium code. 我在网上做了一些研究,根本原因是Selenium代码中3个小时的硬编码套接字超时。 There used to be a hack to overwrite the setting using reflection but with the latest version I don't think it's hackable any more. 过去曾经有黑客利用反射来覆盖设置,但是对于最新版本,我认为它不再是可入侵的。

I wonder whether there is a way to stop the thread that is IO blocked externally since I don't want to change Selenium code and end up having to maintain my own version of Selenium. 我想知道是否有一种方法可以停止被IO外部阻塞的线程,因为我不想更改Selenium代码并最终不得不维护自己的Selenium版本。

Here's how I handle the thread timeout in my code: 这是我在代码中处理线程超时的方法:

ExecutorService executorService = Executors.newSingleThreadExecutor();
Future<Long> future = null;
try {
    future = executorService.submit(new Callable<Long>() {
         @Override
         public Long call() throws Exception {
             return commandProcessor.process(row);
         }
     });   
timeTaken = future.get(currentTimeout, TimeUnit.MILLISECONDS);
executorService.shutdown();
} catch (Exception e) {
    log.error(e.getMessage(), e);
    // cancel the task
    if (future != null && !future.isDone()) {
         future.cancel(true);
    }
    executorService.shutdownNow();
}

And since it fails to timeout randomly, I even created another daemon thread to monitor this thread and shut it down from the outside, but it still fails to terminate the thread when socket read blocks. 而且由于它无法随机超时,所以我什至创建了另一个守护程序线程来监视该线程并将其从外部关闭,但是当套接字读取阻塞时,它仍然无法终止该线程。

In the try block: 在try块中:

TimeoutDaemon timeoutDaemon = new TimeoutDaemon(future, executorService, timeStarted);

// put a daemon on the main execution thread so it behaves
ExecutorService daemonExecutorService = Executors.newSingleThreadExecutor();
daemonExecutorService.submit(timeoutDaemon);

The TimeoutDaemon class: TimeoutDaemon类:

private class TimeoutDaemon implements Runnable {

    private Future<?> future;
    private long timeStarted;
    private ExecutorService taskExecutorService;

    private TimeoutDaemon(Future<?> future, ExecutorService taskExecutorService, long timeStarted) {
        this.future = future;
        this.timeStarted = timeStarted;
        this.taskExecutorService = taskExecutorService;
    }

    @Override
    public void run() {
        boolean running = true;
        while (running) {
            long currentTime = System.currentTimeMillis();
            if (currentTime - timeStarted > currentTimeout + 1000) {
                running = false;
                if (!future.isDone()) {
                    String message = "Command execution is taking longer (%d ms) than the current timeout setting %d. Canceling the execution.";
                    message = String.format(message, currentTime - timeStarted, currentTimeout);
                    taskExecutorService.shutdownNow();
                }
            } else {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    log.error("Timeout Daemon interrupted. Test may be stuck. Close stuck browser windows if any.", e);
                }
            }
        }
    }
}

The only way I know of, is to close the sockets. 我知道的唯一方法是关闭插座。 You're right it's disappointing that the api doesn't allow interrupt or something. 没错,API不允许中断或其他令人失望的事情。 see also Interrupt/stop thread with socket I/O blocking operation 另请参见具有套接字I / O阻止操作的中断/停止线程

From the API spec: 根据API规范:

List shutdownNow() Attempts to stop all actively executing tasks, halts the processing of waiting tasks, and returns a list of the tasks that were awaiting execution. 列表shutdownNow()尝试停止所有正在执行的任务,暂停正在等待的任务的处理,并返回正在等待执行的任务的列表。 This method does not wait for actively executing tasks to terminate. 此方法不等待主动执行的任务终止。 Use awaitTermination to do that. 使用awaitTermination可以做到这一点。

There are no guarantees beyond best-effort attempts to stop processing actively executing tasks. 除了尽最大努力尝试停止处理正在执行的任务之外,没有任何保证。 For example, typical implementations will cancel via Thread.interrupt(), so any task that fails to respond to interrupts may never terminate. 例如,典型的实现将通过Thread.interrupt()取消, 因此任何无法响应中断的任务都可能永远不会终止。

You cannot interrupt Socket.read() operation. 您不能中断Socket.read()操作。

You can take a look at the NIO package which offers InterruptibleChannel. 您可以看一下提供InterruptibleChannel的NIO软件包。 It is possible to interrupt read and write operations on InterruptibleChannel by invoking its close method. 可以通过调用其close方法来中断InterruptibleChannel上的读取和写入操作。

From the API: 从API:

If a thread is blocked in an I/O operation on an interruptible channel then another thread may invoke the channel's close method . 如果某个线程在可中断通道的I / O操作中被阻塞,则另一个线程可以调用该通道的close方法 This will cause the blocked thread to receive an AsynchronousCloseException. 这将导致被阻止的线程接收一个AsynchronousCloseException。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM