我有一个在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) {
}
然而,这是违反规则的,这些规则的存在是有原因的。 即,如果操作系统对终止信号的反应不够快并以更糟糕的方式终止它,则操作系统可能会认为您的程序挂起。