线上某服务一直运行很稳定,最近忽然就cpu
百分百,rpc
远程调用所有失败,并走了mock
逻辑。重启后,一个小时后问题又重现。因而dump
线程栈信息,但不仔细看也看不出什么问题。因而就有了一番排查历程。redis
前一天,我dump
线程栈信息后发现占用cpu
高的线程是阿里的限流组件sentinel
,并根据线程栈信息知道,在sentinel
控制台会拉取该服务的qps
统计信息。这个接口会读取本地文件内容,由于qps
信息是输出到/root/logs/csp/
目录下的某个文件的,而且是用NIO
方式读取文件。所以我猜想是sentinel
在某种条件下会触发死循环bug
,也就能解释得清楚为何cpu
会出现百分百的使用率。数据库
带着这个猜想,我将限流功能去掉,觉得问题就能解决了,可是没想到次日早上有看到这个问题。因而我先将该服务的其它节点重启,留下一个节点,并将剩下的节点从注册中心中剔除。可没想到的是,注册中心已经没有这个节点了,应该是全部该服务的节点都与注册中心失联了,其它是由于重启以后从新注册到注册中心的。缓存
既然没有注册到注册中心,其它服务调用直接走mock
逻辑也解释得通。继续前一天的进度,我首先是查看文件句柄数是否打开不少,因而使用lsof
查看,果真不出所料。socket
进程15567
打开的文件句柄数20
多万,所以我更愿意相信是sentinel
读取文件出现bug
的猜想,由于我想起前一天限流功能尚未彻底去除,只是去除了限流配置。接着查看这个进程是否打开了/root/logs/csp
目录下的文件(这个目录是从限流组件的源码中找出来的)。测试
遗憾的是看不到每一个文件的打开数量。接着我用lsof -p 进程id
命令查看下,该进程打开的文件句柄信息。线程
因而我发现打开最多的不是/root/logs/csp
目录下的文件,而是redis
链接。这下只能将限流组件sentinel
有bug
的猜想去掉了,由于昨天去掉配置后,也并无看到sentinel
线程占用cpu
太高的现象了,只能排除。继续统计redis
的socket
打开数量。3d
确实对上了,文件句柄数达上限的问题确实是与redis-cluster
有关。因而我使用redis-cli
命令链接上redis
服务,看下是否链接数增多了,是否与该进程持有的文件句柄数对得上。由于前一天我也发现某消息消费服务偶尔出现链接超时的状况。code
然而并未发现什么异常,客户端链接数与日常同样。只能悬于此了。因而我又去分析线程栈信息,发现大量的dubbo
线程处于wait
状态,且都是阻塞在归还redis
链接到链接池以及从链接池中申请链接这一步。cdn
还有少几个dubbo
如今是runing
状态,缘由是这些线程走了内存缓存,我用aop
拦截请求,命中内存缓存的不会往下走业务逻辑,也就不会用到redis
。所以这些线程处理的请求是正常的。blog
那么为何cpu
会百分百呢?大片的业务线程都已经被阻塞住了,都在wait
状态,究竟是什么线程占用如此高的cpu
。因而我才想到用top
命令去查看。
看到两个非业务相关的线程,占用很是高的cpu
,因而又回去琢磨线程栈信息,与top
命令显示的command
列对得上的也就只有两个垃圾回收线程。
因而就是jstat
命令查看gc
垃圾回收状况。
连续使用jstat
命令查看gc
状况,发现每次执行命令,大概是1
秒多,fgc
的次数就加1
了。问题的真相已经很是接近。找到这里,就是排查业务代码了。
因为使用了内存缓存,会致使full gc
的状况很正常,我首先想到两点,一是缓存数据太多,二是缓存数据清理的定时任务执行失败了。并且因为以前该服务一直很稳定,猜想多是数据格式不正确触发的bug
,致使数据清理失败,越积越多。可是看了代码后,我是加了try
-catch
的,出异常也会移除缓存,只是清理时间间隔比较长,一个小时才清理一次。
到此,我就再也不往下分析了。去掉内存缓存是不可能的,只能计算业务所须要的内存,提高实例的内存。经过数据库查询,估算须要缓存的记录数是四百多万,本地写了个测试用例,计算四百多万记录大概消耗700
多m
的内存,所以,只须要将机器的内存稍微提升一点就没有问题了。
可是我想不通的是,redis
链接文件句柄数为何会如此高呢,不是用了链接池,是fgc
影响了什么吗?STW
也不至于致使redis
文件句柄打开这么多吧?若是您知道,很是但愿您能留言帮我解答下疑惑,感激涕零!