我是怎樣閱讀 Netty Channel 源碼的

Channel 功能說明

我在使用 ServerBootstrap 來創建服務的時候通過 channel(NioServerSocketChannel.class) 來設置 Channel, 那麼 Channel 的主要作用是什麼呢?

在分析 Channel 之前, 需要先弄懂 NioServerSocketChannel 類, 它的功能對於 JDK NIO 類庫中的
ServerSocketChannel 類, 它是用來監聽TCP連接的(並不管讀寫).

NioServerSocketChannel 最主要就是實現 Channel 接口. Channel 接口, 採用 Facade 模式進行統一封裝, 將網絡的讀、寫, 客戶端發起連接, 主動關閉連接, 鏈路關閉, 獲取通信雙方的網絡地址等.

Channel 接口下有一個重要的抽象類 AbstractChannel, 一些公共的基礎方法都在這個抽象類中實現, 而特定一些功能, 都可以通過各個不同的實現類去實現, 這樣最大程度的實現功能和接口的重用.

爲什麼不實用 JDK NIO 原生的 Channel 而要另起爐竈呢?

  1. JDK SocketChannelServerSocketChannel 沒有統一的 Channel.
  2. JDK 的 SocketChannelServerSocketChannel 的主要職責就是網絡 IO 操作, 由於他們的 SPI 類接口, 由具體的虛擬機廠家提供, 所以通過繼承 SPI 功能類來擴展其功能的難度很大; SocketChannelServerSocketChannel 抽象類, 其工作量和重新開發一個新的 Channel 功能類是差不多的.
  3. Netty 的 Channel 需要能夠跟 Netty 的整體架構融合在一起, 例如 IO 模型、基於 ChannelPipeline 的定製模型, 以及基於元數據描述配置化的 TCP 參數等, 這些 JDK 的 SocketChannelServerSocketChannel 都沒有提供, 需要重新封裝.
  4. 自定義的 Channel, 功能實現更加靈活.

Channel 方法介紹

clipboard.png

Channel read(): 從當前的 Channel 中讀取數據到第一個 inbound 緩衝區中, 如果數據被成功讀取, 觸發 ChannelHandler.channelRead(ChannelHandlerContext, Object) 事件.

讀取操作 API 調用完成之後, 緊接着會觸發 ChannelHandler.channelReadComplete(ChannelHandlerContext) 事件, 這樣業務的 ChannelHandler 可以決定是否需要繼承讀取數據. 如果已經有讀操作請求被掛起, 則後續的讀操作會被忽略.

ChannelFuture write(Object): 請求將當前的 msg 通過 ChannelPipeline 寫入到目標 Channel 中. 注意, write 操作只是將消息存入到消息發送環數組中, 並沒有真正被髮送, 只有調用 flush 操作纔會被寫入到 Channel 中, 發送給對方.

ChannelFuture write(Object, ChannelPromise): 功能與 Channel read() 相同, 但是攜帶了 ChannelPromise 參數負責設置寫入操作的結果.

ChannelFuture writeAndFlush(Objec, ChannelPromise): 與上面方法功能類似, 不同之處在於它會將消息寫入 Channel 中發送, 等價於單獨調用 writeflush 操作的組合.

Channel flush(): 將之前寫入到發送環形中的消息全部寫入到目標 Channel 中, 發送給通信對方.

ChannelFuture close(ChannelPromise): 主動關閉當前連接, 通過 ChannelPromise 設置操作結果並執行結果通知, 無論操作是否成功, 都可以通過 ChannelPromise 獲取操作結果. 該操作會級聯觸發 ChannelPipeline 中所有 ChannelHandlerChannelHandler.close(ChannelHandlerContext, ChannelPromise) 事件.

ChannelFuture disconnect(ChannelPromise): 請求斷開與遠程通信對端的連接並使用 ChannelPromise 來獲取操作結果的通知消息. 該方法會級聯觸發 ChannelHandler.disconnect(ChannelHandlerContext, ChannelPromise) 事件.

