发送队列积压导致内存泄漏

导致Netty内存泄漏的原因很多,例如,使用内存池方式创建的对象忘记释放,或者系统处理压力过大导致发送队列积压。
尽管Netty 采用了NIO非阻塞通信,I/O处理往往不会成为业务瓶颈,但是如果客户端并发压力过大,超过了服务端的处理能力,又没有流控保护,则很容易发生内存泄漏。

【客户端发送大量数据】

问题代码:某个客户端建立多个线程循环发送消息,客户端的发送队列会比较繁忙,此时客户端内存使用会不断飙升。
 
统计GC数据,发现老年代已满,发生多次Full GC,耗时3分钟多,系统已经无法正常运行,如图5-2所示。查看CPU的使用情况,发现GC线程占用了大量的CPU资源,
分析原因查看dump图可以知道NioEventLoop,即消息处理handler线程内存溢出了,继续对引用关系进行分析,发现真正泄漏的对象是 WriteAndFlushTask,它包含了待
发送的客户端请求消息msg 及 promise对象,引用关系示例如图5-5所示。
Netty的消息发送队列为什么会积压呢?通过源码分析发现,调用Channel的write方法时,如果发送方为业务线程,则将发送操作封装成WriteTask,放到Netty的NioEventLoop中执行,源码如下(AbstractChannelHandlerContext):
这里有个结论,如果是业务方线程调用write方法,会被netty强制转化成使用NioEventLoop线程去执行,这里也符合netty的反应器模式设计:链接建立由Reactor线程池处理,接收到的消息转发给handler线程池处理,自然handler也必须负责消息的出栈
为防止客户端发送巨量消息导致发送队列堆积,则在发送前需要判断该channel能否可读,netty提供了高低水位来实现客户端消息流控,先用setWriteBufferHignWaterMark预设最高字节数,当达到这个高水位时channel会自动变为不可写,改动如下
在实际项目中,根据业务QPS规划、客户端处理性能、网络带宽、链路数、消息平均码流大小等综合因素计算并设置高水位(WriteBufferHighWaterMark)值,利用高水位做消息发送速度的流控,既可以保护自身,同时又能减轻服务端的压力,防止服务端被压挂

【什么情况会造成消息挤压发送不出去或发送不及时】

1.网络瓶颈,当发送速度超过网络链接处理能力,会导致发送队列积压。
2. 当对端读取速度小于己方发送速度,导致自身TCP发送缓冲区满,频繁发生write0字节时,待发送消息会在Netty发送队列中排队。 发送队列堆积的本质是服务端接收不及时,因为消息是从客户端send区转移到服务端recv区的,如果服务端recv区满了则客户端的send区也会逐渐变满,然后客户端数据不能放进send区则只能堆积到队列中。同样的,客户端接收消息不及时业务挤压服务端发送队列。这个情况很好模拟,在服务端程序cahnnelRead方法中设置断点,相当于不接收客户端消息,客户端就很容易出现这个情况
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

0x13

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值