Netty 防止内存泄漏措施

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

1.2 问题分析
首先对 GC 数据进行分析,发现老年代已满,发生多次 Full GC,耗时达 3 分多,系统已经无法正常运行(示例):
Netty 防止内存泄漏措施

图 1 直播高峰期服务端 GC 统计数据
Dump 内存堆栈进行分析,发现大量的发送任务堆积,导致内存溢出(示例):
Netty 防止内存泄漏措施

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

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

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

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

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

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

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

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

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

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

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

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

无论采用哪种×××实现,都对消息的最大长度做限制,当超过限制之后,抛出解码失败异常,用户可以选择忽略当前已经读取的消息,或者直接关闭链接。
以 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);

}
需要根据单个 Netty 服务端可以支持的最大客户端并发连接数、消息的最大长度限制以及当前 JVM 配置的最大内存进行计算,并结合业务场景,合理设置 maxFrameLength 的取值。
2.1.2 ChannelHandler 的并发执行
Netty 的 ChannelHandler 支持串行和异步并发执行两种策略,在将 ChannelHandler 加入到 ChannelPipeline 时,如果指定了 EventExecutorGroup,则 ChannelHandler 将由 EventExecutorGroup 中的 EventExecutor 异步执行。这样的好处是可以实现 Netty I/O 线程与业务 ChannelHandler 逻辑执行的分离,防止 ChannelHandler 中耗时业务逻辑的执行阻塞 I/O 线程。

ChannelHandler 异步执行的流程如下所示:
Netty 防止内存泄漏措施

图 3 ChannelHandler 异步并发执行流程
如果业务 ChannelHandler 中执行的业务逻辑耗时较长,消息的读取速度又比较快,很容易发生消息在 EventExecutor 中积压的问题,如

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值