8. Netty中线程处理 - NioEventLoopGroup,NioEventLoop

本文详细剖析了Netty中的线程处理机制,包括EventLoopGroup(boss和worker)的使用,Channel如何绑定到特定线程,以及NioEventLoopGroup的初始化和工作原理。重点讲解了NioEventLoop的执行逻辑和Channel与NioEventLoop的绑定过程。

通过之前关于Netty的分析,我们已经了解到,一般我们在Netty服务端启动的时候会传入boss线程组和worker线程组,这节结合之前我们的分析,来聊聊Netty中的线程处理。
我们知道,Netty是一个高性能的网络框架,针对网络中,我们一般常见处理是网络的连接的建立、消息读写等。这些在Netty中线程模型是怎样的。
首先说一下一个结论,在Netty中每个Channel都会在全局运行的时候绑定到一个线程中去,不管是ServerChannel还是ClientChannel。
首先一般的Netty服务端启动程序如下:

public static void main(String[] args) {
   
   
        EventLoopGroup boss = new NioEventLoopGroup();
        EventLoopGroup worker = new NioEventLoopGroup();
        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(boss, worker)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<SocketChannel>() {
   
   
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
   
   
                        socketChannel.pipeline().addLast("hello world hanlder", new HelloWorldHandler());
                    }
                });
        try {
   
   
            ChannelFuture channelFuture = bootstrap.bind(8080).sync();
            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
   
   
            e.printStackTrace();
        } finally {
   
   
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }
    }

或者也可以这样:

public static void main(String[] args) {
   
   
        EventLoopGroup worker = new NioEventLoopGroup();
        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(worker)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<SocketChannel>() {
   
   
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
   
   
                        socketChannel.pipeline().addLast("hello world hanlder", new HelloWorldHandler());
                    }
                });
        try {
   
   
            ChannelFuture channelFuture = bootstrap.bind(8080).sync();
            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
   
   
            e.printStackTrace();
        } finally {
   
   
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }
    }

这里需要强调的一点是,第一种方式中,boss线程组如果没有绑定多个端口的情况下,建议手动指定线程个数为1,因为如果不指定线程数的话,Netty中默认线程组中线程的数量是CPU*2,而Netty中对于Server的Channel来说,是根据端口号来绑定线程的,当然了,这样指定也是可以的,只不过,只有一个线程会运行,其他的线程只是创建了,并未运行。
在Netty中我们常用的线程组就是NioEventLoopGroup,而其中的线程为NioEventLoop,二者的类继承结构如下:
在这里插入图片描述

在这里插入图片描述

这里说下,上面这两段代码的区别,第一种就是我们常见的,一个worker线程,一个boss线程,第二种,其实也很好理解,我们看下其方法实现就全明白了:

    super.group(parentGroup);
        if (this.childGroup != null) {
   
   
            throw new IllegalStateException("childGroup set already");
        }
        this.childGroup = ObjectUtil.checkNotNull(childGroup, "childGroup");
        return this;
    }
public ServerBootstrap group(EventLoopGroup group) {
   
   
        return group(group, group);
    }

其实说白了,第二种方式就是worker线程组既充当了boss线程组又充当了woker线程组,仅此而已,没有什么特别的,因为Netty中是每个Channel绑定到一个线程上去的,和具体的线程池关系不大。

首先我们看下NioEventLoopGroup是怎么进行初始化的,在父类MultithreadEventExecutorGroup中,进行了如下处理:

protected MultithreadEventExecutorGroup(
6:09:45.702 [nioEventLoopGroup-8-1] ERROR c.F.C.I.p.GNGateMessageProcessor - [replyMsg,182] - Cannot invoke "com.FX.Charg.IOT.GN.GNEBikeDeviceServer.sendMessage(byte[], String)" because "this.tcpServer" is null 16:09:45.702 [nioEventLoopGroup-8-1] ERROR c.F.C.I.G.GNGateServerReceiver - [channelRead,63] - java.lang.NullPointerException: Cannot invoke "com.FX.Charg.IOT.GN.GNEBikeDeviceServer.sendMessage(byte[], String)" because "this.tcpServer" is null at com.FX.Charg.domain.devices.Devices.sendMessage(Devices.java:639) at com.FX.Charg.IOT.processor.GNGateMessageProcessor.replyMsg(GNGateMessageProcessor.java:180) at com.FX.Charg.IOT.processor.GNGateMessageProcessor.replyDeviceLogin(GNGateMessageProcessor.java:172) at com.FX.Charg.IOT.processor.EBikeMessageProcessor.onDeviceLogin(EBikeMessageProcessor.java:207) at com.FX.Charg.IOT.processor.EBikeMessageProcessor.process(EBikeMessageProcessor.java:88) at com.FX.Charg.IOT.GN.GNGateServerReceiver.onChannelRead(GNGateServerReceiver.java:18) at com.FX.Charg.IOT.tcp.TCPServerReceiver.channelRead(TCPServerReceiver.java:55) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1357) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:868) at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166) at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:788) at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:724) at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:650) at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:562) at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997) at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) at java.base/java.lang.Thread.run(Thread.java:1583)
最新发布
08-29
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值