Netty中线程名称的示例分析

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

成都创新互联公司是一家专业提供烈山企业网站建设,专注与成都网站制作、成都做网站、H5场景定制、小程序制作等业务。10年已为烈山众多企业、政府机构等服务。创新互联专业网络公司优惠进行中。

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

Netty中线程名称的示例分析

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

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

Netty中线程名称的示例分析

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

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

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

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

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

Netty中线程名称的示例分析

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

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

Netty中线程名称的示例分析

备注: 示例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() {                        @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查看线程.

Netty中线程名称的示例分析

通过telnet连接

Netty中线程名称的示例分析

Netty中线程名称的示例分析

我们发现多了两个线程, 因为我们通过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也会被实例化.

Netty中线程名称的示例分析

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

final ThreadFactory threadFactory = new DefaultThreadFactory(DefaultThreadFactory.toPoolName(getClass()), false, Thread.NORM_PRIORITY, null);

再跟踪下DefaultThreadFactory

Netty中线程名称的示例分析

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

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

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


当前标题:Netty中线程名称的示例分析
标题来源:http://hbruida.cn/article/jjcpgh.html