netty服务端源码分析之server channel注册

前面我们分析server channel注册时,知道将channel的unsaferegister0(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的绑定过程。

你可能感兴趣的:(netty服务端源码分析之server channel注册)