故障表现
服务在运行中,突然出现客户端请求hang住超时。服务端没有打响应任何日志。消息到了服务端就石沉大海没有任何动静。重启之后服务恢复正常,过几天后又突然异常。出异常的服务端业务逻辑是使用了一个第三方SDK接口。
故障分析
请求异常的时候,观察服务端进程,首先检查VM状态,查看CPU内存状态都正常。此时打出Jstack日志,发现tomcat的http的线程有部分集体进入locked状态,锁住的还是同一片资源区。如下图:
查看对应方法的源码区,发现调用的httpclient的getPoolEntryBlocking在获取资源的时候一直block住了。首先httpclient是很成熟的工具了,不太可能是本身BUG导致死锁。看这块的逻辑,其实还是和申请连接池资源相关,此时想到业务代码在连接成功的时候是不是没释放
照着Jstack中调用的逻辑向上查,果然发现SDK中对statusCode进行了封装处理,当SDK使用的HTTP返回非200时,直接抛出异常,并没有释放httpclient资源。这样连接一直存在,就会导致新请求无法复用之前连接池的资源,后续请求全部进入等待状态。所以在编写HTTP连接的代码的时候一定要注意HTTP的释放。尽量不要交叉其他业务逻辑。
修改方案
因为是SDK的BUG,所以直接联系对方进行修复升级即可。 # 经验总结 Java的故障其实排查思路不复杂,常见的分析工具jstack、jsutil、top等就可以解决大多数问题,疑难杂症就要dump内存进行详细分析。