定位CPU 100%的问题,可以从业务和技术两个角度分析。从业务上说,如果在执行某个已知的计算型业务操作后出现CPU飙升,就直接从这个操作入手,分析可能的直接或间接原因;在技术角度可以按一般思路展开分析,下面以一个实例说明。
如图1,测试的同事在性能测试时,发现微服务出现CPU持续100%的问题。该应用是部署在Docker容器中的Java微服务。
图1 CPU持续100%
(1)进入dockerdocker ps | grep SERVICE_NAME # SERVICE_NAME是微服务名称,如optimizer
docker exec -it CONTAINER_ID bash # CONTAINER_ID是容器
(2)找到CPU高的线程
top -H # -H是线程模式,对于Java应用来说可以看具体线程
结果如图2,可以看到,CPU比较高的线程ID是40506和51244
图2 top –H执行结果
(3)转为16进制printf "%x\n" 51244 # \n是为了换行显示,可以不要
得到16进制数:c82c
(4)获取线程堆栈如果服务中部署了jdk工具jstack,可以使用jstack 进行线程转储,但是一般现网环境不会部署jdk工具的(当然可以再上传,然后用相同用户执行),这里介绍使用Linux的kill命令打印线程转储信息。
kill的信号大概有60多种,最常用的是kill -9,它发送的是SIGKILL信号,表示强制终止线程,我们这里使用kill -3命令,它发送的是SIGQUIT信号,Java会转储指定的线程堆栈,但这并不会杀死进程。
部署在Tomcat中的服务一般会将日志输出到catalina.out(tomcat-catalina.log)等日志文件中。
如果需要取多次时间间隔的线程堆栈,写个循环就好了。
(5)日志过滤
cat tomcat-catalina.log | grep –A 50 c82c # c82c是上面的pid=51244的线程
结果如图3所示,根据多个线程堆栈,可以看到这个线程一直处于RUNNABLE状态,这个例子根据线程名称、堆栈信息,已经可以大致清楚问题原因了(这里是解决NP-C问题的算法迭代产生的问题),如果还需要进一步分析,可以获取堆转储、使用MAT、JProfile等工具,这里就不展开了。
图3 c82c线程堆栈