扫描下方二维码或者微信搜索公众号
菜鸟飞呀飞
,即可关注微信公众号,阅读更多Spring源码分析
和Java并发编程
文章。
问题
- 在JDK的原生NIO的写法中,通过
serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT)
将服务端channel注册到多路复用器selector上,那么在Netty中,又是如何将NioServerSocketChannel注册到多路复用器上的呢?在注册过程中,Netty又额外做了哪些事情呢? - 在上一篇文章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")