Netty 之 AbstractChannel 和 AbstractNioChannel 源碼分析

概述

channel 是 netty 網絡 IO 操作抽象出來的一個接口,主要功能有:網絡IO的讀寫,客戶端發起連接、主動關閉連接,鏈路關閉,獲取通信雙方的網絡地址等。

這裏我們只簡單分析下 AbstractChannel 和 AbstractNioChannel 這兩個Channel源碼。

AbstractChannel 源碼

public abstract class AbstractChannel extends DefaultAttributeMap implements Channel {
    // 父 Channel(NioServerSocketChannel 是沒有父channel的)
    private final Channel parent;
    // Channel 唯一ID
    private final ChannelId id;
    // Unsafe 對象,封裝 ByteBuf 的讀寫操作
    private final Unsafe unsafe;
    // 關聯的 Pipeline 對象
    private final DefaultChannelPipeline pipeline;
    
    private final VoidChannelPromise unsafeVoidPromise = new VoidChannelPromise(this, false);
    private final CloseFuture closeFuture = new CloseFuture(this);
    // 本地地址和遠端地址
    private volatile SocketAddress localAddress;
    private volatile SocketAddress remoteAddress;
    // EventLoop 封裝的 Selector
    private volatile EventLoop eventLoop;
    // 是否註冊
    private volatile boolean registered;
    private boolean closeInitiated;

    /** Cache for the string representation of this channel */
    private boolean strValActive;
    private String strVal;

AbstractChannel 構造

protected AbstractChannel(Channel parent, ChannelId id) {
    this.parent = parent;
    this.id = id;
    unsafe = newUnsafe();
    pipeline = newChannelPipeline();
}

// Unsafe 實現交給子類實現
protected abstract AbstractUnsafe newUnsafe();

// 創建 DefaultChannelPipeline 對象
protected DefaultChannelPipeline newChannelPipeline() {
    return new DefaultChannelPipeline(this);
}

Unsafe類裏實現了具體的連接與寫數據。比如:網絡的讀,寫,鏈路關閉,發起連接等。之所以命名爲unsafe是不希望外部使用,並非是不安全的。

DefaultChannelPipeline 只是一個 Handler 的容器,也可以理解爲一個Handler鏈,具體的邏輯由Handler處理,而每個Handler都會分配一個EventLoop,最終的請求還是要EventLoop來執行,而EventLoop中又調用Channel中的內部類Unsafe對應的方法。
新建一個channel會自動創建一個ChannelPipeline。

這裏創建 DefaultChannelPipeline,構造中傳入當前的 Channel,而讀寫數據都是在 ChannelPipeline 中進行的,ChannelPipeline 進行讀寫數據又委託給 Channel 中的 Unsafe 進行操作。

AbstractUnsafe 類結構


從類結構中可以看出 Unsafe 實現了 網絡的讀,寫,連接關閉,發起連接等操作。

AbstractNioChannel 源碼

public abstract class AbstractNioChannel extends AbstractChannel {

    // 抽象了 SocketChannel 和 ServerSocketChannel 的公共的父類
    private final SelectableChannel ch;
    // SelectionKey.OP_READ 讀事件
    protected final int readInterestOp;
    // 註冊到 selector 上返回的 selectorKey
    volatile SelectionKey selectionKey;
    // 是否還有未讀的數據
    boolean readPending;
    private final Runnable clearReadPendingRunnable = new Runnable() {
        @Override
        public void run() {
            clearReadPending0();
        }
    };

    /**
     * The future of the current connection attempt.  If not null, subsequent
     * connection attempts will fail.
     */
    // 連接操作的結果
    private ChannelPromise connectPromise;
    // 連接超時定時任務
    private ScheduledFuture<?> connectTimeoutFuture;
    // 客戶端地址
    private SocketAddress requestedRemoteAddress;
   
    ....

SelectableChannel 抽象了 java.nio.SocketChannel 和 java.nio.ServerSocketChannel 的公共方法。netty 封裝了 NIO 的channel。

註冊 Channel

protected void doRegister() throws Exception {
    boolean selected = false;
    for (;;) {
        try {
            // 這裏註冊的事件爲0
            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;
            }
        }
    }
}

javaChannel() :是 Nio 中的 channel。
eventLoop().unwrappedSelector(): 是 Nio 中的 selector。

在java.nio.channels.SelectionKey 類中定義了 4種事件。

// 讀操作事件
public static final int OP_READ = 1 << 0;
// 寫操作事件
public static final int OP_WRITE = 1 << 2;
// 客戶端鏈接服務的操作事件
public static final int OP_CONNECT = 1 << 3;
// 服務端接受客戶端連接事件
public static final int OP_ACCEPT = 1 << 4;

這裏註冊的是0,說明對任何事件都不感興趣,僅僅完成註冊操作。把當前的 Channel當做附件進行註冊。如果註冊成功則返回 selectionKey,通過 selectionKey 可用從 Selector 中獲取 當前註冊的 Channel。

什麼情況下才拋出 CancelledKeyException 異常呢?

由於尚未調用select.select(..)操作,因此可能仍在緩存而未刪除“已取消”selectionkey,因此強制調用 selector.selectNow() 方法將已經取消的 selectionKey 從 selector 上刪除。

只有第一次拋出此異常,才調用 selector.selectNow() 進行取消。 如果調用 selector.selectNow() 還有取消的緩存,可能是jdk的一個bug。

@Override
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);
    }
}

該方法首先判斷 Channel 是否可用,不可用直接返回。
獲取該 selectionKey 註冊到 selector 的事件。
如果註冊的事件 位運算與 讀事件 等於0,則說明該 Channel 沒有在 selelctor 上註冊讀事件,在這裏註冊讀事件。

什麼情況下會調用 doBeginRead() 方法?

當 Channel 處於 channelActive 狀態後,就會在DefaultChannelPipeline.channelActive 方法中調用 doBeginRead() 方法,在 selector 上註冊讀事件。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章