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内部最终会掉用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;
}

从方法中可以看出调用bind方法,并不是tail,而是它的上一个出站处理器,也就是HeadContext,HeadContext的bind方法又是调用channel内部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);
}

这个方法又主要有三个步骤,绑定到服务器端口doBind,提交一个触发channelActive的任务到队列中,promise的result设置为成功,通知监听器。

触发channel的channelActive方法,内部会调用HeadContext的channelActive

@Override
public void channelActive(ChannelHandlerContext ctx) {
    ctx.fireChannelActive();

    readIfIsAutoRead();
}

HeadContext直接转发给下一个inboundHandler的channelActive,再调用readIfIsAutoRead

private void readIfIsAutoRead() {
    if (channel.config().isAutoRead()) {
        channel.read();
    }
}

而看到channel.read方法,程序又会执行太Context的read方法,参考channel、handler调用关系,而TailContext最终又是调用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的监听事件注册为accept。当NioEventLoop执行完队列所有的任务之后,在事件循环开始的地方又进入Selector.select的阻塞,监听客户端的连接。

最后总结一下,netty直接调用channel的相关方法时,会先转给pipeline,再由pipeline根据入站事件还是出站事件,转给HeadContext和TailContext,而TailContext在处理出站事件时,最终又回到channel的unsafe对象去执行相应的IO方法。

至此,netty只是将channel注册到selector上,且监听accept事件。下篇再来讲述下NioEventLoop的事件循环,及如何将新的连接channel注册到worker group上去监听read事件。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容