这个是我很早以前解决的一个案例,其现象是系统每次上线后,20多台机器,总有两三机器,出现假死的情况。如何判断出系统假死?借助的是一个第三方公司运维监控平台;这种情况,前同事称之为的“假死”,需要重新启动系统才能恢复。因为我是新来乍到,觉得这种情况不正常,而且对研发(在这边是研发上线)来说,是一个非常大的上线负担;于是我决定解决一下这个“百年难题”。
我亲自上线,果然很快就碰到了假死的机器。我看到机器的CPU,内存和磁盘IO都很正常,根本不像出问题的机器。直觉告诉我,先用jstack打印个堆栈看看当前tomcat在做什么吧,于是叫上支持上线的运维小哥给打印了一个,然后手工重新部署了一下有问题的机器(记住出问题一定要先止损)。
拿到手的堆栈,第一眼就发现了一些问题。前几行如下:
可以看到tomcat的线程号已经到了215,而tomcat默认最大处理数为200,已经到了饱和状态。后续的请求就只能排队了。
堆栈中,有很多waiting to lock <0x0000000784588098>的线程,从执行堆栈看,应该是CXF要调用.NET的webservice。调用的业务方法各不相同。
继续往下看,在堆栈的后半部分(注意行数),打印了一个死锁的提示。
我们进一步分析,为了方便大家阅读,我对上面的死锁线程画了一个依赖图,可以看出,线程25和线程48形成了死锁。这4个线程的等待关系如下:
继续分析,什么导致的死锁;
线程25的堆栈如下:
线程48的堆栈:
线程持有锁和堆栈中提示的锁信息正好照应
从上面堆栈可以分析出,gson和第三方的agent发生了循环死锁。至此问题的解决方法已经有了,要不去掉gson,要不就去掉那个第三方agent。
除了上面的解决方法外,我们还在系统中增加了一个容器探活的接口(这个功能从监控来看,非常有意义)。即在controller中写一个方法,直接返回一个字符串。这样在外部定时的去调用接口(也可以手工使用curl来探测),就知道这个服务是否还存活,也不用第三方监控系统来判断了;
经验教训:
1、系统需从容器级别支持外部探测,以证明自身健康
2、不要轻易引入外部agent
知识点:
1、tomcat(BIO)默认最大线程数200
关注我的微信公众号,获取最新故障案例分析;