问题发现
grafna jvm监控告警,cpu使用率持续30秒超过80%
问题排查
排查方向,查看jvm进程中,cpu占比高的线程,然后查看该线程的执行堆栈,进而定位到问题代码。
由于jvm在docker容器中部署,进程ID固定为1,所以免去查找jvm进程ID的步骤。
- 查看cpu使用率高的线程
# 该命令查看 jvm进程中,cpu占用高的线程,其中PID=1
top -p PID -H
从结果可以看到,ID为9001、10610的线程cpu使用率占比持续维持在40%左右
- 查看jvm进程内线程堆栈
步骤1中的线程ID分别为9001、10610,在jstack文件中要转换为十六进制查找,9001转为十六进制是0x2329,10610转为十六进制是0x2972。
为了避免线程文件过大,可以选择只打印cpu占用高的线程堆栈,打印 PID=10610或者PID=9001 的信息
# 将线程堆栈输出到 stack_info.txt 文件中,其中PID=10610或者PID=9001
jstack PID > stack_info.txt
结果如下,发现是敏感词检测拦截器正则匹配过程消耗大量CPU资源。
等待这波流量结束后(问题尚未解决),可以看到grafana面板中cpu使用率已经降低,再次执行top -p PID -H
指令,看到已经没有占比特别高的线程了
问题处理
找到问题后,就可以找出具体原因。
通过检查代码,发现该连接器是对requestbody整个输入流做敏感词检测,但事实上这个功能是针对搜索类型的接口,用户输入的搜索词中包含敏感词,而搜索词长度是有限制的,仅匹配搜索词不太可能触发cpu负载过高的情况。故需要修改拦截器代码逻辑,精细化过滤粒度,仅过滤搜索词。
拓展优化
- 开启tomcat access log jstack 文件中通过nid定位线程后,可能不能直接定位到所在接口。可以结合access log中的线程名+时间定位当时的请求接口具体是什么。然后再通过其他接口日志找到具体的用户和具体的请求。
其他
类似全局拦截的切面代码很容易引发问题,上线类似功能时,很难做到全覆盖测试,有一点bug或者不合理的逻辑就可能引发局部问题甚至全面的系统瘫痪。
曾经项目A中一个关于接口参数整形的处理,引发了十几分钟接口不可用
此次项目B中敏感词检测,引发CPU负过高,即使有多副本负载均衡也可能引发系统不可用
Prometheus 系统监控
Grafana 可视化
告警机器人
top命令和jstack命令使用
jstack 中 nid 和 tid 的区别