Netty源码分析系列之服务端Channel注册

扫描下方二维码或者微信搜索公众号菜鸟飞呀飞,即可关注微信公众号,阅读更多Spring源码分析Java并发编程文章。

微信公众号

问题

  1. 在JDK的原生NIO的写法中,通过serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT)将服务端channel注册到多路复用器selector上,那么在Netty中,又是如何将NioServerSocketChannel注册到多路复用器上的呢?在注册过程中,Netty又额外做了哪些事情呢?
  2. 在上一篇文章Netty源码分析系列之服务端Channel初始化中,分析了在init(channel)方法中,向pipeline添加了一个匿名类:ChannelInitializer,在该匿名类的initChannel(channel)方法中,执行了很重要的逻辑:向pipeline中添加了两个handler。但是上一篇文章是直接说了initChannel(channel)的执行结果,没有说代码是如何回调到这个匿名类的initChannel(channel)方法上的,本文接下来将详细说明这一点。

上篇回顾

当调用ServerBootstrap.bind(port)时,代码会执行到AbstractBootstrap.doBind(localAddress)方法,在doBind()方法中又会调用initAndRegister()方法。initAndRegister()方法简化后的代码如下。

final ChannelFuture initAndRegister() {
   
    Channel channel = null;
    try {
   
        /**
         * newChannel()会通过反射创建一个channel,反射最终对调用到Channel的构造器
         * 在channel的构造器中进行了Channel很多属性的初始化操作
         * 对于服务端而言,调用的是NioServerSocketChannel的无参构造器。
         */
        channel = channelFactory.newChannel();
        /**
         * 初始化
         * 调用init方法后,会想服务端channel的pipeline中添加一个匿名类,这个匿名类是ChannelInitializer
         * 这个匿名类非常重要,在后面channel的register过程中,会回调到该匿名类的initChannel(channel)方法
         */
        init(channel);
    } catch (Throwable t) {
   
        // 省略部分代码...
    }
    ChannelFuture regFuture = config().group().register(channel);
    // 省略部分代码...
    return regFuture;
}

initAndRegister()方法的作用是初始化服务端channel,即NioServerSocketChannel,并将服务端channel注册到多路复用器上。在上一篇文章Netty源码分析系列之服务端Channel初始化中分析了initAndRegister()方法的前半部分,即服务端channel初始化的过程。在服务端channel初始化的过程中通过channelFactory.newChannel()会反射调用到NioServerSocketChannel的无参构造器,最终会创建一个NioServerSocketChannel实例,在构造方法中会对NioServerSocketChannel的很多属性进行初始化,例如:pipeline、unsafe。接着调用init(channel)方法,会为NioServerSocketChannel设置options、attr等属性,最重要的是向NioServerSocketChannel的pipeline中添加了一个ChannelInitinalizer类型的匿名类。

当执行完init(channel)方法后,代码接着就会执行到ChannelFuture regFuture = config().group().register(channel);。这一行代码就是本文今天分析的重点,它的主要功能就是将服务端Channel注册到多路复用器上。

register(channel)源码

ChannelFuture regFuture = config().group().register(channel);

在这一行代码中,config()获取到的是ServerBootstrapConfig对象,这个对象保存了我们为Netty服务端配置的一些属性,例如设置的bossGroup、workerGroup、option、handler、childHandler等属性均被保存在ServerBootstrapConfig这个对象中。(bossGroup、workerGroup表示的是Reactor的主从线程池,也就是我们通过如下代码创建的NioEventLoopGroup)

// 负责处理连接的线程组
NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
// 负责处理IO和业务逻辑的线程组
NioEventLoopGroup workerGroup = new NioEventLoopGroup(8);

config().group()获取到的是我们为服务端设置的bossGroup。因此config().group().register(channel)实际上调用的是NioEventLoopGroup的register(channel)方法。由于NioEventLoopGroup继承了MultithreadEventLoopGroup,register(channel)定义在MultithreadEventLoopGroup类中,所以会调用MultithreadEventLoopGroup.register(channel)。(Netty中类的继承关系十分复杂,所以在看源码过程中,最好使用IDEA的debug方式去看源码,否则有时候都不知道某个方法的具体实现究竟是在哪个类中)。

MultithreadEventLoopGroup.register(channel)方法的源码如下。

public ChannelFuture register(Channel channel) {
   
    return next().register(channel);
}

NioEventLoopGroup是一个线程组,它包含了一组NioEventLoop。next()方法就是从NioEventLoopGroup这个线程组中通过轮询的方法取出一个NioEventLoop(NioEventLoop可以简单理解为它是一个线程),然后通过NioEventLoop来执行register(channel)。

当调用NioEventLoop.register(channel)时,实际上调用的是SingleThreadEventLoop类的register(channel)。SingleThreadEventLoop.register(channel)源码如下。

public ChannelFuture register(Channel channel) {
   
    return register(new DefaultChannelPromise(channel, this));
}

此时的channel为NioServerSocketChannel,this为NioEventLoop。先创建了一个ChannelPromise对象,然后将channel和NioEventLoop保存到这个ChannelPromise对象中,这是为了后面方便从ChannelPromise中获取到channel和NioEventLoop。

接着调用的是SingleThreadEventLoop中的另一个register(channelPromise)重载方法。源码如下。

public ChannelFuture register(final ChannelPromise promise) {
   
    ObjectUtil.checkNotNull(promise, "promise");
    
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值