近期在玩大数据. 有个朋友找过来, 说他线上的 tomcat 会莫名其妙的退出, 表示非常苦恼, 请我帮看看. 每次他发现退出了, 都通过腾讯云的 web 控制台登录, 启动 tomcat.
本着助人为乐 (shao kao hao chi) 的精神, 我连上去开始分析. 首先肯定是看 tomcat 的日志, 看看有没有记录到相关信息, 是什么途径退出的.
从日志上看, tomcat 收到了退出请求, 并按照要求关闭容器. 那么是否可以认为是有人执行了 shutdown.sh 呢? 并不能. 执行了 shutdown 脚本的关闭日志是这样的.
与其相关的 tomcat 源码截图如下. 截图左侧有行号.
tomcat 启动时, 设置 await, 等待关闭指令进入. org\apache\catalina\startup\Bootstrap.java
catalinaDaemon 的定义如下.
org\apache\catalina\startup\Catalina.java
具体实例化时, 会将接口 Server 的实例指向 StandardServer. 类路径如下.
org\apache\catalina\Server.java
org\apache\catalina\core\StandardServer.java
而 StandServer 中的输出相关日志的源码如下:
读取的配置文件为 org\apache\catalina\core\LocalStrings.properties
当 tomcat 收到正经的关闭指令时, 会输出此日志, 说明是收到指令关闭容器.
正经的指令关闭容器, 相关代码如下.
那么, 现在的证据说明, 这个 tomcat 不是通过 SHUTDOWN 报文关闭的. 而且, 从下图来看, 也颇能说明这个 SHUTDOWN 指令不是这么容易发成功的.
那么现在可能性最大的办法就是通过 KILL 指令来操作. 执行 bash 脚本需要登录机器, 那么从 wtmp,utmp 查找一下这个时间点的登录记录呢?
下面是 IPIP 的结果.
换言之, 23 日早上 tomcat 异常退出的时候, 有一个来自腾讯云的 BGP 机房的地址也巧合的断开了会话. 而我这个朋友的机器就放在腾讯云. 有点奇怪是吗?
继续追查, 连续追溯几天的 tomcat 日志, 比对 utmp,wtmp 结果, 再比对 IPIP 结果, 都是如此. 来自腾讯云 BGP 机房的会话断开, tomcat 同一时间点退出. 精确到秒级. 连续多天出现很多次, 说明 tomcat 退出和 Web 会话退出是具备因果关系的.
经过询问, 朋友确认他是习惯于使用 Web 控制台的方式登录服务器, 启动了 tomcat 以后就丢在一边, 开始调试接口了. 那么有什么可能会导致这样的因果关系出现? 这就要说到 Linux 系统的一个历史悠久的进程间通信的机制 -- 信号量.
具体信号量是什么, 请自行查询相关资料了解学习. 针对本次问题, 可以简单的理解为进程间通讯的一种机制.
进程 A 需要进程 B 做点事, 而进程间的内存区域某种意义上说是互不可见的. 这个时候就需要通过信号量来完成. 进程 A 可以按照预先定义的信号量规范向进程 B 发出信号量, 当进程 B 收到后, 根据具体信号量的值决定处理逻辑. 具体信号量清单, 可以在命令行通过如下命令查询. 命令中均为字母, 没有数字 1.
这其中最常见的就是 9,SIGKILL. 当进程收到此信号量时, 会被 KILL 掉. 此信号量由操作系统处理, 应用不能处理. 在 vista 之前的 Windows 系统中, 是有办法渗透到内核中的. 此时可以拦截类似 WM_CLOSE 之类的消息, 让某个程序无法关闭. 到了 win7,win10 时代, 已经不能使用此类技巧了.
此外, 我们熟悉的 CTRL + C 操作, 发出的是 SIGINT. 有些场景下, 我们需要通知程序优雅的退出, 此时可以发出 SIGQUIT, 也就是 kill -3.
那么 Web 控制台会话断开, 会发出什么信号量呢? 我们来试试就知道了. Java 虽然说不能操作系统底层, 但是 sun.misc 包有惊喜哦. 代码如下.
如图所示, 这段代码会在收到信号量时输出线程名称, 信号量名称, 并翻译成具体的数字. 随后, 在 main 函数中, 我 "注册" 了 HUP,INT,ABRT,TERM 四种信号量. 注册四种是因为不清楚具体会发什么出来, 索性有可能的都搞起来.
编译, 打包. 此处有一个问题需要注意, 由于信号量属于操作系统底层机制, 每个不同操作系统所支持的信号量是不同的, JVM 中通过 private static native int findSignal(String paramString)提供支持. native 方法涉及具体 VM 实现, 不贴代码了. 不过很容易想到的是, Windows 和 Linux 当然不同. 所以此处就要在上位机编写, 下位机调试了. Windows 底下运行报错可不要慌张哦.
接下来的事情就简单了. 把程序上传服务器, 通过 Web 控制台登录服务器, 将执行结果重定向到文本文件中, 然后静待控制台超时. 结果如下.
结果不用再分析了, Web 控制台会在退出时发出 SIGHUP, 相当于 kill -1. 而 tomcat 在收到 SIGHUP 会怎么操作呢? 小伙伴们可以试试看 kill -1 pid, 再看看日志, 就明白了.
解决方案其实也简单. SIGHUP 是 HANG UP 的意思, 可以用 nohup xx.sh & 来彻底屏蔽 SIGHUP 和 SIGINT. 另外, 经过测试发现, 通过单击 SecureCRT 的 tab 页右侧的 * 也可以触发信号量, 而直接 logout 或者点菜单上的红叉则并不会触发.
感兴趣的小伙伴可以把这个程序扩展一下, 测测看. 说不定你的异常退出问题也能迎刃而解呢.
此问题的解决离不开上海中通的刘建刚同学, 特此致谢.
来源: https://www.cnblogs.com/xhr8334/p/11103911.html