记一次Linux服务器CPU长期100%问题排查
背景
几周前利用业余时间给朋友写了一个最近挺火的 midjourney
生成工具后端服务,采用的Java代码实现,昨天排查某个BUG登录上 Linux 服务器上查看程序日志,然后在 MobaXterm 自带终端的监控上看到服务器CPU一直长期处于100%的状态,对于2h4c的配置内存才差不多用一半多,QPS也很低,不至于CPU一直处于这么高的复杂,于是进行了一翻排查,本文针对解决情况做个记录。
排查步骤
- 登录服务器,查看CPU占用最高的所在进程,执行
top -c
,发现是 java 程序导致CPU飚高; - 查看服务器日志,是否是因为请求量过大和机器性能不行,查看后不是这原因,于是进行下一步;
- 由于采用的是docker部署,所以宿主机查看 pid 无法定位具体问题,这里直接采用
arthas
定位到具体的问题; docker stats
查看容器CPU占用最高的,然后使用docker exec -it {容器id} /bin/bash 进入容器
;- 由于 docker 安装都是最小化镜像,没有jdk环境,只有jre运行环境,无法执行类似于
jps
命令,所以先临时在docker容器中安装个jdk,然后启动arthas
进行排查; 安装jdk和启动arthas命令步骤如下:
# 下载jdk wget https://mirrors.huaweicloud.com/java/jdk/8u202-b08/jdk-8u202-linux-x64.tar.gz # 解压 tar -zxvf jdk-8u202-linux-x64.tar.gz # 下载 arthas wget https://arthas.aliyun.com/arthas-boot.jar # 启动 arthas ./jdk-8u202-linux-x64/bin/java -jar arthas-boot.jar
- 执行
thead -n 3
查看cpu最忙的3个线程,记录下pid; 继续执行
thead pid号
,定位到具体的代码,我这里的日志如下:[arthas@1]$ thread 5781 "http-nio-8080-exec-211" Id=5781 RUNNABLE at java.util.regex.Pattern$CharProperty.match(Pattern.java:3778) at java.util.regex.Pattern$Branch.match(Pattern.java:4606) at java.util.regex.Pattern$GroupHead.match(Pattern.java:4660) at java.util.regex.Pattern$Loop.match(Pattern.java:4787) at java.util.regex.Pattern$GroupTail.match(Pattern.java:4719) at java.util.regex.Pattern$BranchConn.match(Pattern.java:4570) at java.util.regex.Pattern$CharProperty.match(Pattern.java:3779) at java.util.regex.Pattern$Branch.match(Pattern.java:4606) at java.util.regex.Pattern$GroupHead.match(Pattern.java:4660) at java.util.regex.Pattern$Loop.match(Pattern.java:4787) at java.util.regex.Pattern$GroupTail.match(Pattern.java:4719) at java.util.regex.Pattern$BranchConn.match(Pattern.java:4570) at java.util.regex.Pattern$CharProperty.match(Pattern.java:3779) at java.util.regex.Pattern$Branch.match(Pattern.java:4606) at java.util.regex.Pattern$GroupHead.match(Pattern.java:4660) at java.util.regex.Pattern$Loop.match(Pattern.java:4787) at java.util.regex.Pattern$GroupTail.match(Pattern.java:4719) at java.util.regex.Pattern$BranchConn.match(Pattern.java:4570) at java.util.regex.Pattern$CharProperty.match(Pattern.java:3779)
分析结果
根据排查的结果发现是因为正则匹配的时候循环浪费的资源,这个正则表达式代码只有在一个接口中做前置判断有用到,就快速定位到了是正则表达式的问题,查阅相关资料和询问ChatGPT后,大概确定问题就是因为正则表达式不规范导致循环进行失控模式,分析参考文章链接,感兴趣的可以点进去看下,最后采用修改正则表达式然后重新发布服务CPU使用恢复正常。
总结
在本次操作过程中,遇到了很多问题坑,很多参考文章都是基于linux的指令和借助jstack、jps命令来排查,然后也不是针对于容器内的java程序进行排查,并且容器对应的pid和宿主机对应的pid不是一回事,所以只有靠定位到具体容器,进入容器再进行排查,一般容器使用的基础镜像为了保证足够精简,所以常常命令也是太简单,所以需要临时安装一些环境,这里推荐给大家arthas这款工具确实能够大大减少针对java程序排查问题的复杂度。
最后还是要提醒多久爱,写正则表达式参考网上的很可能有问题,使用前还是多斟酌,以免出现类似的线上问题。
参考链接
Regex gone wild: java.util.regex.Pattern matcher goes into high CPU loop
商业转载请联系作者获得授权,非商业转载请注明本文出处及文章链接