nohup 服务启动与kill -9 服务停止
用 java -jar xx.jar运行某一jar包, 退出终端后,或按ctl+c时,发现服务已不在运行。于是往往会用如下命令启动
nohup java -jar xxx.jar &
用如下命令停止运行中的服务
kill -9 12343
以上命令确实能正常启动服务,还能确保服务不会因为不会因为终端退出而被终止。我相信大部会人都是这样做的。但它却存在一个致命的缺陷:资源不能被清理。
比如服务启动时,会发送一个注册命令到注册中心(我用的是consul,其它注册中心的机制我没有研究过)。服务停止时,理所应当要发送一个注销命令注销服务。但kill -9 杀死服务后,服务未能正确注销,而且日志中也没有清理资源的记录。
kill 介绍
kill命令因为叫“kill”,所以kill 给我们的映像就是一个杀手,专们用来杀进程,其实我们可能是被这个耸人听闻的名字给欺骗了,它确实有杀死进程的目的,但更准确的是发送信号。man手册中的说明是:
kill – terminate or signal a process
信号(signal)是linux进程通信的一种机制,我们经常使用到的管道如:tailf ** | grep ,也是进程通信的方式。
kill -9 进程号,意思就是向某进程发送为9的信号。可以查看手册发现信号的含意
1 HUP (hang up)
2 INT (interrupt)
3 QUIT (quit)
6 ABRT (abort)
9 KILL (non-catchable, non-ignorable kill)
14 ALRM (alarm clock)
15 TERM (software termination signal)
我们可以看到 1即为hup信号,
man手册对nohup命令的定义为:
nohup – invoke a utility immune to hangups
查看一个运行的jar包
(base) ➜ target git:(develop) ✗ ps -ef| grep wzp-test-1.2.0-SNAPSHOT.jar
501 20902 19982 0 9:26下午 ttys001 1:33.85 /usr/bin/java -jar wzp-test-1.2.0-SNAPSHOT.jar
501代表的是我终端的pid,20902代表当前jar的pid,当关闭终端时,会发送一个hup信号给501进程,而它的子进程也会接到hup信号,但用nohup启动的程序会忽略这个信号 ,所以程序不会杀死。如果我们直接用kill -1 向程序发送一个hup信号,程序也不会停止运行。
setsid 与 kill -1
那有没有办法让程序不受终端的影响,且能让程序接收hup信号,从而自动清理资源。
可以尝试一下setsid命令,setsid会为新的进程创建一个新的会话id,然它与父进程无关。可以 用kill -1去杀死它。资源便能得到正确的清理。(我发现了mac上面无法运行setsid命令,而我的云环境竟没有安装java环境,以后我再继上实验结果)
docker镜像中的信号处理方案
在容器编排时,可能会遇到信号方面的问题,据说用dumb-init可以解决。请看code-server的dockerfile文件
意外收获
不知道你们发现了没有,上面展示的信号15的意思是TERM (software termination signal),意思是软中断,我刚才尝试了用nohup启动,可以用kill -15正确的终结程序。(linux上的试验我还没做)
spring 注入钩子线程
在JVM退出时,Hook会自动执行,spring在hook线程清理资源
@Override
public void registerShutdownHook() {
if (this.shutdownHook == null) {
// No shutdown hook registered yet.
this.shutdownHook = new Thread() {
@Override
public void run() {
synchronized (startupShutdownMonitor) {
doClose();
}
}
};
Runtime.getRuntime().addShutdownHook(this.shutdownHook);
}
}