netty注冊流程分析二

上一篇中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事件。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容