前面我们分析server channel注册时,知道将channel的unsafe
的register0(promise)
函数添加到了相应eventloop的taskqueue
中,随后在eventloop的run()
中异步执行。
这一节我们将详细分析channel注册具体是如何实现的,register0(promise)
函数的代码如下:
private void register0(ChannelPromise promise) {
try {
// check if the channel is still open as it could be closed in the mean time when the register
// call was outside of the eventLoop
if (!promise.setUncancellable() || !ensureOpen(promise)) {
return;
}
boolean firstRegistration = neverRegistered;
// 调用的函数 AbstractNioChannel.doRegister()
doRegister();
neverRegistered = false;
registered = true;
// Ensure we call handlerAdded(...) before we actually notify the promise. This is needed as the
// user may already fire events through the pipeline in the ChannelFutureListener.
pipeline.invokeHandlerAddedIfNeeded();
safeSetSuccess(promise);
pipeline.fireChannelRegistered();
// Only fire a channelActive if the channel has never been registered. This prevents firing
// multiple channel actives if the channel is deregistered and re-registered.
if (isActive()) {
if (firstRegistration) {
pipeline.fireChannelActive();
} else if (config().isAutoRead()) {
// This channel was registered before and autoRead() is set. This means we need to begin read
// again so that we process inbound data.
//
// See https://github.com/netty/netty/issues/4805
beginRead();
}
}
} catch (Throwable t) {
// Close the channel directly to avoid FD leak.
closeForcibly();
closeFuture.setClosed();
safeSetFailure(promise, t);
}
}
从代码可知,register0()
先调用了doRegister()
函数,doRegister()
在AbstractNioChannel
中实现,我们看下函数的具体代码:
protected void doRegister() throws Exception {
boolean selected = false;
for (;;) {
try {
selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
return;
} catch (CancelledKeyException e) {
if (!selected) {
// Force the Selector to select now as the "canceled" SelectionKey may still be
// cached and not removed because no Select.select(..) operation was called yet.
eventLoop().selectNow();
selected = true;
} else {
// We forced a select operation on the selector before but the SelectionKey is still cached
// for whatever reason. JDK bug ?
throw e;
}
}
}
}
原来doRegister()
通过selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
调用了JDK的相关函数将channel注册到了eventloop的selector。注册时传入的两个参数我们需要特别注意,一个是ops参数值为0,表示注册时没有添加任何需要监听的事件,后面需要通过selectionKey
添加监听的OP_ACCEPT事件,第二个是参数att传入的是当前channel,eventloop在处理监听到的事件时,会根据selectionKey attachment的对象获取事件对应的channel。
channel注册完后,将registered
设置为true,表示channel已经注册了。
继续往下走,然后遇到这行代码:
pipeline.invokeHandlerAddedIfNeeded();
我们分析把channel handler添加到pipeline中时,由于channel还没有注册,所以将一个任务添加到了pendingHandlerCallbackHead
链表中,这个函数会将registered
标志设为true,同时在eventloop中执行pendingHandlerCallbackHead
链表中的所有任务:
PendingHandlerCallback task = pendingHandlerCallbackHead;
while (task != null) {
task.execute();
task = task.next;
}
上面task
对应的实现类为PendingHandlerAddedTask
,它的execute()
函数会调用函数callHandlerAdded0()
,这个函数的主要内容为:
ctx.handler().handlerAdded(ctx);
ctx.setAddComplete();
前面context添加到pipeline时,状态为ADD_PENDING
,这里的ctx.setAddComplete()
会把context状态设置为ADD_COMPLETE
。我们再回头分析ctx.handler().handlerAdded(ctx)
。
为了便于分析,我们把添加的handler的内容重新贴一下:
// 为 ServerSocketChannel 的pipeline添加handler
p.addLast(new ChannelInitializer() {
@Override
public void initChannel(final Channel ch) throws Exception {
final ChannelPipeline pipeline = ch.pipeline();
// 通过ServerBootstarp配置的handler添加到pipeline
ChannelHandler handler = config.handler();
if (handler != null) {
pipeline.addLast(handler);
}
// 将handler ServerBootstrapAcceptor添加到pipeline
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
可见,handler是ChannelInitializer
的实例,跟踪它的handlerAdded(ctx)
函数
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
if (ctx.channel().isRegistered()) {
initChannel(ctx);
}
}
private boolean initChannel(ChannelHandlerContext ctx) throws Exception {
if (initMap.putIfAbsent(ctx, Boolean.TRUE) == null) { // Guard against re-entrance.
try {
initChannel((C) ctx.channel());
} catch (Throwable cause) {
// Explicitly call exceptionCaught(...) as we removed the handler before calling initChannel(...).
// We do so to prevent multiple calls to initChannel(...).
exceptionCaught(ctx, cause);
} finally {
remove(ctx);
}
return true;
}
return false;
}
因为channel已经注册了,所以执行initChannel(ctx)
,如果当前context还没有添加到initMap中,则执行handler的initChannel(ch)函数,即把启动辅助类的b.handler()
添加的handler及ServerBootstrapAcceptor
的handler实例添加到pipeline中,然后调用remove(ctx)
从pipeline删除旧的context。
回到register0()
继续往下分析:
safeSetSuccess(promise);
设置channelFuture,表示完成了注册,main()
函数的b.bind(PORT).sync()
调用可以返回了。
继续往下, :
pipeline.fireChannelRegistered();
注册完后,触发fireChannelRegistered
,它的调用链如下代码,可以看代码注释,了解处理过程
// DefaultChannelPipeline
public final ChannelPipeline fireChannelRegistered() {
AbstractChannelHandlerContext.invokeChannelRegistered(head);
return this;
}
// AbstractChannelHandlerContext
static void invokeChannelRegistered(final AbstractChannelHandlerContext next) {
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeChannelRegistered();
} else {
executor.execute(new Runnable() {
@Override
public void run() {
next.invokeChannelRegistered();
}
});
}
}
// HeadContext
private void invokeChannelRegistered() {
if (invokeHandler()) {
try {
// 返回的context的channel handler,对于head来说,因为head本身实现了channelhandler接口,返回的就是head自己
((ChannelInboundHandler) handler()).channelRegistered(this);
} catch (Throwable t) {
notifyHandlerException(t);
}
} else {
fireChannelRegistered();
}
}
// 调用channel handler的相应函数
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
// 实际干活的函数
invokeHandlerAddedIfNeeded();
// 执行下一个context的相应函数
ctx.fireChannelRegistered();
}
public ChannelHandlerContext fireChannelRegistered() {
invokeChannelRegistered(findContextInbound());
return this;
}
private AbstractChannelHandlerContext findContextInbound() {
AbstractChannelHandlerContext ctx = this;
do {
ctx = ctx.next;
} while (!ctx.inbound);
return ctx;
}
register完成事件的pipeline调用链传递过程如上面的代码注释所示。这里可以总结一下pipleline的任务链传递和处理模式(XXX表示register、bind等具体事件):
pipeline.fireXXX -> headContext.invokeXXX -> channelHandler.XXX -> next Context.fireXXX -> next Context.invokeXXX -> next channelHandler.XXX
注册完成事件处理完成之后,我们继续回到register0()
,
继续看后面的代码:
if (isActive()) {
if (firstRegistration) {
pipeline.fireChannelActive();
} else if (config().isAutoRead()) {
// This channel was registered before and autoRead() is set. This means we need to begin read
// again so that we process inbound data.
//
// See https://github.com/netty/netty/issues/4805
beginRead();
}
}
从代码调用来看,应该是如果channel已经是active状态,而且是第一次注册,那么触发fireChannelActive事件的处理链,如果已经不是第一次注册,又设置了自动读的功能,则执行beginRead()
但是跟踪代码可知,isActive()
在NioServerSocketChannel
中实现
public boolean isActive() {
return javaChannel().socket().isBound();
}
到此为止,因为我们还没有为channel socket执行绑定动作,所有if(isActive())
分支中的内容并不会发生。可以预期channel bind完后会执行if分支中相应的功能,那么我们继续分析channel的绑定过程。