近期打算整理一系列线上问题排查的文章,也做备忘用吧,虽然问题排查都有印象了,但是真遇到线上问题难免紧张,万一步骤弄错了就耽误时间了
CPU飙高问题一般通过以下几个步骤来排查
- 找到cpu占比高的Java进程ID,通过这一步就知道是哪个Java应用出了问题。
- 然后再找到该Java进程中哪些线程占用cpu时间比较高
- jstack -l 该Java进程到某个文件(比如/tmp/jstack.dump)。
- 再将步骤2得到的线程ID由10进制转换成16进制,去jstack.dump文件中看这些线程到底是执行了哪些Java代码,从而可以找出问题。
先来看看步骤1,定位cpu占比高的Java进程比较简单,可以直接通过top命令或ps命令,但由于top命令比较简单,不像ps命令有过多的参数,所以大多数会优先使用top命令。其实现在线上大多是每个服务都有单独的容器了,所以CPU飙高大部分是你的服务有问题(少数情况下有一些插件异常导致的CPU过高,也需要排查少部分情况,做到严谨)
详细描述
一:找到最耗CPU的进程
执行top -c,显示进程运行信息列表
输入P,进程按照CPU使用率排序
如图所示,最耗CPU的进程PID为32761
二:找到最耗CPU的线城
top -Hp 32761 显示一个进程的线城运行信息列表
输入P,线程按照CPU使用率排序,这里没图了,假设最耗CPU的线程PID为10704
三:查看堆栈信息,定位线程在做啥,定位对应代码
1.将线程PID转为16进制
printf "%x\n" 10704
得到对应的16进制是29d0
2. 接着查看堆栈,找到线程
jstack 32761|grep '29d0'
打印进程堆栈,通过线程ID过滤得到线程堆栈
最后根据堆栈里的信息,找到对应的代码即可
以上几个步骤是没问题的,但是真到线上排查时显得步骤有些多,毕竟出问题的时候是要争分夺秒的,最好是通过一个脚本就能完成上述所有操作
入参只有一个就是Java的pid,如果没有入参那么默认会取最耗CPU的Java进程
#!/bin/bash
if [ -z "$1" ]; then
### 1.先找到消耗cpu最高的Java进程 ###
pid=`ps -eo pid,%cpu,cmd --sort=-%cpu | grep java | grep -v grep | head -1 | awk 'END{print $1}' `
if [ "$pid" = "" ]; then
echo "无Java进程,退出。"
exit
fi
else
pid=$1
fi
curTime=$(date +%Y%m%dT%H:%M:%S)
dumpFilePath="/tmp/pid-${pid}−${curTime}.jstack"
echo -e "java 进程ID为 $pid" > ${dumpFilePath}
ps -ef|grep ${pid} >> ${dumpFilePath}
topThreadId=`top -b -n 1 -Hp ${pid}|grep java|head -n 1|awk '{print $1}'`
cpuUsage=`top -b -n 1 -Hp ${pid}|grep java|head -n 1|awk '{print $9}'`
echo -e "最耗cpu的使用率为 $cpuUsage">>$dumpFilePath
topThreadId16=`printf "%x" ${topThreadId}`
topThreadId16ThreadName="nid=0x${topThreadId16}"
echo -e "最耗cpu的java线程ID 16进制为 $topThreadId16ThreadName">>$dumpFilePath
jstack -l ${pid} >>$dumpFilePath
#threadDe -e tail=`jstack ${pid}|grep ${topThreadId16} -C 10`
#echo "$threadDetail"
这样就能实现一个脚本记录所有信息了,大大节省线上排查问题的时间,也不容易看错