Netty线程数膨胀案例

Posted Netty之家

tags:

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

问题描述

分布式服务框架在进行现网问题定位时,Dump 线程堆栈之后发现Netty的NIO线程竟然有3000多个,大量的NIO线程占用了系统的句柄资源、内存资源、CPU资源等,引发了一些其它问题,需要尽快查明原因并解决线程过多问题。


问题分析

在研发环境中模拟现网组网和业务场景,使用jmc工具进行问题定位,

使用飞行记录器对系统运行状况做快照,模拟示例图如下所示:



获取到黑匣子数据之后,可以对系统的各种重要指标做分析,包括系统数据、内存、GC数据、线程运行状态和数据等,如下图所示:


Netty线程数膨胀案例


通过对线程堆栈分析,我们发现Netty的NioEventLoop线程超过了3000个!


Netty线程数膨胀案例


对服务框架协议栈的Netty客户端和服务端源码进行CodeReview,发现了问题所在:

  • 客户端每连接1个服务端,就会创建1个新的NioEventLoopGroup,并设置它的线程数为1;

  • 现网有300个+节点,节点之间采用多链路(10个链路),由于业务采用了随机路由,最终每个消费者需要跟其它200多个节点建立长连接,加上自己服务端也需要占用一些NioEventLoop线程,最终客户端单进程线程数膨胀到了3000多个。


业务的伪代码如下:


for(Link linkE : links)

{

EventLoopGroup group = new NioEventLoopGroup(1);

Bootstrap b = new Bootstrap();

b.group(group)

.channel(NioSocketChannel.class)

.option(ChannelOption.TCP_NODELAY, true)

// 此处省略.....

b.connect(linkE.localAddress, linkE.remoteAddress);

}


如果客户端对每个链路连接都创建一个新的NioEventLoopGroup,则每个链路就会占用1个独立的NIO线程,最终沦为 1连接:1线程 这种同步阻塞模式线程模型。随着集群组网规模的不断扩大,这会带来严重的线程膨胀问题,最终会发生句柄耗尽无法创建新的线程,或者栈内存溢出。


从另一个角度看,1个NIO线程只处理一条链路也体现不出非阻塞I/O的优势。案例中的错误线程模型如下所示:



案例总结

无论是服务端监听多个端口,还是客户端连接多个服务端,都需要注意必须要重用NIO线程,否则就会导致线程资源浪费,在大规模组网时还会存在句柄耗尽或者栈溢出等问题。


Netty官方Demo仅仅是个Sample,对用户而言,必须理解Netty的线程模型,否则很容易按照官方Demo的做法,在外层套个For循环连接多个服务端,然后,悲剧就这样发生了。


修正案例中的问题非常简单,原理如下:



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

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

Jmeter命令行控制线程数

案例分享Netty线程安全疑问

netty - 线程模型 reactor

netty - 线程模型 reactor

为啥我们真的需要多个 netty boss 线程?