【问题标题】:Shutdown thread disappears when performing endpoints call执行端点调用时关闭线程消失
【发布时间】:2018-01-15 09:44:39
【问题描述】:

我有一个在 Ubuntu 16.04 LTS 上运行的 Java 应用程序。

当应用程序接收到关闭信号时,会像这样运行关闭序列:

    Runtime.getRuntime().addShutdownHook(new Thread() {
        @Override
        public void run() {
            shutdown();
        }
    });

这很好用,但是一旦我尝试调用外部 REST 端点(我正在使用带有 Rx Observables 的 Retrofit),线程就会完全消失在那里,永远不会调用端点,并且不再执行连续的命令。

  • 我首先认为 Observables 会是问题所在,并改用了 Retrofit Call。同样的问题。
  • 然后我尝试进行同步调用,而不是异步调用端点。同样的问题。
  • 关机时间不是问题。我明确给出了 30 秒。

我认为这与线程有关,例如当一个库在关闭时创建额外的线程时,JVM 似乎会杀死一切。

请任何人阐明或建议我还可以尝试什么。

--

额外信息:

我需要执行长时间运行的清理。问题是我无法确定关闭信号的外观和发生时间,因为 JVM 在 Docker 容器中运行,该容器在 docker stop(主机停止时)首先发送 SIGTERM,然后在超时后(我可以设置,目前为 60 秒)SIGKILL。容器中的 JVM 运行 socket.io,可能会连接数千个客户端。我想使用 60 秒向每个客户端发送 good-bye 并完全断开这些连接,以及从负载均衡器中注销服务器。所以在清理过程中有很多潜在的阻塞操作。

如果 Java 认为清理总是很短,那么 Java 就错了:(

【问题讨论】:

  • Shutdown hooks应该尽快退出,当然不能参与网络操作。请参阅 Javadoc。

标签: java multithreading sigkill sigterm


【解决方案1】:

在您的情况下关闭 JVM 时会引发中断标志。因此,一旦您调用任何阻塞操作,该操作将立即被 InterruptedException 终止。

如果您必须在应用程序终止时执行进一步的、可能持续时间较长的代码,那么关闭钩子不是可行的方法。它的存在是为了关闭开放资源,而不是为了创建新资源。

由于您没有提供有关代码的更多信息,因此我无法给出确切的建议,但总体思路是让主线程在应用程序运行时等待,并让主线程进行清理工作。但是,在这种情况下,您需要一个不是系统关闭信号的关闭信号,以便应用程序可以继续正常运行。

如果由于某种原因太复杂,您可以尝试在关闭挂钩开始时清除中断标志,方法是:

try {
  Thread.sleep(1);
} catch (InterruptedException e) {
}

然而,这违反了规则,这些规则的存在是有原因的。 IE。如果操作系统对终止信号的反应不够快并以更糟糕的方式终止它,则操作系统可能会认为您的程序已挂起。

【讨论】:

  • 这回答了我的问题。如果您有更多想法,我仍然在上面添加了额外的信息。但我想中断线程是我唯一能做的事情,而且如果我被中断的线程没有按时退出,“OS is Docker”将发送SIGKILL,这是相当安全的。
猜你喜欢
  • 2012-12-26
  • 1970-01-01
  • 2016-11-30
  • 2013-12-17
  • 1970-01-01
  • 1970-01-01
  • 2017-01-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多