上一篇中netty注冊流程分析一,分析到了channel注冊到nioEventLoop中了,接下來就是doBind0方法
private static void doBind0(
final ChannelFuture regFuture, final Channel channel,
final SocketAddress localAddress, final ChannelPromise promise) {
channel.eventLoop().execute(new Runnable() {
@Override
public void run() {
if (regFuture.isSuccess()) {
channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
} else {
promise.setFailure(regFuture.cause());
}
}
});
}
通過channel.bind內(nèi)部最終會掉用pipeline上TailContext的bind方法,這個看下這篇文章
public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) {
ObjectUtil.checkNotNull(localAddress, "localAddress");
if (isNotValidPromise(promise, false)) {
// cancelled
return promise;
}
final AbstractChannelHandlerContext next = findContextOutbound(MASK_BIND);
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeBind(localAddress, promise);
} else {
safeExecute(executor, new Runnable() {
@Override
public void run() {
next.invokeBind(localAddress, promise);
}
}, promise, null, false);
}
return promise;
}
從方法中可以看出調(diào)用bind方法,并不是tail,而是它的上一個出站處理器,也就是HeadContext,HeadContext的bind方法又是調(diào)用channel內(nèi)部unsafe的bind方法。
@Override
public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
//省略....
assertEventLoop();
boolean wasActive = isActive();
try {
doBind(localAddress);
} catch (Throwable t) {
safeSetFailure(promise, t);
closeIfClosed();
return;
}
if (!wasActive && isActive()) {
invokeLater(new Runnable() {
@Override
public void run() {
pipeline.fireChannelActive();
}
});
}
safeSetSuccess(promise);
}
這個方法又主要有三個步驟,綁定到服務(wù)器端口doBind,提交一個觸發(fā)channelActive的任務(wù)到隊列中,promise的result設(shè)置為成功,通知監(jiān)聽器。
觸發(fā)channel的channelActive方法,內(nèi)部會調(diào)用HeadContext的channelActive
@Override
public void channelActive(ChannelHandlerContext ctx) {
ctx.fireChannelActive();
readIfIsAutoRead();
}
HeadContext直接轉(zhuǎn)發(fā)給下一個inboundHandler的channelActive,再調(diào)用readIfIsAutoRead
private void readIfIsAutoRead() {
if (channel.config().isAutoRead()) {
channel.read();
}
}
而看到channel.read方法,程序又會執(zhí)行太Context的read方法,參考channel、handler調(diào)用關(guān)系,而TailContext最終又是調(diào)用channel中Unsafe的beginRead方法
protected void doBeginRead() throws Exception {
// Channel.read() or ChannelHandlerContext.read() was called
final SelectionKey selectionKey = this.selectionKey;
if (!selectionKey.isValid()) {
return;
}
readPending = true;
final int interestOps = selectionKey.interestOps();
if ((interestOps & readInterestOp) == 0) {
selectionKey.interestOps(interestOps | readInterestOp);
}
}
直到這邊,netty才將NioServerSocketChannel注冊到Selector的監(jiān)聽事件注冊為accept。當(dāng)NioEventLoop執(zhí)行完隊列所有的任務(wù)之后,在事件循環(huán)開始的地方又進(jìn)入Selector.select的阻塞,監(jiān)聽客戶端的連接。
最后總結(jié)一下,netty直接調(diào)用channel的相關(guān)方法時,會先轉(zhuǎn)給pipeline,再由pipeline根據(jù)入站事件還是出站事件,轉(zhuǎn)給HeadContext和TailContext,而TailContext在處理出站事件時,最終又回到channel的unsafe對象去執(zhí)行相應(yīng)的IO方法。
至此,netty只是將channel注冊到selector上,且監(jiān)聽accept事件。下篇再來講述下NioEventLoop的事件循環(huán),及如何將新的連接channel注冊到worker group上去監(jiān)聽read事件。