Netty线程数膨胀案例
Posted Netty之家
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Netty线程数膨胀案例相关的知识,希望对你有一定的参考价值。
问题描述
分布式服务框架在进行现网问题定位时,Dump 线程堆栈之后发现Netty的NIO线程竟然有3000多个,大量的NIO线程占用了系统的句柄资源、内存资源、CPU资源等,引发了一些其它问题,需要尽快查明原因并解决线程过多问题。
问题分析
在研发环境中模拟现网组网和业务场景,使用jmc工具进行问题定位,
使用飞行记录器对系统运行状况做快照,模拟示例图如下所示:
获取到黑匣子数据之后,可以对系统的各种重要指标做分析,包括系统数据、内存、GC数据、线程运行状态和数据等,如下图所示:
通过对线程堆栈分析,我们发现Netty的NioEventLoop线程超过了3000个!
对服务框架协议栈的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线程数膨胀案例的主要内容,如果未能解决你的问题,请参考以下文章