大家好,又见面了,我是你们的朋友全栈君。
问题描述
- tomcat进程已经不在;
- 由于在启动命令行参数中增加了相关的日志监控,重点查找JVM内存溢出、jvm的crash的日志进行问题定位;
- 没有.hprof文件生成【基本可以推论没有出现JVM内存溢出】
- 没有hs_err_xxx.log文件生成【基本可以推论JVM没有出现严重的crash异常】
问题分析
1)通过catalina.log 看出tomcat出现了非正常关闭操作下的停机;如果是正常停机会在输出图1的日志前输出如图2所示的内容
2)tomcat停机的时间发生在15:32:28秒
3)查看应用日志,没有发现存在业务异常;但是佐证了tomcat停机的时间,如图3所示:
4)对比tomcat停机的时间,查看操作系统的日志/var/log/messages在15:32:28相关日志内容,如图4所示,可以得出以下信息:
5)tomcat宕机、sshd进程收到断开连接的事件都发生在同一秒。
6)该日志中也记录了发出ssh断开连接事件的客户端ip地址(该信息非常有用,可以根据ip地址定位到操作者,并通过操作者了解问题发生前相关操作信息,这为后期的缩小问题范围并将问题复现提供了很有价值的线索)。
7)至此,发现了一个重要线索:tomcat的退出与sshd的session关闭发生在同一时刻;即使是巧合,这个信息也是值得去深究。
8)我们知道,tomcat在以下两种情况下会触发shutdownhook:(1)代码里面执行了System.exit;(2)tomcat进程接收到了除9以外的会引起退出的信号量;我们对第一种情况进行了代码扫描并逐一的排除,不存在业务代码调用System.exit的场景;对于第(2)种场景的排查很显然需要针对tomcat的启动脚本。
9)基于此,根据断开ssh的session会话的ip地址,定位到相应的操作者,获取到当时执行的操作命令项目,了解到采用seeyonupdate脚本执行启动;以下图示是更新脚本片段;从代码片段来看,在启动进程里面增加了一个tail语句。
10)现在的问题焦点就转移到,原有启动脚本里面增加了一行tail语句导致tomcat异常退出的原因:从脚本的执行过程来看,tomcat启动后,当前shell进程并没有退出,而是挂在tail进程上。这种情况下,如果用户直接关闭ssh终端的窗口,sshd会把SIGHUP信号传递给bash进程,bash会把SIGHUP传递给seeyonupdate进程及该进程所属的进程组的所有进程成员。(java后台进程继承了父进程startup.sh的pgid,所以java进程仍属于进程组里的成员,收到SIGHUP后会退出。
原因定位
综合以上过程的分析,我们可以把整个tomcat异常退出的流程串联一下:
1)操作用户在ssh终端执行了seeyonupdate脚本,对综合办公应用平台进行升级部署与启动;脚本的最后,通过执行tail命令,对日志信息进行查看(此时,seeyonupdate脚本并未退出,而是挂在了tail进程上)
2)用户关闭了ssh终端窗口或网络断开导致ssh连接断开,sshd会把SIGHUP信号发给了窗口内的bash进程,bash进程会把SIGHUP传递给seeyonupdate进程及该进程所属的进程组的所有进程成员。(java后台进程继承了父进程startup.sh的pgid,所以java进程仍属于进程组里的成员,也会收到SIGHUP信号)
Tomcat收到SIGHUP信号后,会激活SIGHUP handler线程(如图6所示);该线程会触发Tomcat的shutdownhook函数(如图7所示),在该函数中会执行tomcat退出时的资源销毁操作(如图8所示):
图6 jstack堆栈快照
图7 jstack堆栈快照
图8 ctp.log日志片段
修改与建议
该问题的解决,也能解释之前项目现场其他环境下没有异常日志生成,却出现了tomcat异常宕机的情况。
技术方面
方法1:只需要在tail命令后面加&,把tail命令转后台执行,让seeyonupdate进程正常退出
方法2:在seeyonupdate对应的脚本中,增加“set -m”,开启作业控制
针对这两种修改方法进行了验证,tomcat的进程不会销毁
管理方面
1)建立对运维脚本的代码审查过程;
2)对于运维脚本加强多场景下的功能测试。
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/163641.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...