Netty 源碼(二)NioEventLoop 之 Channel 註冊

Netty 源碼(二)NioEventLoop 之 Channel 註冊

Netty 系列目錄(http://www.javashuo.com/article/p-hskusway-em.html)html

Channel 註冊

1、Channel 註冊到 NioEventLoop

chnnel 初始化完成後就須要將其註冊到對應的 NioEventLoop 上。java

(1) NioEventLoopGroup 註冊promise

// NioEventLoopGroup -> MultithreadEventLoopGroup
public ChannelFuture register(Channel channel) {
    return next().register(channel);
}

group() 返回的是上文配置的 childGroup 對象,即 NioEventLoopGroup,每一個 NioEventLoopGroup 持有多個 NioEventLoop,next 依次返回一個 NioEventLoop。緩存

(2) NioEventLoop 註冊多線程

// NioEventLoop -> SingleThreadEventLoop
public ChannelFuture register(Channel channel) {
    return register(new DefaultChannelPromise(channel, this));
}
public ChannelFuture register(final ChannelPromise promise) {
    ObjectUtil.checkNotNull(promise, "promise");
    promise.channel().unsafe().register(this, promise);
    return promise;
}

能夠看到最終調用對應 channel 的 unsafe 進行註冊,下面看一下 unsafe 是如何註冊的。app

(3) Unsafe 註冊ide

NioServerSocketChannel 的 unsafe = new NioMessageUnsafe(),而 NioMessageUnsafe 繼承自 AbstractUnsafe。oop

// NioServerSocketChannel -> AbstractChannel.AbstractUnsafe
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
    // 省略...
    AbstractChannel.this.eventLoop = eventLoop;
    // 同一個 channel 的註冊、讀、寫等都在 eventLoop 完成,避免多線程的鎖競爭
    if (eventLoop.inEventLoop()) {
        // 將 channel 註冊到 eventLoop 上
        register0(promise);
    } else {
        try {
            eventLoop.execute(new Runnable() {
                @Override
                public void run() {
                    register0(promise);
                }
            });
        } catch (Throwable t) {
            // 省略...
        }
    }
}
private void register0(ChannelPromise promise) {
    // 1. 確保 channel 的狀態是 open,最終調用  ch.isOpen()
    if (!promise.setUncancellable() || !ensureOpen(promise)) {
        return;
    }
    boolean firstRegistration = neverRegistered;
    // 2. channel 註冊到 eventLoop 上
    doRegister();
    neverRegistered = false;
    registered = true;

    // 3. 此時 channel 已經註冊到 eventLoop 上,此時須要將註冊的 handler 綁定到 channel 上。eg: ChannelInitializer
    pipeline.invokeHandlerAddedIfNeeded();

    safeSetSuccess(promise);
    pipeline.fireChannelRegistered();
    // 4. channel 狀態爲 active 就須要觸發 ChannelActive 事件或準備讀
    if (isActive()) {
        if (firstRegistration) {
            pipeline.fireChannelActive();
        } else if (config().isAutoRead()) {
            beginRead();
        }
    }
}

register 中最重要的方法是調用 doRegister 進行註冊,該方法調用了 JDK 底層的代碼進行註冊。this

(4) doRegister線程

// AbstractNioChannel
protected void doRegister() throws Exception {
    boolean selected = false;
    for (;;) {
        try {
            // 1. 調用 JDK NIO 將 channel 註冊到 eventLoop 的 selector 上
            selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
            return;
        } catch (CancelledKeyException e) {
            if (!selected) {
                // 2. 調用 selectNow 將註冊的 channel 移除,否則有可能被緩存沒有刪除
                //    移除後繼續註冊
                eventLoop().selectNow();
                selected = true;
            } else {
                // 3. JDK 提供的文檔此時不會出現該異常,實際... JDK bug
                throw e;
            }
        }
    }
}

2、註冊感興趣的事件

AbstractNioChannel#javaChannel().register(eventLoop().unwrappedSelector(), 0, this);

register 方法註冊 Java 原生 NIO 的 Channel 對象到 Selector 對象。可是爲何感興趣事件的是 0 呢?正常狀況下,對於服務端來講,須要註冊 SelectionKey.OP_ACCEPT 事件。

(1) 註冊方式是多態的,它便可以被 NIOServerSocketChannel 用來監聽客戶端的鏈接接入,也能夠註冊 SocketChannel 用來監聽冉瑩穎讀或者寫操做。

(2) 經過 SelectionKey#interestOps(int ops) 方法能夠方便地修改監聽操做位。因此,此處註冊須要獲取 SelectionKey 並給 AbstractNIOChannel 的成員變量 selectionKey 賦值。

  • SelectionKey.OP_READ(1)
  • SelectionKey.OP_WRITE(4)
  • SelectionKey.OP_CONNECT(8)
  • SelectionKey.OP_ACCEPT(16)
// AbstractChannel.AbstractUnsafe
public final void beginRead() {
    assertEventLoop();
    if (!isActive()) {
        return;
    }

    try {
        doBeginRead();
    } catch (final Exception e) {
        invokeLater(new Runnable() {
            @Override
            public void run() {
                pipeline.fireExceptionCaught(e);
            }
        });
        close(voidPromise());
    }
}

// AbstractNioChannel
protected void doBeginRead() throws Exception {
    // Channel.read() or ChannelHandlerContext.read() was called
    final SelectionKey selectionKey = this.selectionKey;
    if (!selectionKey.isValid()) {
        return;
    }

    readPending = true;

    // 從新註冊感興趣的事件類型,readInterestOp 是 channel 初始化的時候傳進來的
    final int interestOps = selectionKey.interestOps();
    if ((interestOps & readInterestOp) == 0) {
        selectionKey.interestOps(interestOps | readInterestOp);
    }
}

到此 NioServerSocketChannel 就從新註冊上了 OP_ACCEPT 事件


天天用心記錄一點點。內容也許不重要,但習慣很重要!

相關文章
相關標籤/搜索