记一次线上服务器oom 排查过程

2,410 阅读3分钟

太长不看版 这篇文章讲了啥

  • 记录本次oom的排查方法。
  • 一些好用的命令帮助观察堆,gc等状态。jmap和jstat。
  • 事故发生的原因,解决方法和回顾

1: 事故的简述

某天晚上的21点,业务模块(微服务)调微信模块的rpc频繁超时,接到大量报错。排查到一半收到如下报警。 堆溢出了。微信模块机器全部挂掉。

2: 开始排查

立刻紧急重启机器,先保证线上服务可用。然后下载heapdump.hprof文件。需要在jvm启动的时候加参数才有dump文件。否则需要使用jmap命令生成。

因为dump是二进制文件需要使用图形化工具分析。我用的是eclipse memory analyzer。

点击leak suspects. 看下可能的内存泄漏的点。

发现web容器建立了很多链接实例,占用了非常多内存。 又立刻用jstat -gcutil pid看了下进程gc情况,fgc触发了非常多次,且ygc消耗的时间都很长。马上就想到是不是因为很多连接还在keep alive状态并没有归还给连接池,新的请求过来只能建立新的连接对象connection。且这些等待中的连接是不会被垃圾回收器回收的,因为还在alive。这样就会出现已经创建的对象无法回收,新连接对象却在被不停创建的情况。这样就消耗尽了堆内存。

3: 几个好用的观察heap的命令

可以用这个命令看gc状态。挺重要的,因为有些频繁fgc的,就可以提前处理了避免oom。

可以用这个命令直接看heap状态。

jmap -heap pid

可以用这个看堆上的对象【粗略看下 具体的还得用分析工具看链路】

jmap -histo:live pid | sort -k 2 -g -r| less

4: 论证

既然有了思路,就只是验证就可以了。看了事发地前后的日志,基本上锁定了事故现场。 首先在9点的时候有一个比较大的流量。2台机器在1分钟左右接了18.2w个请求。大部分请求都是这个:

微信服务器在发送模版消息以后会推送【Template Send Job Finish】的事件到开发者服务器处理。其实挺坑的。如果超时了,微信还会每个请求再发。

再加上为了第一时间回复给微信服务器success。处理回调的代码逻辑用了异步逻辑。 但是我们再次发了之前的错误,这个异步用了spring默认的@async注解。这个注解不初始化自己的线程池就是天坑。之前已经出了一次小事故 @Async用了springboot提供的线程池,居然引发了事故?。这个会导致任务堆积。任务堆积导致连接超时。

总结下,就是天灾【突然大规模的请求】+ 人祸【代码写的有问题】。

5: 解决和回顾

  • 优化线程池配置 注入自己配置的线程池
  • 服务器扩容。不知不觉已经累计用户快100w了,微信服务还是只有两台机器。这次加了两台。

过了7,8天,又经历了几次10w级别的回调,cpu基本上只有30util。效果很明显。