Netty 防止内存泄漏措施

本文主要分析了直播平台基于 Netty 构建的服务端在高并发场景下出现的内存泄漏问题,以及解决方案。问题表现为老年代满,Full GC 频繁,内存溢出,主要原因是消息发送积压。为了解决这个问题,服务端设置了消息发送的高低水位,根据并发接入数和内存大小调整策略,有效防止了发送队列积压。此外,文章还讨论了消息接收、ByteBuf 的申请和释放策略,以及 Netty 服务端的高并发保护,提供了防止内存泄漏的多种措施。
摘要由CSDN通过智能技术生成

1. 背景

1.1 直播平台内存泄漏问题

某直播平台,一些网红的直播间在业务高峰期,会有 10W+ 的粉丝接入,如果瞬间发生大量客户端连接掉线、或者一些客户端网络比较慢,发现基于 Netty 构建的服务端内存会飙升,发生内存泄漏(OOM),导致直播卡顿、或者客户端接收不到服务端推送的消息,用户体验受到很大影响。

1.2 问题分析

首先对 GC 数据进行分析,发现老年代已满,发生多次 Full GC,耗时达 3 分多,系统已经无法正常运行(示例):

图 1 直播高峰期服务端 GC 统计数据

Dump 内存堆栈进行分析,发现大量的发送任务堆积,导致内存溢出(示例):

图 2 直播高峰期服务端内存 Dump 文件分析

通过以上分析可以看出,在直播高峰期,服务端向上万客户端推送消息时,发生了发送队列积压,引起内存泄漏,最终导致服务端频繁 GC,无法正常处理业务。

1.3 解决策略

服务端在进行消息发送的时候做保护,具体策略如下:

  1. 根据可接入的最大用户数做客户端并发接入数流控,需要根据内存、CPU 处理能力,以及性能测试结果做综合评估。

  2. 设置消息发送的高低水位,针对消息的平均大小、客户端并发接入数、JVM 内存大小进行计算,得出一个合理的高水位取值。服务端在推送消息时,对 Channel 的状态进行判断,如果达到高水位之后,Channel 的状态会被 Netty 置为不可写,此时服务端不要继续发送消息,防止发送队列积压。

服务端基于上述策略优化了代码,内存泄漏问题得到解决。

1.4. 总结

尽管 Netty 框架本身做了大量的可靠性设计,但是对于具体的业务场景,仍然需要用户做针对特定领域和场景的可靠性设计,这样才能提升应用的可靠性。

除了消息发送积压导致的内存泄漏,Netty 还有其它常见的一些内存泄漏点,本文将针对这些可能导致内存泄漏的功能点进行分析和总结。

2. 消息收发防内存泄漏策略

2.1. 消息接收

2.1.1 消息读取

Netty 的消息读取并不存在消息队列,但是如果消息解码策略不当,则可能会发生内存泄漏,主要有如下几点:

1. 畸形码流攻击:如果客户端按照协议规范,将消息长度值故意伪造的非常大,可能会导致接收方内存溢出。

2. 代码 BUG:错误的将消息长度字段设置或者编码成一个非常大的值,可能会导致对方内存溢出。

3. 高并发场景:单个消息长度比较大,例如几十 M 的小视频,同时并发接入的客户端过多,会导致所有 Channel 持有的消息接收 ByteBuf 内存总和达到上限,发生 OOM。

避免内存泄漏的策略如下:

  1. 无论采用哪种解码器实现,都对消息的最大长度做限制,当超过限制之后,抛出解码失败异常,用户可以选择忽略当前已经读取的消息,或者直接关闭链接。

以 Netty 的 DelimiterBasedFrameDecoder 代码为例,创建 DelimiterBasedFrameDecoder 对象实例时,指定一个比较合理的消息最大长度限制,防止内存溢出:

/**
{1}
* Creates a new instance.
{1}
*
{1}
*@parammaxFrameLength the maximum length of the decoded frame.
{1}
* A {@linkTooLongFrameException} is thrown if
{1}
* the length of the frame exceeds this value.
{1}
*@paramstripDelimiter whether the decoded frame should strip out the
{1}
* delimiter or not
{1}
*@paramdelimiter the delimiter
{1}
*/

publicDelimiterBasedFrameDecoder(

intmaxFrameLength,booleanstripDelimiter, ByteBuf delimiter) {

this(maxFrameLength, stripDelimiter,true, delimiter);

}
  1. 需要根据单个 Netty 服务端可以支持的最大客户端并发连接数、消息的最大长度限制以及当前 JVM 配置的最大内存进行计算,并结合业务场景,合理设置 maxFrameLength 的取值。

2.1.2 ChannelHandler 的并发执行

Netty 的 ChannelHandler 支持串行和异步并发执行两种策略,在将 ChannelHandler 加入到 ChannelPipeline 时,如果指定了 EventExecutorGroup,则 ChannelHandler 将由 EventExecutorGroup 中的 EventExecutor 异步执行。这样的好处是可以实现 Netty I/O 线程与业务 ChannelHandler 逻辑执行的分离,防止 ChannelHandler 中耗时业务逻辑的执行阻塞 I/O 线程。

ChannelHandler 异步执行的流程如下所示:

图 3 ChannelHandler 异步并发执行流程

如果业务 ChannelHandler 中执行的业务逻辑耗时较长,消息的读取速度又比较快,

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值