执行终结点调用时关闭线程消失



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

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

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

这工作正常,但是一旦我尝试调用外部 REST 端点(我正在使用带有 Rx 可观察量的 Retrofit(,线程就会在那里消失,端点永远不会被调用,并且不再执行连续的命令。

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

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

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

--

额外信息:

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

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

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

如果必须在应用程序终止时执行进一步的、可能持久的代码,则关闭钩子不是要走的路。它的存在是为了关闭开放资源,而不是创造新的资源。

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

如果由于某种原因太复杂,您可以尝试通过执行以下操作来清除关闭钩子开头的中断标志:

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

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

最新更新