【问题标题】:Java: nice way to stop threaded TCP server?Java:停止线程 TCP 服务器的好方法?
【发布时间】:2011-04-11 15:28:48
【问题描述】:

我有以下 TCP 客户端-服务器通信结构:

  • 在服务器启动时服务器启动 接受者线程,接受客户端 连接和传递 ServerSocket 给它。
  • 当客户端连接到达时, 接受者线程调用accept() ServerSocket 并提交客户端 处理工作到工作线程 (通过执行程序/线程池)并为其提供客户端套接字。
  • 循环中的工作线程从 客户端套接字流,对其进行处理并发送回复。

问题是如何优雅地停止整个系统?我可以通过关闭 ServerSocket 来停止接受线程。它将导致accept() 阻塞调用抛出SocketException。但是如何阻止工人呢?他们从流中读取,这个调用被阻塞了。根据this,流不会抛出 InterruptedException,因此工作者不能被中断()。

看起来我需要从另一个线程关闭工作套接字,对吧?为此,应将套接字设为公共字段,或者应在 worker 中提供关闭它的方法。这会好吗?或者可能是我的整个设计有缺陷?

【问题讨论】:

    标签: java multithreading concurrent-programming


    【解决方案1】:

    您的模型工作正常。中断像 IO 这样的不可中断结构的最好方法是关闭套接字。你当然可以在进入阻塞状态之前处理它,但是如果 IO 函数对中断没有反应,你真的没有很多好的选择

    【讨论】:

      【解决方案2】:

      我建议使用工人定期检查的布尔标志。调用标志shouldStop,如果它设置为true,工人清理然后死亡。遵循此方法将允许您实现一些清理代码,这样您就不会让资源挂起等。

      【讨论】:

      • 最佳实践是使用 Thread.interrupted()。这是标准解决方案,可以在 ThreadPool 中正常工作。
      • @Andrey Vityuk Thread.interrupted 实际上不是很好的做法,因为它重置了中断标志。您可能指的是 Thread.interrupt 或 Thread.isInterrupted。话虽如此,正如我在回答中所说,许多 IO 构造没有考虑线程中断,因此(尽管我部分同意您的评论)它在这种情况下并不成立。
      • @Andrey 中断从套接字读取被阻塞的线程不会中断它。关闭线程的唯一方法是关闭套接字并捕获异常 SocketException 以进行一些清理。
      【解决方案3】:

      您不能简单地停止服务器。关闭过程可能需要一段时间才能进行清理,因为您需要确保一致性。

      想象一个数据库服务器,如果您只是在它执行事务时将其关闭,您可能会导致其数据不一致。这就是关闭服务器通常需要一段时间的原因。

      • 您必须先停止接受新的 服务器中的连接。
      • 然后您可以等待 当前要完成的工作线程 他们的工作,然后关闭 服务器并正式关闭。
      • 或者你强制工作线程 关闭他们与
        的联系 客户端(可能使用某种类型
        建议的标志)。这有可能 暗示一些清理以留下数据 一致的,例如还原 交易或任何类型的更改 你已经在文件或内存中完成了。

      据我所知,在服务器端关闭与客户端的连接应该会导致客户端在他们身边获得 EOF。

      [EDIT-1]

      我对这个问题进行了一些研究,只是因为我有一段时间没有使用套接字,而且我发现这个问题很有趣。我认为正如其他人所指出的那样,唯一的选择是关闭套接字,根据得到的Javadocs会自动关闭输入和输出流/

      如果线程有机会没有被IO阻塞,而是处于等待状态或睡眠状态,我认为仍然建议为给定套接字的相应工作线程发出Thread.interrupt();因为无法确定每个线程的阻塞状态。

      public static class IOServerWorker implements Runnable{
      
              private Socket socket;
      
              public IOServerWorker(Socket socket){
                  this.socket = socket;
              }
      
              @Override
              public void run() {
                  String line = null;
                  try{
                      BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                      while( (line = reader.readLine())!=null){
                          System.out.println(line);
                      }
                      reader.close();
                  }catch(IOException e){
                      //TODO: do cleanup here
                      //TODO: log | wrap | rethrow exception
                  }
              }
          }
      

      【讨论】:

      • 我非常了解这一点。 “那么你可以等待当前的工作线程完成他们的工作” - 如果你再次阅读我的问题,你会发现这是一个问题。工作者可以通过从套接字流中读取来阻塞。问题是如何优雅地打断他们。标记在这里不起作用,因为工作人员在阻塞 I/O 状态时无法检查它
      • “您不能简单地停止服务器。”实际上,如果您的服务器设计正确(ACID 事务和所有),只需停止它即可。导致数据库提交的更改被视为已发生,而未发生的更改从未发生。使这项工作顺利进行是将数据库与中间业务逻辑和 Web 前端分离的重要原因之一。
      猜你喜欢
      • 2011-02-17
      • 2020-01-23
      • 2019-01-04
      • 2011-07-11
      • 2019-11-30
      • 1970-01-01
      • 1970-01-01
      • 2019-06-18
      • 1970-01-01
      相关资源
      最近更新 更多