线上服务一直很稳定,最近忽然cpu使用率百分百,诡异问题排查

线上某服务一直运行很稳定,最近忽然就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链接。这下只能将限流组件sentinelbug的猜想去掉了,由于昨天去掉配置后,也并无看到sentinel线程占用cpu太高的现象了,只能排除。继续统计redissocket打开数量。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的,出异常也会移除缓存,只是清理时间间隔比较长,一个小时才清理一次。

到此,我就再也不往下分析了。去掉内存缓存是不可能的,只能计算业务所须要的内存,提高实例的内存。经过数据库查询,估算须要缓存的记录数是四百多万,本地写了个测试用例,计算四百多万记录大概消耗700m的内存,所以,只须要将机器的内存稍微提升一点就没有问题了。

可是我想不通的是,redis链接文件句柄数为何会如此高呢,不是用了链接池,是fgc影响了什么吗?STW也不至于致使redis文件句柄打开这么多吧?若是您知道,很是但愿您能留言帮我解答下疑惑,感激涕零!

相关文章
相关标签/搜索