Netty中线程名称的示例分析

Netty中线程名称的示例分析

小编给大家分享一下Netty中线程名称的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!

NioEventLoop创建过程.
创建的第一个步骤就是创建线程执行器ThreadPerTaskExecutor, 这个线程执行器就是用来创建Netty底层的线程的. 在学习Java的Thread时候,线程默认名称类似thread-0,thread-1,thread-2...以此类推. 而线程的名称对于我们排查问题的时候也是起到很大作用的, 因此我们在设计线程池, 也会根据一定的规则给线程池中的线程命名, 这也是一个好的习惯.
在Netty中自然也会给线程池中的线程命名, 接下来我们就分析下它的命名规则.

上面的图中有两个线程池,一个叫bossGroup,另一个叫workerGroup. 它们都属于EventLoopGroup类型. 前面我们也提高过, bossGroup负责接收客户端请求, workerGroup犹如其名一样, 是个'工人',负责处理客户端的IO读写操作的.

在这两个Group内部有很多个NioEventLoop

如果我们在创建EventLoopGroup时, 没有传线程数量, 那么每个线程池默认创建 2*CPU个线程.
每个线程的命名规则: nioEventLoop-n-n, 例如nioEventLoop-2-1

接下来我们解释下后面两个数字如何确定的.

我们就拿nioEventLoop-2-1这个为例

备注: 包括dubbo, RocketMQ这样的框架在内, 它们底层在使用Netty时的代码类似, 如下

EventLoopGroupbossGroup=newNioEventLoopGroup(1);EventLoopGroupworkerGroup=newNioEventLoopGroup();

上面的代码主线程main从上往下执行的时候, 第一个bossGroup是第1个线程池, 第二个workerGroup是第2个线程池. 依次类推, 如果代码里new了5个NioEventLoopGroup, 那么第五个group就是第5个线程池.
因此我们示例中的nioEventLoop-2-1的数字2就表示第2个线程池的意思. 也就是nioEventLoop-2-1这个名字的线程是在第2个线程池中的.

我们继续分析nioEventLoop-2-1中数字1的由来.

每个EventLoopGroup中有多个NioEventLoop. 当NioEventLoop在启动的时候会创建底层的线程.根据 选择器EventExecutorChooser, 从线程池中第一个被选择出来为客户端提供服务的NioEventLoop就是第1个线程, 从线程池中第二个被选择出来为客户端提供服务的NioEventLoop就是第2个线程, 以此类推. 所以示例nioEventLoop-2-1中的数字1就是表示线程池中的第1个线程, 整体就表示第2个线程池中的第1个线程.

备注: 示例nioEventLoop-2-1中的nioEventLoop这个名字是固定的.


实战

接下来我们从实际去看下它们的名字

服务端代码如下


import io.netty.bootstrap.ServerBootstrap;import io.netty.channel.ChannelFuture;import io.netty.channel.ChannelInitializer;import io.netty.channel.ChannelPipeline;import io.netty.channel.EventLoopGroup;import io.netty.channel.nio.NioEventLoopGroup;import io.netty.channel.socket.nio.NioServerSocketChannel;import io.netty.channel.socket.nio.NioSocketChannel;import io.netty.handler.codec.FixedLengthFrameDecoder;import io.netty.handler.codec.string.StringDecoder;import io.netty.handler.codec.string.StringEncoder;import io.netty.handler.logging.LogLevel;import io.netty.handler.logging.LoggingHandler;import io.netty.handler.timeout.IdleStateHandler;
import java.util.concurrent.TimeUnit;
public class Server {

public static void main(String[] args) throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap serverBootstrap = new ServerBootstrap();
try{ serverBootstrap.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .handler(new LoggingHandler(LogLevel.INFO)) .childHandler(new ChannelInitializer<NioSocketChannel>() { @Override protected void initChannel(NioSocketChannel ch) {
ChannelPipeline channelPipeline = ch.pipeline(); // ... } });
// 绑定端口 同步等待成功 ChannelFuture channelFuture1 = serverBootstrap.bind("127.0.0.1", 8080).sync();
// 等待服务端监听端口关闭 channelFuture1.channel().closeFuture().sync(); } finally { // 执行到此处说明服务端已经关闭 bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); }
}
}

我们把上面的代码启动, 然后通过telnet 127.0.0.1 8080方式连接服务器. 我们使用JDK自带的jvisualvm查看线程.

通过telnet连接

我们发现多了两个线程, 因为我们通过telnet连接了两次, 所以多了两个线程. 其中第二个数字一个是-1, 另一个是-2,表示第1个和第2个线程的意思.

但是

根据上面的服务端代码和前面的讲解, 我们明明创建了两个线程池, 那么第一个数字应该是-1和-2才对, 可是我们实际观察发现, 却是-2和-3. (更准确的说, nioEventLoopGroup-2表示bossGroup, nioEventLoopGroup-3表示workerGroup). 我们的代码明明只是new出来2个NioEventLoopGroup, 现在实际观察却发现nioEventLoopGroup-1被别人占了.

我们从源码中寻找答案

当我们在代码中通过new实例化NioEventLoopGroup时, 由于NioEventLoopGroup继承MultithreadEventExecutorGroup, 所以这个MultithreadEventExecutorGroup也会被实例化.

从图中我们发现, 会实例化一个DefaultPromise, 其中有个GlobalEventExecutor.INSTANCE. 使用单例模式创建GlobalEventExecutor. 其中GlobalEventExecutor有个属性

finalThreadFactorythreadFactory=newDefaultThreadFactory(DefaultThreadFactory.toPoolName(getClass()),false,Thread.NORM_PRIORITY,null);

再跟踪下DefaultThreadFactory

我们看右下角发现了真相, -1被globalEventExecutor-1-使用了.

备注: DefaultThreadFactory这个工厂类在创建bossGroup和workerGroup都会被使用.

以上是“Netty中线程名称的示例分析”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注恰卡编程网行业资讯频道!

发布于 2021-12-28 22:17:53
收藏
分享
海报
0 条评论
38
上一篇:Netty中粘包和半包产生原因有哪些 下一篇:EXCEL数据分析技巧是什么
目录

    0 条评论

    本站已关闭游客评论,请登录或者注册后再评论吧~

    忘记密码?

    图形验证码