Channel的register流程
学习目标
- 掌握Channel的register的本质
- 掌握Channel的register流程
Channel的register的本质
本质就是将channel注册到线程NioEventLoop中的Selector上,让Selector帮我们监听感兴趣的IO事件(accept,read,write,connect)。如图所示:
Channel的register流程
Channel的register流程分为 启动时Channel的register流程 和 新连接Channel的register流程 两大部分内容。
启动时Channel的register流程
研究server和client端的register流程代码入口分别为:ServerBootstrap#bind方法和Bootstrap#connect方法。无论server端还是client端入,它们最终都会调到AbstractBootstrap#initAndRegister方法,我们直接看这个方法。
// AbstractBootstrap 类
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
// 创建一个NioServerSocketChannel实例
channel = channelFactory.newChannel();
// 初始化channel配置
init(channel);
} catch (Throwable t) {
......
}
// 服务端channel注册到bossGroup线程池,由bossGroup线程池来处理accept事件
ChannelFuture regFuture = config().group().register(channel);
......
}
从config().group().register(channel);这行代码开始register流程,这个register方法调用的就是NioEventLoopGroup父类MultithreadEventLoopGroup的register方法。
// MultithreadEventLoopGroup 类
public ChannelFuture register(Channel channel) {
return next().register(channel);
}
// next()方法
public EventLoop next() {
return (EventLoop) super.next();
}
其中的next()方法是通过选择器chooser选择一个线程NioEventLoop,调用该线程的register方法,把channel注册到上去,让我们来看看该线程的register方法,其实调用的是父类SingleThreadEventLoop的register方法。
// SingleThreadEventLoop类
public ChannelFuture register(Channel channel) {
return register(new DefaultChannelPromise(channel, this));
}
public ChannelFuture register(final ChannelPromise promise) {
ObjectUtil.checkNotNull(promise, "promise");
// 调用AbstractChannel的register方法,当前线程NioEventLoop作为参数,传递过去了
promise.channel().unsafe().register(this, promise);
return promise;
}
接下来,我们在看下AbstractChannel的register方法:
// AbstractChannel类
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
......
AbstractChannel.this.eventLoop = eventLoop;
// 判断当前线程和EventLoop中的线程是不是同一个线程。
// 启动时,当前线程是main主线程,EventLoop中的线程为null,不是同一个线程
if (eventLoop.inEventLoop()) {
register0(promise);
} else {
try {
// 把register当成一个任务,提交给线程池eventLoop执行
eventLoop.execute(new Runnable() {
@Override
public void run() {
register0(promise);
}
});
} catch (Throwable t) {
......
}
}
}
由于当前线程和EventLoop不是同一个线程,随后把register操作当成一个任务,提交给线程池eventLoop了,eventLoop是个单线程的线程池。顺便来看下execute方法吧。
// SingleThreadEventExecutor类
private void execute(Runnable task, boolean immediate) {
boolean inEventLoop = inEventLoop();
// 添加到队列中
addTask(task);
// 启动时,肯定不是同一个线程
if (!inEventLoop) {
// 启动一个线程执行
startThread();
......
}
......
}
private void doStartThread() {
......
// executor是ThreadPerTaskExecutor,在创建NioEventLoop时传的进来的
executor.execute(new Runnable() {
@Override
public void run() {
// 设置当前线程
thread = Thread.currentThread();
try {
// 执行run方法,调用的是NioEventLoop的run方法,
SingleThreadEventExecutor.this.run();
success = true;
} catch (Throwable t) {
logger.warn("Unexpected exception from an event executor: ", t);
}
});
}
我们可以了解到NioEventLoop创建时,并没有立即启动线程执行,进入execute方法,就是想告诉大家这个,让我们在回到register0方法。
// AbstractChannel 类
private void register0(ChannelPromise promise) {
......
// 调用AbstractNioChannel#doRegister方法
doRegister();
......
}
// AbstractNioChannel类
protected void doRegister() throws Exception {
boolean selected = false;
for (;;) {
try {
// channel注册到selector,但是监听事件为0。
// 对server来说,监听事件应该是ACCEPT
// 对client来说,监听事件应该是READ
selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
return;
} catch (CancelledKeyException e) {
}
}
在这篇文章《Netty源码-Channel讲解》中讲到创建channel时,ACCEPT/READ事件以参数形式传递给父类AbstractNioChannel的readInterestOp成员变量,什么时候把ACCEPT/READ事件绑定到Selector上呢?不再展开细说了,最终会在AbstractNioChannel的doBeginRead方法中绑定到Selector上,有兴趣的同学,可以按照下面的流程打断点跟踪下。
AbstractBootstrap#doBind0-->....-->DefaultChannelPipeline#bind-->...
--> DefaultChannelPipeline.HeadContext#bind
--> AbstractChannel#bind(){...pipeline.fireChannelActive()...}
--> DefaultChannelPipeline.HeadContext#channelActive(){...readIfIsAutoRead()...}--> ....
---> DefaultChannelPipeline.HeadContext#read
--> AbstractNioChannel#doBeginRead(){...selectionKey.interestOps(interestOps | readInterestOp);}
Channel的register流程源码先分析到这,本篇只研究了启动时Channel的register流程,还剩下新连接Channel的register流程没有分析,等研究完线程EventLoop后,在续上剩下的部分。
总结
- register的本质是channel注册到selector上,而不是EventLoop上,selector帮忙监听ACCEPT/READ事件。
- 线程EventLoop创建时,没有立即运行,而是在第一个任务提交时运行的。