一、发现问题
生产突然出现大量请求间隙出现500,查看监控发现gateway其中一个节点挂掉了!!!!!
二、临时处理
临时紧急下掉了这个节点的流量,加上万能的重启,恢复正常了。。。
三、分析问题整理思路
现在开始分析问题以及思路整理。
起初我满脑子的问号???
网关只有一个入参解码操作,没有什么复杂业务怎么会出现OOM??
或者在那个时间点流量突然太大承接不足啦??
在一个监控报备看内存也没有往上飙升就直接挂掉啦??
先点一支冷静冷静,平复下生产事故的心情
又仔细去看了一遍日志发现OOM报的是Direct buffer memory,报的是直接内存不是堆内存
开始有眉目了,继续分析
先看一张图(网上扒的图右下角有水印出处)
竟然知道是直接内存的溢出那就清晰多了
我上面监控中是jvm内存的指标,这不是堆内存溢出自然就不会出现飙升的情况
流量突然太大承接不足这个,排查了系统的业务以及业务峰值的时间点也不是在出现问题的时间段,所以也不会是流量太大导致的
竟然知道是直接内存的问题,就以这个为出发点继续开始提问然后答题!!!
直接内存,一般都是NIO,并且不受GC管理
思考思考···
gateway底层使用的就是netty典型的NIO模型
难道解码处理时,gateway转发请求时使用直接内存过多,未清理导致直接内存OOM?
OK,到此已经将出现OOM的问题大致分析清楚,开始验证想法!!
JVM不太熟悉的朋友可以看看这篇博客(JVM——感谢黑马程序员官方文档_jvm官方文档-优快云博客)
四、定位问题、问题复现
本地启动网关服务和业务服务
为了更容易复现问题网关服务限制直接内存最大10M:-XX:MaxDirectMemorySize=10m
启动完成查看直接内存使用情况,一切正常
JMeter模拟并发推送请求进入
发现立马就打满了,直接超出10M限制就出现报OOM:Direct buffer memory
生产限制会比测试限制大,而且也不会立马巨量的流量给打满,会慢慢获取内存进入直至内存溢出
这是网关服务自定义的过滤器代码,用来做处理请求参数解密解析处理(便于观察业务部分已注释)
先简单去掉这个这个过滤器然后运行测试
哦~不错哟!
确定是这个过滤器的问题检查代码分析问题
过滤器进来就是用的gateway工具包里的这个方法org.springframework.cloud.gateway.support.ServerWebExchangeUtils.cacheRequestBody
()
看看这个方法里面的逻辑
基本就是这两步:1.将原来请求的body重新封装 2.通过decorate()方法处理dataBuffer
这里的dataBuffer使用是我们的直接内存,继续看decorate()方法怎么使用的,最重要看使用后是否有释放这里块内存
可以看到decorate()方法使用完dataBuffer后并没有做释放的操作(这就很好解释为什么内存使用了为什么没释放)
接下来继续看代码,也只是使用了但并未释放
增加使用后释放内存,查看测试结果
OK,至此问题已确定
五、代码改造测试
正常来讲只需要增加一行代码释放内存即可,可实际场景比较复杂直接释放会导致后续逻辑无法正常运行
那自己写又不知道怎么写,怎么办勒!小时候不会写作业怎么办,直接抄啊!!!
抄ServerWebExchangeUtils.cacheRequestBody()的逻辑,一共这3步
这下面是改造后的代码(使用完一定记得释放内存)
改造完,继续测试
加大并发数,加大测试时长
这里也有超出的的情况会出现OOM,那时因为请求太多并发太搞10M不够了
测试结果:
六、问题分析与总结
在使用NIO模型时,注意释放内存!!直接内存GC不管的!!!
不太清楚的工具不会用,不要瞎用!!
遇到问题不要慌,有万能重启在!然后点一支冷静下来,思考解决方案。