项目场景:
开放平台的网关应用
问题描述:
网关每天或者隔几天就会出现cpu异常飙高的问题,导致交易处理特别慢
排查过程:
1、首先使用 top 命令查看是哪个java进程导致cpu异常飙高
1.1、查看日志文件发现日志中有too many open files 的报错信息,这个明显是打开了很多的文件句柄没有被释放导致的。初步怀疑也是同样的进程导致的这个问题,通过命令查看该进程下面打开的文件句柄数的数量 lsof -p 进程ID | wc -l 发现该进程下文件句柄数达到了3W多,然后看了下基本都是连接到一个ip上状态为ESTABLESHED的,问了下运维那边连接的ip为缓存中间件对应的机器ip,这样的话基本上就有一个大致的判断,应该是对接缓存中间件的问题。接下来继续排查。
2、使用top -Hp 进程ID 命令查看该进程下面是哪个线程占用cpu过高
通过监控该进程下的线程信息,发现有几个线程每隔几秒就会占用很高的cpu资源,初步判断可能是GC在执行
3、通过命令jstat -gcutil 进程ID 1s 查看gc的情况,发现FGC非常频繁,已经达到了万次(整个人都不好了)
4、导出堆栈文件进行分析:jmap -dump:file=/home/supdev/20211102.hprof 进程ID
5、通过jvisualVM分析堆栈文件,查看类,点击占用空间最大的那个类,查看这个类是在代码中什么地方用到了。
6、查看代码中使用的地方,发现是在对接项目上的缓存中间件初始化连接的时候没有判断是否已经创建了连接,然后程序会有一个定时任务每隔3s中从其他应用刷新配置,刷新配置的时候会重新实例化对象,导致这个地方不停的在创建连接,并且创建的连接都是强引用FGC的时候压根没办法进行回收,导致老年代空间被占满,出现频繁FGC的情况,同时占用了大量的文件句柄没有被释放,在日志文件中出现too many open files的错误提示。
解决方案:
新增一个判断,判断创建的连接是不是为null,如果不为null就将已经创建的连接关闭重新去创建(业务场景需要只能关闭上一个连接然后重新再去创建新的连接)