Channel 功能說明
我在使用 ServerBootstrap
來創建服務的時候通過 channel(NioServerSocketChannel.class)
來設置 Channel
, 那麼 Channel
的主要作用是什麼呢?
在分析 Channel
之前, 需要先弄懂 NioServerSocketChannel
類, 它的功能對於 JDK NIO 類庫中的 ServerSocketChannel
類, 它是用來監聽TCP連接的(並不管讀寫).
而 NioServerSocketChannel
最主要就是實現 Channel
接口. Channel
接口, 採用 Facade
模式進行統一封裝, 將網絡的讀、寫, 客戶端發起連接, 主動關閉連接, 鏈路關閉, 獲取通信雙方的網絡地址等.
Channel
接口下有一個重要的抽象類 AbstractChannel
, 一些公共的基礎方法都在這個抽象類中實現, 而特定一些功能, 都可以通過各個不同的實現類去實現, 這樣最大程度的實現功能和接口的重用.
爲什麼不實用 JDK NIO 原生的 Channel
而要另起爐竈呢?
- JDK
SocketChannel
和ServerSocketChannel
沒有統一的Channel
. - JDK 的
SocketChannel
和ServerSocketChannel
的主要職責就是網絡 IO 操作, 由於他們的 SPI 類接口, 由具體的虛擬機廠家提供, 所以通過繼承 SPI 功能類來擴展其功能的難度很大;SocketChannel
和ServerSocketChannel
抽象類, 其工作量和重新開發一個新的Channel
功能類是差不多的. - Netty 的
Channel
需要能夠跟 Netty 的整體架構融合在一起, 例如 IO 模型、基於ChannelPipeline
的定製模型, 以及基於元數據描述配置化的 TCP 參數等, 這些 JDK 的SocketChannel
和ServerSocketChannel
都沒有提供, 需要重新封裝. - 自定義的
Channel
, 功能實現更加靈活.
Channel 方法介紹
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
中發送, 等價於單獨調用 write
和 flush
操作的組合.
Channel flush()
: 將之前寫入到發送環形中的消息全部寫入到目標 Channel
中, 發送給通信對方.
ChannelFuture close(ChannelPromise)
: 主動關閉當前連接, 通過 ChannelPromise
設置操作結果並執行結果通知, 無論操作是否成功, 都可以通過 ChannelPromise
獲取操作結果. 該操作會級聯觸發 ChannelPipeline
中所有 ChannelHandler
的 ChannelHandler.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 的唯一標識, 它的可能生成策略如下:
- 機器的 MAC 地址(EUI-48 或 EUI-64) 等可以代表全局唯一的信息;
- 當前的進程 ID;
- 當前系統的毫秒-- System.currentTimeMillis();
- 當前系統的納秒-- System.nanoTime();
- 32 位的隨機整數;
- 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 進行具體邏輯的處理.