在LISTEN状态下持续tcp连接

我的Java应用程序有时可以被外部脚本杀死。 这可以通过SIGTERM或者SIGKILL来完成。

应用程序是一个每秒接收很多连接的服务器,在尝试提供服务时可能会被终止。

我想重新启动应用程序,所以我已经为此准备了一个脚本。

问题是,一旦应用程序被终止,新的应用程序实例就无法绑定到前一个实例使用的端口,因为“地址已经在使用”了。 前一个实例的进程已经被终止了,反正这个有问题的监听端口仍然存在,但是被分配给了bash (或者在其他机器上)。

Obviouly, 我的目标是重新启动应用程序,并让它成功绑定到以前的地址

我已经尝试等待200多秒,然后重新启动无济于事,无论如何我不能等待那么多。

我在运行应用程序的所有机器(这是一个Java 1.6的docker服务器)上遇到过这个问题。

任何build议表示赞赏,谢谢,

西尔维奥

编辑杀死jvm进程不是我退出我的应用程序的正常方式,这是用来在出现问题(OutOfMemoryErrors)的情况下。 我从来不需要用SIGKILL来杀死它,因为SIGTERM总是足够的,只有在SIGTERM失败的情况下,我才会使用SIGKILL,这从来没有发生过。 我正在研究一个长期的解决scheme,同时我必须通过在这里和那里应用缝线来运行我的应用程序。

编辑更清楚:这是netstat -tunap | grep行我查杀之前看过程:

tcp6 0 0 :::8898 :::* LISTEN 22709/java 

这是在杀了这个过程之后

 tcp6 0 0 :::8898 :::* LISTEN 23665/sh 

注意PID 22709的进程被杀死了,但是端口仍然在那里(但是被sh锁住了)

更新后,我杀了我的应用程序,用netstat我可以看到一个长长的CLOSE_WAIT状态挂起的连接列表,我的IP作为目的地。 另外,我可以在状态LISTEN中看到一个sh进程监听我的端口:当我终止它时,一个睡眠进程replace它并监听同一个端口:当我终于终止了这个hibernate进程时,端口被释放,我可以成功重启我的服务器。 这可能是一个解决scheme,让我的端口释放,但我担心自动杀死进程,以释放一个端口有点冒险

由于您只能手动执行此操作,因此可能需要添加其他检查。

 netstat -p 

并杀死与您打开的套接字关联的pid,即使是bash或sh。

另外,你提到大部分的时候,SIGTERM都起作用。 如果是这样的话,你的应用程序应该捕获SIGTERM并跳转到一些优雅的退出代码,RST打开所有的连接,然后closures套接字。

HTH

侦听套接字closures后,服务器仍然期望来自客户端的一些数据包,并保持端口分配。 应用程序可以使用SO_REUSEADDR套接字选项来允许立即重新使用套接字地址。

这是我的Linux ip(7)手册页摘录:

除非设置了SO_REUSEADDR标志,否则绑定的TCP本地套接字地址在closures后一段时间内将不可用。 使用这个标志时要小心,因为它使得TCP不太可靠。

应用程序或应用程序服务器可能具有使用此套接字选项的configuration设置。

你真的不杀死你的Java应用程序,你实际上是杀死你的Java虚拟机(JVM)实例,这反过来运行你的Java应用程序。

这不是终止你的java进程的想法。

如果你用kill -9杀死你的jvm,那么jvm将不能自行清理,从而使操作资源处于闲置状态。 🙁

添加一些function到您的应用程序,使其优雅地退出。 如果你别无select,那么试着用-15杀死你的jvm,它可能会帮助它自己清理掉。

如果你的Java程序真的挂了jvm,那么你需要debugging器来压缩这些有害生物。

杀死一个进程并重新启动它是一个黑客攻击,但没有解决。 如果一个进程没有响应任何其他方法,你应该只使用SIGKILL。

我通常尝试

杀-15

那么只能杀死-9作为最后的手段。

为了好玩

http://www.youtube.com/watch?v=Fow7iUaKrq4

如果您有权访问源代码,则需要使用Jacek提到的SO_REUSEADDR选项来创build套接字。 另外感兴趣的是tcp_tw_recycletcp_tw_reuse内核标志(在Linux上)。

真正的问题在协议devise中,您可能会也可能不能更改。 关于这个话题的有趣线索:

随着你的更新,我有另一个解释。 保持套接字打开的sh进程必须是应用程序的subprocess,在侦听套接字打开后分叉。 它并没有和父母一起死亡,而是被初始化过程所采用。

你应该尝试找出什么是shell进程(可能是你的应用程序启动的一些脚本)以及为什么没有终止。 也许这将足以解决脚本,所以它完成后终止工作就足够了? 或者,有一种方法可以使其不与父代分离(如果是同一个进程组的一部分,它应该与父代一起死掉),或者使其closures从父代inheritance的所有不需要的文件描述符。

你可以尝试:

 fuser -p $pid_of_the_sh_process 

看看它保持打开的其他文件。 其中之一就是shell脚本。 知道这是什么,我们可能会find解决问题的方法。