ChannelFuture connect(SocketAddress remoteAddress): 客戶端使用指定的服務端地址 remoteAddress 發起連接請求, 如果連接因爲應答超時而失敗, ChannelFuture 中的操作結果就是 ConnectTimeoutException 異常; 如果連接被拒絕, 操作結果爲 ConnectException. 該方法會級聯觸發 ChannelHandler.connect(ChannelHandlerContext, SocketAddress, SocketAddress, ChannelPromise) 事件.

ChannelFuture bind(SocketAddress localAddress): 綁定指定的本地 Socket 地址 localAddress, 該方法會級聯觸發 ChannelHandler.bind(ChannelHandlerContext, SocketAddress, ChannelPromise) 事件.

ChannelConfig config(): 獲取當前 Channel 的配置信息, 例如 CONNECT_TIMEOUT_MILLIS.

boolean isOpen(): 判斷當前 Channel 是否已經打開.

boolean isRegistered(): 判斷當前 Channel 是否已經註冊到 EventLoop 上.

boolean isActive(): 判斷當前 Channel 是否已經處於激活狀態.

ChannelMetadata metadata(): 獲取當前 Channel 的元數據描述信息, 包括 TCP 參數配置等.

SocketAddress localAddress(): 獲取當前 Channel 的本地綁定地址.

SocketAddress remoteAddress(): 獲取當前 Channel 通信的遠程 Socket 地址.

eventLoop(): Channel 需要註冊到 EventLoop 的多路複用器上, 用於處理 IO 事件, 通過 eventLoop() 方法可以獲取到 Channel 上註冊的 EventLoop. EventLoop 本質上就是處理網絡讀寫事件的 Reactor 線程. 也可以用來執行定時任務和用戶自定義 NioTask 等任務.

id(): 它返回 ChannelId 對象, 是 Channel 的唯一標識, 它的可能生成策略如下:

  1. 機器的 MAC 地址(EUI-48 或 EUI-64) 等可以代表全局唯一的信息;
  2. 當前的進程 ID;
  3. 當前系統的毫秒-- System.currentTimeMillis();
  4. 當前系統的納秒-- System.nanoTime();
  5. 32 位的隨機整數;
  6. 32 位自增的序列數.

AbstractChannel 源碼分析

AstractChannel 聚合了所有 Channel 使用到的對象, 由 AbstractChannel 提供初始化和統一封裝, 如果功能和具體子類相關, 則定義成抽象方法由子類具體實現.

static final ClosedChannelException CLOSED_CHANNEL_EXCEPTION = new ClosedChannelException();
static final NotYetConnectedException NOT_YET_CONNECTED_EXCEPTION = new NotYetConnectedException();

static {
    CLOSED_CHANNEL_EXCEPTION.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE);
    NOT_YET_CONNECTED_EXCEPTION.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE);
}

private MessageSizeEstimator.Handle estimatorHandle;

private final Channel parent;
private final ChannelId id = DefaultChannelId.newInstance();
private final Unsafe unsafe;
private final DefaultChannelPipeline pipeline;
private final ChannelFuture succeededFuture = new SucceededChannelFuture(this, null);
private final VoidChannelPromise voidPromise = new VoidChannelPromise(this, true);
private final VoidChannelPromise unsafeVoidPromise = new VoidChannelPromise(this, false);
private final CloseFuture closeFuture = new CloseFuture(this);

....
protected AbstractChannel(Channel parent, EventLoop eventLoop) {
    this.parent = parent;
    this.eventLoop = validate(eventLoop);
    unsafe = newUnsafe();
    pipeline = new DefaultChannelPipeline(this);
}

每一個 Channel 中包含一個 ChannelPipeline. 在 AbstractChannel 的構造方法中初始化. AbstractChannel 也提供了一些公共 API 的具體實現, 例如 localAddress()remoteAddress().

當 Channel 進行 IO 操作時會產生對應的 IO 事件, 然後驅動事件在 ChannelPipeline 中傳播, 由對應的 ChannelHandler 對事件進行攔截和處理, 不關心的事件可以直接忽略.

網絡 IO 操作直接調用 DefaultChannelPipeline 的相關方法, 由 DefaultChannelPipeline 中對應的 ChannelHandler 進行具體邏輯的處理.

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