一次CPU负载过高案例

问题发现
grafna jvm监控告警,cpu使用率持续30秒超过80%


image.png

问题排查

排查方向,查看jvm进程中,cpu占比高的线程,然后查看该线程的执行堆栈,进而定位到问题代码。

由于jvm在docker容器中部署,进程ID固定为1,所以免去查找jvm进程ID的步骤。

  1. 查看cpu使用率高的线程
# 该命令查看 jvm进程中,cpu占用高的线程,其中PID=1
top -p PID -H

从结果可以看到,ID为9001、10610的线程cpu使用率占比持续维持在40%左右


image.png
  1. 查看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资源。


image.png

等待这波流量结束后(问题尚未解决),可以看到grafana面板中cpu使用率已经降低,再次执行top -p PID -H指令,看到已经没有占比特别高的线程了

image.png

问题处理

找到问题后,就可以找出具体原因。

通过检查代码,发现该连接器是对requestbody整个输入流做敏感词检测,但事实上这个功能是针对搜索类型的接口,用户输入的搜索词中包含敏感词,而搜索词长度是有限制的,仅匹配搜索词不太可能触发cpu负载过高的情况。故需要修改拦截器代码逻辑,精细化过滤粒度,仅过滤搜索词。

拓展优化

  1. 开启tomcat access log jstack 文件中通过nid定位线程后,可能不能直接定位到所在接口。可以结合access log中的线程名+时间定位当时的请求接口具体是什么。然后再通过其他接口日志找到具体的用户和具体的请求。

其他

  1. 类似全局拦截的切面代码很容易引发问题,上线类似功能时,很难做到全覆盖测试,有一点bug或者不合理的逻辑就可能引发局部问题甚至全面的系统瘫痪。

  2. 曾经项目A中一个关于接口参数整形的处理,引发了十几分钟接口不可用

  3. 此次项目B中敏感词检测,引发CPU负过高,即使有多副本负载均衡也可能引发系统不可用

  4. Prometheus 系统监控

  5. Grafana 可视化

  6. 告警机器人

  7. top命令和jstack命令使用

  8. jstack 中 nid 和 tid 的区别

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容