Netty OOM案例

Posted Netty之家

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Netty OOM案例相关的知识,希望对你有一定的参考价值。

问题:最近公司某产品商用发布在即,连续性能测试1个小时左右,开始发生时延变大、应答消息丢失等问题,最后抛出OOM异常,服务端宕机。

异常日志如下:




问题分析

通过异常堆栈和HeapAnalyzer工具分析,发现是Netty的内存池直接内存溢出,由于业务的消息接收和发送ByteBuf都使用了内存池直接内存,首先排查消息接收ByteBuf,业务处理流程如下:

1、业务的解码器继承自LengthFieldBasedFrameDecoder,根据报文中的消息长度做半包解码,解码成功之后将消息投递到后端业务线程池;

2、业务没有主动释放消息接收ByteBuf, 由于Netty解码之后会主动释放ByteBuf,所以不主动释放也没问题


Netty OOM案例


排查完消息接收之后,再查看消息发送。消息发送流程是对请求消息包装之后,编码转发给其它第三方模块,消息发送采用了1个独立的发送线程,在发送线程中通过Netty的NiosocketChannel直接write ByteBuf,ByteBuf在发送线程中分配,发送完成之后没有调用release方法主动释放内存。

示例代码如下:


execut.execute(new Runnable() {

@Override

public void run() {

ByteBuf buf = PooledByteBufAllocator.DEFAULT.buffer(1024 * 1024);

CodeC.endcode(sendMessage, buf);

channel.write(buf);

//后续业务逻辑处理,没有主动释放内存

}

});


在业务线程中通过内存池申请了一个直接内存,编码发送之后并没有主动释放内存,是否有问题? 我们继续看Netty的源码:


Netty OOM案例


通过代码分析,我们发现当Netty的ChannelOutboundBuffer将ByteBuf发送之后,会将ByteBuf从Entry[] buffer 中删除,同时调用safeRelease方法将ByteBuf释放。即便业务代码不主动释放发送的ByteBuf,Netty也会帮助用户释放,不应该发生内存泄漏啊?!


查看业务的Netty版本,发现业务使用的是Netty 4.0.X版本,突然想到了前段时间Netty 4内存池泄漏问题:在业务线程中通过内存池申请内存,又在Netty的NIO线程中释放内存,这会导致内存泄漏。该问题是Netty 4内存池机制和线程模型优化导致的问题,原理如下:


Netty OOM案例


使用Netty 4.X +版本的内存池,内存的申请和释放必须要在同一个线程中,否则会导致内存引用错乱、内存溢出等问题。


问题定位出来之后,将内存池ByteBuf申请的代码迁移到ChannelHandler的CodeC中,由Netty的NIO线程统一申请和释放。优化之后,性能测试72个小时,内存占用平稳、GC正常,问题解决。


随后进行压力测试,客户端启动N个线程,使用同一个SocketChannel对服务端进行压测,24小时之后又发生了OOM异常,分析之后仍然是内存池的直接内存泄漏,怎么回事?


Netty OOM案例


通过定位发现,在压力测试模式下,消息发送速度大于消息接收处理的速度,也就是说ByteBuf的申请速度大于释放速度,这导致了内存池不断膨胀,最终内存溢出。


如何解决这个问题? 业务建议通过调大服务端work线程数的方式提升服务端并行处理性能,但实际行不通。因为对于单链路场景,1个链路只被某一个work线程处理,增加work线程是没有效果的。


既然通过增大服务端线程数无法解决问题,那有没有更好的解决办法?方法有三个:

1、放弃内存池,使用非内存池模式;

2、动态流量控制;

3、采用多链路的方式。


使用非内存池模式,内存最终被JVM回收,而不是缓存在线程中,因而只要堆内存设置合适就可以解决内存溢出问题。


动态流控方案:可以使用Netty默认提供的流量整形功能,它可以解决两个问题:

1、防止由于上下游网元性能不均衡导致下游网元被压垮,业务流程中断

2、防止由于通信模块接收消息过快,后端业务线程处理不及时导致的“撑死”问题


原理如下:




多链路方案:通过调大服务端work线程个数,提升服务端的并行处理性能,满足高峰期的浪涌冲击。




案例总结

尽管Netty使用起来比较简单,但是如何在高并发和负载情况下保证系统平稳运行,却是并非一件易事。

除了完善的性能测试、压力测试之外,对Netty底层处理机制的理解和Code Review也是必不可少的。


以上是关于Netty OOM案例的主要内容,如果未能解决你的问题,请参考以下文章

实践案例丨Netty案例集锦之多线程篇(续)

Netty4.x实战专题案例文章列表

跟着案例学Netty:Netty内存池泄漏问题

荐书 | Netty进阶之路:跟着案例学Netty

Netty之家举办案例分享赠书活动通知

从入门到实战,Netty多线程篇案例集锦