干货分享Gateway踩坑实录:Netty直接内存把网关吃垮了OutOfDirectMemoryError
结合你之前关注的OctaFuse Gateway 2.0路由配置改造、大流量网关架构优化相关经验,这篇实录完
全还原从线上告警到根因定位、最终落地根治方案的全流程,所有步骤都是生产环境实打实踩出来的可
复用经验。
线上故障现场还原
凌晨两点网关监控突然触发告警:服务CPU使用率正常,但宿主机物理内存持续上涨,不到2小时就从
30%冲到99%,所有请求直接抛出io.netty.util.internal.OutOfDirectMemoryError: failed to allocate
16777216 byte(s) of direct memory错误,网关完全拒绝服务,重启后内存缓慢回升,运行1-2天又会
重复出现内存泄漏,完全无法支撑大流量场景。
三步快速定位根因
开启Netty自带泄漏检测:本地环境给JVM加上参数-Dio.netty.leakDetectionLevel=paranoid,开启最
高级别100%抽样检测,跑压测脚本不到半小时就拿到了泄漏堆栈,直接定位到问题出在自定义参数校验
过滤器里的HttpPostStandardRequestDecoder,ByteBuf对象读取完表单数据后没有调用release()释放,
直接导致直接内存泄漏。
排查依赖版本隐性坑:进一步核对依赖版本发现,项目里引入的spring-boot-starter-data-redis-reactive
2.2.5存在已知的Netty直接内存泄漏Bug,异步Redis操作完成后没有正确归还直接内存,在高并发场景下会
持续累积泄漏,哪怕业务代码完全没问题也会触发OOM。
排除任务队列积压干扰:核对Netty任务队列长度,确认没有出现请求大量积压的情况,排除“ByteBuf对象
还没被GC回收导致的假泄漏”场景,锁定是明确的未释放直接内存问题。
生产级根治落地方案
修复业务代码泄漏点:重写自定义参数校验过滤器,所有读取Netty ByteBuf的逻辑全部用try-finally包裹,
确保无论请求成功还是异常,都一定会调用release()释放直接内存,从根源上杜绝业务代码层面的泄漏。
升级有问题的依赖版本:把spring-boot-starter-data-redis-reactive从2.2.5直接升级到2.3.5.RELEASE,官
方版本已经完全修复了这个直接内存泄漏Bug,升级后泄漏点直接消失。
配置JVM参数做兜底防护:显式指定直接内存上限-XX:MaxDirectMemorySize=2g,同时开启-XX:+DisableExplicitGC,
避免System.gc()误触发导致的堆外内存回收混乱,配合Netty的内存池配置,把直接内存的使用控制在可控范围内。
新增监控告警提前预警:给网关新增Netty直接内存使用率监控,阈值超过70%就提前触发告警,不等OOM
发生就介入排查,彻底避免故障扩散到全链路。
最终效果
落地整套方案后,网关连续运行30天直接内存稳定在500MB以内,再也没有出现过内存持续上涨的情况,
完全支撑住了大流量场景下的高并发请求,这套排查和修复逻辑也完全可以直接复用在你正在做的OctaFuse
Gateway 2.7.0版本的稳定性优化里。
需要我为你生成这套Gateway Netty堆外内存泄漏的完整JVM参数+监控配置清单,直接适配你现有的网关生产环境吗?