NOTE

2.91 3.注册channel

1. 要分析的代码 2. 选择一个NioEventLoop - config()返回ServerBootstrap - Serverbootstrap的group()返回EventLoopGroup,EventLoopGroup继承了MultithreadEventLoopGroup,所以调用的是M

Java创建于 更新于 historical

这是历史学习笔记,可能存在过时或不完整的理解。

1. 要分析的代码

ChannelFuture regFuture = config().group().register(channel)

2. 选择一个NioEventLoop

  • config()返回ServerBootstrap
  • Serverbootstrap的group()返回EventLoopGroup,EventLoopGroup继承了MultithreadEventLoopGroup,所以调用的是MultithreadEventLoopGroup的register方法
  • MultithreadEventLoopGroup#register
public ChannelFuture register(Channel channel) {
    return next().register(channel);
}

next()会调用到MultithreadEventExecutorGroup#next

public EventExecutor next() {
    //这个chooser要么是PowerOfTwoEventExecutorChooser,要么是GenericEventExecutorChooser
    return chooser.next();
}

chooser的next方法就是选择一个NioEventLoop选择一个NioEventLoop调用他的register方法 最终调用到SingleThreadEventLoop#register

3. 创建一个Future

  • SingleThreadEventLoop#register
public ChannelFuture register(Channel channel) {
    return register(new DefaultChannelPromise(channel, this));
}

把Channel和EventLoopGroup传入DefaultChannelPromise构造,先看看DefaultChannelPromise

3.1. DefaultChannelFuture类体系

JDK的Future提供了获取异步任务结果的接口

Future
	cancel
	isCancelled
	isDone
	get
	get

而Netty的Future除了以上这些外,还提供了sync() 和 await() 用于阻塞等待, Listeners用于任务结束时回调【相比于JDK的Future的主动轮询,Netty的属于异步回调】 ChannelFuture继承了Netty的Future,则是将Channel【IO操作】和Future绑定在一起 Promise继承了Netty的Future,提供了setSucess和setFailure方法用于唤醒sync和await的线程 ChannelPromise则结合了ChannelFuture和Promise 最底层的的DefaultChannelPromise就是ChannelPromise默认的实现类

如上述我们有两种编程方式,

  • 同步阻塞
    • 一种时sync/await阻塞等待,直至有线程调用setSuccess/setFailure通知
  • 异步回调
    • 另一种时绑定listener实例,有结果时异步通知

4. 调用Unsafe进行register

将创建完的DefaultChannelPromise传入register

public ChannelFuture register(final ChannelPromise promise) {
    ObjectUtil.checkNotNull(promise, "promise");
    //AbstractUnsafe#register把当前channel关联到eventLoop里面
    promise.channel().unsafe().register(this, promise);
    return promise;
}
  • AbstractUnsafe
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
    if (eventLoop == null) {
        throw new NullPointerException("eventLoop");
    }
    if (isRegistered()) {
        promise.setFailure(new IllegalStateException("registered to an event loop already"));
        return;
    }
    if (!isCompatible(eventLoop)) {
        promise.setFailure(
                new IllegalStateException("incompatible event loop type: " + eventLoop.getClass().getName()));
        return;
    }

	//终于把NioEventLoop关联到channel里了
    AbstractChannel.this.eventLoop = eventLoop;

	//如果当前线程是在EventLoop中的,那么直接注册
    if (eventLoop.inEventLoop()) {
        register0(promise);
    } else {
    	//否则做成Runnable实例丢进SingleThreadEventExecutor#execute
        try {
            eventLoop.execute(new Runnable() {
                @Override
                public void run() {
                    register0(promise);
                }
            });
        } catch (Throwable t) {
            logger.warn(
                    "Force-closing a channel whose registration task was not accepted by an event loop: {}",
                    AbstractChannel.this, t);
            closeForcibly();
            closeFuture.setClosed();
            safeSetFailure(promise, t);
        }
    }
}

4.1. 把channel关联到NioEventLoop中

AbstractChannel.this.eventLoop = eventLoop;

4.2. 实际注册

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;
        //真的要注册了
        doRegister();
        neverRegistered = false;
        registered = true;


        //调用handler的handlerAdded方法
        pipeline.invokeHandlerAddedIfNeeded();

        safeSetSuccess(promise);
    	//调用Handler的channelRegistered方法
        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.
        //这里会返回false,只有在bind的时候才会返回true
        if (isActive()) {
            if (firstRegistration) {
            	//所以不会调用handler的channelActive方法
                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);
    }
}

