记录一次Gateway内存溢出问题排查

问题描述

线上运行一段时间的gateway服务网关会莫名其妙的“崩掉”,请求不通,看日志每个请求都报错,报错的大致意思是分配内存的时候,超过项目设置的最大内存。

这属于是比较典的内存溢出问题,只要找到没有释放内存的问题代码即可。

排查步骤

  • 首先看看线上运行的gateway内存情况,看看内存占用是不是一直在涨

但开了几天的监控页面,发现内存占用一直很低,甚至都还没触发堆内存的扩容(设置最大内存1G,但一直保持在500MB),非常诡异。。。。

大概曲线就和下面图一样,比较平稳

  • 那就怀疑是某个请求带了超大数据,让gateway内存崩掉

但项目里面数据最大的请求也不超过5M,并且用postman并发访问,gateway的堆内存都会被很好的回收,并不会造成内存溢出。。。

  • 又将跑了几天的gateway的heapdump文件下载下来研究一下

发现也没有问题,占用内存加起来不超过200M。。。。。

  • 线上的环境排查没招了,只有本地跑一个gateway看能不能复现,直接在本地设置服务最大内存50MB,然后用postman调用接口猛测

调用了一个数据量巨大的接口,返回数据5M左右,但连续猛调了几十次,gateway也没崩,看监控堆内存也被回收了的,说明普通的接口访问大概率不会造成溢出

  • 接着怀疑是文件上传导致的

马上用gateway上传24MB的PPT文件,第一次成功,第二次果然触发了同款报错,之后所有的请求过来,都会提示“内存分配超限制”,说明上传文件的请求会导致内存无法回收

但实际上项目中的文件上传功能用的很少,一个月上传的文件可能都不超过100MB,所以怀疑导致问题的请求可能跟文件上传请求一样有共同的逻辑

  • 接下来就是研究文件上传代码跟普通代码的逻辑区别了

gateway的主要逻辑都在几个filter里面,经过排查,在参数加密解密的filter里面需要参数解密的请求会调用这样一个方法resolveBodyFromRequest(),里面有一段代码DataBufferUtils.release(buffer);

Flux是一个响应式流,用于处理异步数据流,这个方法通过DataBufferUtils来获取流中的参数。而在顶级的filter里面,DataBufferUtils会将所有请求的流信息用DataBufferUtils订阅。DataBufferUtils.release(buffer)方法则会清除DataBufferUtils中buffer的数据缓冲区。

这就导致,不需要参数解密的请求,DataBufferUtils.release(buffer)不会执行,静态工具类的缓冲区也不会被回收。。。。

导致原因

因为在处理请求的异步数据流的时候,顶级filter用了DataBufferUtils静态的工具类来接收数据(他会为数据创建一个缓冲区,并不会直接占用数据大小的内存)

但释放数据缓冲区的代码写在了参数解密filter里面,且只有post的json和urlecode请求才会走获取参数清理缓冲区的方法,导致不需要数据加密的post请求(如登录请求、文件上传请求、白名单中的请求),就会跳过释放DataBufferUtils缓冲区的方法,久而久之就导致了DataBufferUtils中设置的缓冲区巨大。当某一天缓冲区大小超过服务能给他分配的最大上限时,DataBufferUtils获取数据就会报错之前那个内存分配不足的错。

所以在缓冲区未超过服务极限的情况下,直接观察堆内存占用是发现不了问题的......

解决办法

知道原因之后,解决就很简单了

只需要保证所有请求执行完之后,都清理DataBufferUtils的缓冲区就行了,这个回调在顶级filter的方法中有,在其中写上清理缓冲区的代码就行了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值