【问题标题】:Java - Stop thead after time without blocking main threadJava - 一段时间后停止线程而不阻塞主线程
【发布时间】:2017-06-15 08:16:55
【问题描述】:

我正在制作一个接收 HTTP 请求的 Java 应用程序。对于传入的每个请求,我都会启动一个新线程,并在该线程中读取请求并执行必要的操作。但是,我想阻止用户执行“Slow Loris Attack”,所以我正在考虑给线程一个maxTime 值。如果线程花费的时间超过maxTime,无论如何它都会终止。所以它也会阻止慢速连接,这不是问题。

但是,我不知道这样做的正确方法是什么。我试过下面的代码,但是这段代码阻塞了我的主线程。我正在寻找一种方法来做类似这样的事情,而不会阻塞主线程。

代码:

/**
 * Executor which is used for threadpools.
 */
private ExecutorService executor;

/**
 * Constructor for the class RequestReceiver.
 * Initializes fields.
 */
public RequestReceiver() {
    this.executor = Executors.newFixedThreadPool(200);
}

@Override
public void run() {
    try {
        this.serverSocket = new ServerSocket(port);
    } catch (IOException ex) {
        Logger.getInstance().logText("Could not start server on port: " + port);
        return;
    }

    Logger.getInstance().logText("Server running at port: " + port);

    try {
        while (shouldContinue) {
            Socket client = serverSocket.accept();
            HTTPRequestHandler handler = new HTTPRequestHandler(client);
            Thread t = new Thread(handler);
            executor.submit(t).get(10, TimeUnit.SECONDS); //This line is blocking
        }
    } catch (IOException ex) {
        Logger.getInstance().logText("Server is shutdown");
    } catch (InterruptedException | ExecutionException | TimeoutException ex) {
        Logger.getInstance().logText("Thread took too long, it's shutdown");
    }
}

【问题讨论】:

  • 能否在阻塞语句executor.submit(t).get(10, TimeUnit.SECONDS); 之后立即添加executor.shutdown(); 并尝试?
  • @harshavmb 我不明白这将如何解决我的问题。我确实尝试过,就像我预测的那样它崩溃了。因为当您发送第二个 HTTP 请求时,池不会接受它,因为它已关闭。 java.util.concurrent.RejectedExecutionException
  • 你为什么在你的主线程上调用 Future#get?你甚至什么都不做。另外,TimeOutException 不会停止任务。
  • @matt 因为我在 SO 上找到了多个答案,说明你可以使用这个。 stackoverflow.com/a/2733370/4653908(见 cmets)。
  • 答案还是 cmets?我不遵循您所链接的内容,情况似乎有所不同。我通过示例提供了答案,因此.get() 不会阻塞您的主线程。

标签: java multithreading


【解决方案1】:

对您的示例所做的最小更改就是提交一个新任务。

Socket client = serverSocket.accept();
HTTPRequestHandler handler = new HTTPRequestHandler(client);
Future f = executor.submit(handler);
executor.submit(()->{
    try{
        f.get(10, TimeUnit.SECONDS);
    } catch(TimeoutException to){
        //timeout happened, this will cancel/interrupt the task.
        f.cancel(true);
    } catch(Exception e){
        throw new RuntimeException(e);
        //something else went wrong...
    }
});

这可行,但会阻塞等待 get 调用的额外线程。您还需要在 HTTPRequestHandler 代码中处理中断。

另一种方法是使用ScheduledExecutorService

【讨论】:

    【解决方案2】:

    您的方法存在一些问题:

    我正在制作一个接收 HTTP 请求的 Java 应用程序。对于传入的每个请求,我都会启动一个新线程,并在该线程中读取请求并执行必要的操作。

    这是耗尽内存或其他资源的可靠方法。相反,在 servlet 容器(tomcat、jetty)中运行您的应用程序并让它为您处理多线程。只要确保处理请求的代码是“线程安全的”,因为它将被多个线程同时调用。

    如果您必须使用 ServerSocket,则使用具有固定线程数的 ExecutorService。 (永远不要使用无限数量的线程)

    但是,我想阻止用户执行“Slow Loris Attack”

    为防止slowloris 攻击,您应该在http 服务器(例如Apache)后面运行您的应用程序,并确保安装适当的安全模块。例如:http://www.techrepublic.com/blog/smb-technologist/secure-your-apache-server-from-ddos-slowloris-and-dns-injection-attacks/

    我正在考虑给线程一个 maxTime 值。如果线程花费的时间比 maxTime 长,它无论如何都会终止。

    这不是一个好主意。一般来说,当你收到一个 HTTP 请求时,控制器(处理请求的代码)必须尽可能快地回复,但是做到这一点的方法是通过优化请求的处理(例如,不做操作处理http请求时花费的时间太长)。 如果您的处理时间很快,但从客户端的角度来看,服务器没有响应,那么您应该考虑分发您的应用程序(将服务器的许多实例放在负载均衡器后面)

    【讨论】:

      猜你喜欢
      • 2012-01-19
      • 1970-01-01
      • 1970-01-01
      • 2011-11-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多