最近在使用linux下tomcat运行javaweb项目时,出现tomcat莫名关闭的问题,经过观察,感觉tomcat是在某种条件下自行关闭,并不是程序崩溃、内存溢出等问题。
经观察tomcat的关闭与远程发布终端的关闭的时间非常吻合,猜测可能和linux远程终端有关。
后来在网上看到这篇文章,印证了上述猜测。作者对问题的发生进行了详细的观察,找到问题发生的原因并给出了解决方案。文章较长,大家有兴趣可以详细看一下,作者还有很多其他关于linux的文章,看下来受益很多。
链接地址:http://hongjiang.info/why-kill-2-cannot-stop-tomcat/
下面本文对该问题发生的机制进行一些总结,希望能够和大家一块学习:
发生条件:
1、使用发布脚本发布web项目并启动tomcat。
2、脚本中启动了某个前台进程,tail -f ……/tomcat/logs/catalina.out
3、在前台进程运行时,关闭终端。
在这种情况下,tomcat就会随着终端关闭而关闭
该现象的发生由以下逻辑导致:
准备条件:
1、在shell环境下启动了脚本,由该脚本启动的所有进程(包括tomcat)属于一个进程组,拥有同一个pgid,等于该脚本的pid;
2、启动tomcat后,由于运行了某个一直运行的进程(tail),使得该脚本处于运行状态;
触发过程:
此时关闭终端,sshd发出的sighup信号传递到该脚本的进程,并分发给进程组所有成员,使得tomcat(java)接受到sighup信号而关闭。
根据上述逻辑,破坏这种现象的某一个准备条件或者阻止触发过程,阻止sighup信号被发送给tomcat,即可阻止上述现象的发生。
理论上有一下方法:
1、使tomcat与该进程属于不同的进程组。
2、使该脚本运行后关闭。(sighup先发给该脚本的进程才会分发给tomcat)
3、不要关闭终端(操作上不行),让java不接受sighup信号
上述方法中,除了关闭终端,其他都有一定的解决方案。
先直接给办法:
1A、在脚本中写上 set -m,用作业控制模式运行脚本,该脚本启动的子程序与改进程不属于一个进程组,信号不会传递到tomcat。
1B、在运行启动tomcat的脚本时,-ic,子进程强制不属于改进承租,但是会有一些副作用(这篇文章中有提到http://hongjiang.info/job-control-and-foreground-process-group/)
2A、运行完毕之后,按下ctrl+c,再关终端,这个也是为什么大多数情况下我们遇不到这个问题。shell默认不会让java接受sigint,这也是为什么ctrl+c时,sigint传递到了java但是tomcat不会关闭)。
2B、在脚本中以后台的方式运行tail这样的进程,tail …… &(放入后台的符号),这样脚本会立即运行完,但副作用是tail这样的进程会一直打印内容而且只能kill掉。
3A、不关闭终端(开玩笑)
3B、想办法让java不接受sighup,比如使用nohup(不接受sighup)启动java,但java的启动可能在tomcat自带的脚本里,不建议随便修改。
理解上述办法,需要了解以下知识点:
进程组的形成:
进程pid,pgid,sid的关系,进程组的概念。
linux的交互模式与非交互模式下,默认是否启动作业控制。
linux什么时候是交互,什么时候不是交互模式。
是否启动作业控制对pgid的影响。
信号的出发和传达:
sigint,sigqiut,sighup等常见信号。
sighup的产生和传递规则。个人总结:传递给会话首进程(常见是bash)的子进程(不论前台后台)及子进程领导的进程组(子进程自身销毁后,则进程组不会接受)。
进程对信号的捕获、忽略等。
有时间我会对上面的点一一整理补充,大家亦可自行了解。
转自: https://blog.csdn.net/yaoxiaoyang/article/details/53494946