CosClient连接池泄露
简单描述一下处理的过程。
因为是新机器,又着急发布,很多监控组件并没有集成进去,很多东西也是手工操作,相当痛苦(很不规范),大致的顺序如下:
0、服务发布几天了,流量特别小。
1、早上要发布新功能,由于该没有集成到CI/CD上,所以只能纯手工启动。把服务启动,因为看到原来服务启动时没有指定内存大小,所以修改了启动命令。
原命令:nohup java -jar springtboot.jar &
加上参数:-server -Xms2g -Xmx2g
2、服务有三台,逐个启动。
3、启动后看日志,暂时没有问题。
4、上游说有一批数据需要处理,意味着流量会进来,进来的方式主要是kafka
5、服务正常的消费,处理,看kafka的监控大盘,问题不大
6、过了一会,消息提醒,xxxtopic有消息堆积,请尽快处理...
7、上kafka监控大盘一看,确实有不少消息堆积,消费速率降为0
8、打开一台机器查看服务。
9、top 看指标,CPU偏高,说明服务还在处理,主要是服务在处理数据时是偏吃CPU性能操作。看RES,此时已经去到2.8G了。(没有把2.8G跟启动服务时新加上的配置联系上)
10、free看指标,内存够够的
11、tail -f 查看日志,日志不滚动了。
12、下载arthars 查看,通过thread命令看到很多线程,处于WAITING状态(Threads Total超过4k个线程),但是线程名称是默认的,从名称上看不出来什么。此时通过dashboard看到gc count很大,也就是一直在做gc,存在对象没有及时释放。所以CPU一直偏高可能是因为一直在做gc。
13、此时退出arthar后。ps -ef | grep java找到pid,dump出内存信息,jmap -dump:live,format=b,file=heap.hprof pid
14、一边下载heap.hprof,一边把参数调上去-Xms4g -Xmx4g并重新启动服务,先让能继续提供服务。(毫无疑问,内存还在持续缓慢的上升)
15、通过MAT打开heap.hprof,看到有对象存在4k个对象,并且是腾讯云Cos服务的连接监控线程对象。此时大概猜到,估计是CosClient的相关资源没有释放,查阅业务代码发现没有对CosClient做资源的释放。(shutdown方法)
16、翻看CosClient源代码,找到IdleConnectionMonitorThread对象,是客户端的连接池监控线程,这个线程只是每隔2s去清理一下expire conntion 、idle connection。这个线程随着CosClient创建出来一起被new出来,并且该线程被设置为守护线程。因为线程一直在运行,所以引用会一直存在,故而无法被回收。
17、把CosClient改成单例后重新发布,经观察问题不存在,算是解决了。(官网也提到CosClient是线程安全,原文是:COSClient 是线程安全的类,允许多线程访问同一实例。因为实例内部维持了一个连接池,创建多个实例可能导致程序资源耗尽,请确保程序生命周期内实例只有一个,并在不再需要使用时,调用 shutdown 方法将其关闭。如果需要新建实例,请先将之前的实例关闭。https://cloud.tencent.com/document/product/436/10199)
1、操作很不规范,日常绝大多数时候都是运维同学帮忙操作,很多时候只看到heap.hprof文件,并根据文件去找到泄露的对象,再去定位代码,在机器上实操较少。
2、arthas很少用,查了一些命令行非常丰富,很有必要深入学习一下,在生产排查问题绝对是利器。
3、因为本服务endpoint只有消息队列,有很好的重试机制以及程序的幂等处理,故而在服务节点上的重启成本较低,如果是基于微服务间连接的方式,比如dubbo或者eureka,一般没有很好的重试机制就需要一些优雅的上下线机制来协助。比如在heap dump前尽量从服务集群中摘除,切断流量入口,然后再操作。