背景

几周前利用业余时间给朋友写了一个最近挺火的 midjourney 生成工具后端服务,采用的Java代码实现,昨天排查某个BUG登录上 Linux 服务器上查看程序日志,然后在 MobaXterm 自带终端的监控上看到服务器CPU一直长期处于100%的状态,对于2h4c的配置内存才差不多用一半多,QPS也很低,不至于CPU一直处于这么高的复杂,于是进行了一翻排查,本文针对解决情况做个记录。

排查步骤

  1. 登录服务器,查看CPU占用最高的所在进程,执行top -c,发现是 java 程序导致CPU飚高;
  2. 查看服务器日志,是否是因为请求量过大和机器性能不行,查看后不是这原因,于是进行下一步;
  3. 由于采用的是docker部署,所以宿主机查看 pid 无法定位具体问题,这里直接采用 arthas 定位到具体的问题;
  4. docker stats 查看容器CPU占用最高的,然后使用docker exec -it {容器id} /bin/bash 进入容器
  5. 由于 docker 安装都是最小化镜像,没有jdk环境,只有jre运行环境,无法执行类似于 jps 命令,所以先临时在docker容器中安装个jdk,然后启动 arthas 进行排查;
  6. 安装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
  7. 执行 thead -n 3 查看cpu最忙的3个线程,记录下pid;
  8. 继续执行 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程序排查问题的复杂度。

最后还是要提醒多久爱,写正则表达式参考网上的很可能有问题,使用前还是多斟酌,以免出现类似的线上问题。

参考链接

Java诊断工具arthas官方文档

Java 进程 CPU 100% 问题排查

java 应用cpu飙升(超过100%)故障排查

Regex gone wild: java.util.regex.Pattern matcher goes into high CPU loop

文章目录