使用Jstack进行服务问题排查

最近公司服务遇到了完全没有排查头绪的CPU过载问题,导致服务变为接近不可用的状态或者直接宕机。在二老板(技术组长)指导下学会了使用Jstack进行服务的线程状态查询,通过这种方法来排查线程死锁或者死循环的问题,故此记录排查步骤。

步骤

0. 问题说明

服务突然无法访问或响应速度特别慢。

1. 通过topps指令配合查询服务状态与pid

通过top指令进行服务状态查询,查询结果为核心的SpringBoot服务的CPU占用超过100%。记下此处的问题服务的pid

可以通过C键来显示完整的服务启动命令,以此来判断启动的服务内容

或者记下top中问题服务的pid,通过ps -ef | grep <pid>的方式来查询服务

2. 确保没有新的网络请求

为了保证Jstack的日志不受其他内容影响,需要先关闭服务器开放的接口端口,确保没有新的网络请求。

公司目前所用服务器是阿里云的,所以这次使用的方式是在阿里云控制台安全组中添加 拒绝22以外的所有端口 的规则,该做法是否稳妥有待商榷。

可能也可以通过临时修改nginx配置等其他方法来阻断网络请求,有待实践。

3. 通过jstack指令来获取当前的服务线程状态日志

通过

1
jstack -l <pid>

指令来输出当前的服务线程状态,或者通过

1
jstack -l <pid> > <log-file-name>

来输出到指定的日志文件中,方便排查。

4. 查找日志文件中关于业务逻辑服务的线程

在日志中查找业务相关代码,可以通过项目特定的包结构com.xxx等来找到线程所执行的具体代码位置,例如:

1
2
3
4
5
6
7
8
9
10
......

"XNIO-1 task-26" #100 prio=5 os_prio=0 tid=0x00007f0e6c023000 nid=0x7959 runnable [0x00007f0e4f5f2000]
java.lang.Thread.State: RUNNABLE

......

at com.xxx.service.impl.XXXServiceImpl.SomeMethod(XXXServiceImpl.java:99)

......

其中出现的项目代码位置大部分情况下都不止一处,应从最后一项开始查看,梳理逻辑。

这样就可以看出,在代码执行到XXXServiceImpl.java:99时,产生了线程死锁或者死循环,应从这里排查项目问题。

之后就是研究逻辑错误等一般的项目错误修复流程。

日志内容可能特别多,需要耐心慢慢排查。

为什么不使用Arthas

本次讨论的问题场景为CPU过载,top查询结果中CPU占用超过了100%。这种情况下,若再启动一个Arthas对问题服务进行监控,会导致指令执行极其缓慢或成为导致服务器宕机的最后一根稻草。