关键的两步,一是注册,二是调用handlerAdded方法

4.2.1. 将channel注册到jdk nio selector上

protected void doRegister() throws Exception {
    boolean selected = false;
    for (;;) {
        try {
        	//调用jdk底层的channel注册
        	//eventLoop().unwrappedSelector()是jdk底层的selector
        	//0表示不关心任何事件
        	//this是当前的AbstractNioChannel作为jdk底层selector的attachment
            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;
            }
        }
    }
}

4.2.2. 调用handlerAdded方法

final void invokeHandlerAddedIfNeeded() {
    assert channel.eventLoop().inEventLoop();
    if (firstRegistration) {
        firstRegistration = false;
        // We are now registered to the EventLoop. It's time to call the callbacks for the ChannelHandlers,
        // that were added before the registration was done.
        callHandlerAddedForAllHandlers();
    }
}


private void callHandlerAddedForAllHandlers() {
    final PendingHandlerCallback pendingHandlerCallbackHead;
    synchronized (this) {
        assert !registered;

        // This Channel itself was registered.
        registered = true;

        pendingHandlerCallbackHead = this.pendingHandlerCallbackHead;
        // Null out so it can be GC'ed.
        this.pendingHandlerCallbackHead = null;
    }

    // This must happen outside of the synchronized(...) block as otherwise handlerAdded(...) may be called while
    // holding the lock and so produce a deadlock if handlerAdded(...) will try to add another handler from outside
    // the EventLoop.
    PendingHandlerCallback task = pendingHandlerCallbackHead;
	//这里的task中其中有一个是io.netty.channel.DefaultChannelPipeline.PendingHandlerAddedTask#execute
    while (task != null) {
        task.execute();
        task = task.next;
    }
}
  • DefaultChannelPipeline.PendingHandlerAddedTask#execute
void execute() {
        EventExecutor executor = ctx.executor();
        if (executor.inEventLoop()) {
        	//调用到io.netty.channel.DefaultChannelPipeline#callHandlerAdded0
            callHandlerAdded0(ctx);
        } else {
            try {
                executor.execute(this);
            } catch (RejectedExecutionException e) {
                if (logger.isWarnEnabled()) {
                    logger.warn(
                            "Can't invoke handlerAdded() as the EventExecutor {} rejected it, removing handler {}.",
                            executor, ctx.name(), e);
                }
                remove0(ctx);
                ctx.setRemoved();
            }
        }
    }
}
  • DefaultChannelPipeline#callHandlerAdded0
private void callHandlerAdded0(final AbstractChannelHandlerContext ctx) {
    try {
        // We must call setAddComplete before calling handlerAdded. Otherwise if the handlerAdded method generates
        // any pipeline events ctx.handler() will miss them because the state will not allow it.
        ctx.setAddComplete();
    	//调用io.netty.channel.ChannelInitializer#handlerAdded
    	//都是从pipeline开始传播
        ctx.handler().handlerAdded(ctx);
    } catch (Throwable t) {
        boolean removed = false;
        try {
            remove0(ctx);
            try {
                ctx.handler().handlerRemoved(ctx);
            } finally {
                ctx.setRemoved();
            }
            removed = true;
        } catch (Throwable t2) {
            if (logger.isWarnEnabled()) {
                logger.warn("Failed to remove a handler: " + ctx.name(), t2);
            }
        }

        if (removed) {
            fireExceptionCaught(new ChannelPipelineException(
                    ctx.handler().getClass().getName() +
                    ".handlerAdded() has thrown an exception; removed.", t));
        } else {
            fireExceptionCaught(new ChannelPipelineException(
                    ctx.handler().getClass().getName() +
                    ".handlerAdded() has thrown an exception; also failed to remove.", t));
        }
    }
}

ChannelInitializer#handlerAdded

public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
    if (ctx.channel().isRegistered()) {
        //调用io.netty.channel.ChannelInitializer#initChannel(io.netty.channel.ChannelHandlerContext)
        initChannel(ctx);
    }
}
4.2.2.1. 回调ChannelInitializer#initChannel
  • ChannelInitializer#initChannel
private boolean initChannel(ChannelHandlerContext ctx) throws Exception {
    if (initMap.putIfAbsent(ctx, Boolean.TRUE) == null) { // Guard against re-entrance.
        try {
        	//终于回调到io.netty.channel.ChannelInitializer#initChannel
            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;
}

ChannelInitializer是什么时候加进去的? 见2.初始化channel.md