记一次Dubbo超时&CPU高负载问题排查
1.问题背景
最近经常有同事反馈我们灰度环境老的交易系统,这里简称trade,dubbo消费者调用其他服务超时,因为该项目维护人员众多,加上灰度环境发布较多,一直没有排查,然后五一前再次有同事反馈这个问题,刚好有空就准备分析一下引起超时的原因。
2.问题排查
2-1 问题描述
大量Dubbo服务调用超时
2-2 问题排查
查看日志(上图)发现dubbo超时日志里面的两个时间异常
client elapsed ,server elapsed 时间较长,加起来超过了timeout时间3s,导致服务超时,其中client elapsed 达到了699ms,看到这里第一感觉是很惊讶,为啥消费者端会花费这么长的时间
带着这个疑问,首先确认了这个时间的计算逻辑,具体源码见附图
error日志信息拼装方法
DefaultFuture类,
start = 实例化对象时初始化sent = 和provider通信时调用doSent 进行初始化
可以看出,client elapsed:699ms,表示consumer Future对象实例化到实际doSent之间耗时较长,所以这里产生了一个新的疑问:
为什么这么轻量化的逻辑却要花费699ms甚至更长的时间完成?
带着这个疑问,首先想到的是对服务器当前负载进行确认
下图为top命令截图,可以看到Cpu使用率在72%,
新的问题:Cpu为啥使用率这么高?
理论上来说灰度环境并没有很大的调用量,仅开发和测试用到,80%甚至90%的使用率显然不合理。当前排查重点变成了找出导致cpu高使用率的原因。
首先 top -H -p 22503,找到占用cpu的线程号 22509和22993计算16进制线程号 分别是57ed和59d1
jstack找到占用cpu的对应的线程信息分别是VM Thread和Netty
这里VMThread 是虚拟机线程,猜测是GC导致cpu使用率高
NettyClientWorker应该是dubbo消费者的正常线程,暂时没有分析
分析jvm GC信息,2s一次查询,发下FULL GC频繁
从结果看,YGC一直是604次,但是却频繁执行FULL GC,而且异常的是老年代使用率一直在4%左右
分析GC日志,发下FULL GC原因Full GC (Heap Inspection Initiated GC)
奇怪的是FULL GC前后,老年代使用内存一直都是210M,total却有5G,这里特地查询了启动参数,并未发现异常,只能从GC原因着手排查
百度Heap Inspection Initiated GC 发现,这个GC原因一般是由于jmap -histo:live 触发,该命令一般是用户查询当前堆里面对象存活数量时用到。
结合之前4-5s一次FullGc的频率来看,推测应该是运维有某个脚本在自动检测
查看当前有没有在执行的jmap线程
发现原因jmap -histo:live 22503 和jmaplog.sh
遗憾的是jmaplog.sh 文件以及被删除,从时间上看,2019年便已经启动了该线程。
猜测是当时跑过该脚本但是一直忘记kill了。
kill jmap检测脚本,cpu 使用率降低到9%
观察业务日志:没有发现超时异常
3.问题总结
jmap检测脚本 导致 jvm一直在FGC,频率大概在4-5s左右,每次FGC耗时0.6s左右导致服务器整体负载被打满,然后影响到dubbo服务
本文只是分析了引起dubbo超时的其中一个原因,后来排查分析dubbo超时还有另一个原因是因为 有个dubbo慢sql查询的调用在灰度大量调用(频率高数量大),线上对该方法进行了限流1的qps,灰度没有限流,该查询方法本身就有问题,会导致超时。