Netty實戰讀書筆記二:ChannelHandler和ChannelPipeline

ChannelHandler家族

Channel生命週期

Interface Channel 定義了一組和 ChannelInboundHandler API 密切相關的簡單但 功能強大的狀態模型,表 6-1 列出了 Channel 的這 4 個狀態。


Channel 的正常生命週期如圖 6-1 所示。當這些狀態發生改變時,將會生成對應的事件。 這些事件將會被轉發給 ChannelPipeline 中的 ChannelHandler,其可以隨後對它們做出 響應。

ChannelHandler生命週期

表 6-2 中列出了 interface ChannelHandler 定義的生命週期操作,在 ChannelHandler 被添加到 ChannelPipeline 中或者被從 ChannelPipeline 中移除時會調用這些操作。這些 方法中的每一個都接受一個 ChannelHandlerContext 參數。

Netty 定義了下面兩個重要的 ChannelHandler 子接口:
 ChannelInboundHandler——處理入站數據以及各種狀態變化;

ChannelInboundHandler 接口

表 6-3 列出了 interface ChannelInboundHandler 的生命週期方法。這些方法將會在 數據被接收時或者與其對應的 Channel 狀態發生改變時被調用。正如我們前面所提到的,這些 方法和 Channel 的生命週期密切相關。

ChannelOutboundHandler 接口

出站操作和數據將由 ChannelOutboundHandler 處理。它的方法將被 Channel、Channel- Pipeline 以及 ChannelHandlerContext 調用。

ChannelOutboundHandler 的一個強大的功能是可以按需推遲操作或者事件,這使得可 以通過一些複雜的方法來處理請求。例如,如果到遠程節點的寫入被暫停了,那麼你可以推遲衝 刷操作並在稍後繼續。

表 6-4 顯示了所有由 ChannelOutboundHandler 本身所定義的方法(忽略了那些從 Channel- Handler 繼承的方法)。

ChannelPromise與ChannelFuture ChannelOutboundHandler中的大部分方法都需要一個 ChannelPromise參數,以便在操作完成時得到通知。ChannelPromise是ChannelFuture的一個 子類,其定義了一些可寫的方法,如setSuccess()和setFailure(),從而使ChannelFuture不可變。

ChannelHandler 適配器

你可以使用 ChannelInboundHandlerAdapter 和 ChannelOutboundHandlerAdapter 類作爲自己的 ChannelHandler 的起始點。這兩個適配器分別提供了 ChannelInboundHandler 和 ChannelOutboundHandler 的基本實現。通過擴展抽象類 ChannelHandlerAdapter,它們 獲得了它們共同的超接口 ChannelHandler 的方法。生成的類的層次結構如圖 6-2 所示。

ChannelPipeline 接口

每一個新創建的 Channel 都將會被分配一個新的 ChannelPipeline。這項關聯是永久性 的;Channel 既不能附加另外一個 ChannelPipeline,也不能分離其當前的。在 Netty 組件 的生命週期中,這是一項固定的操作,不需要開發人員的任何干預。

根據事件的起源,事件將會被 ChannelInboundHandler 或ChannelOutboundHandler處理。隨後,通過調用 ChannelHandlerContext 實現,它將被轉發給同一超類型的下一個ChannelHandler。

ChannelHandlerContext使得ChannelHandler能夠和它的ChannelPipeline以及其他的 ChannelHandler 交 互 。 ChannelHandler 可 以 通 知 其 所 屬 的 ChannelPipeline 中 的 下 一 個 ChannelHandler,甚至可以動態修改它所屬的ChannelPipeline1。
ChannelHandlerContext 具有豐富的用於處理事件和執行 I/O 操作的 API。

圖 6-3 展示了一個典型的同時具有入站和出站 ChannelHandler 的 ChannelPipeline 的布 局,並且印證了我們之前的關於 ChannelPipeline 主要由一系列的 ChannelHandler 所組成的 說 法 。C h a n n e l P i p e l i n e 還 提 供 了 通 過 C h a n n e l P i p e l i n e 本 身 傳 播 事 件 的 方 法 。如 果 一 個 入 站 事件被觸發,它將被從 ChannelPipeline 的頭部開始一直被傳播到 Channel Pipeline 的尾端。 在圖 6-3 中,一個出站 I/O 事件將從 ChannelPipeline 的最右邊開始,然後向左傳播。

修改 ChannelPipeline

ChannelHandler 可以通過添加、刪除或者替換其他的 ChannelHandler 來實時地修改 ChannelPipeline 的佈局。(它也可以將它自己從 ChannelPipeline 中移除。)這是 Channel- Handler 最重要的能力之一,所以我們將仔細地來看看它是如何做到的。表 6-6 列出了相關的方法。

觸發事件

ChannelPipeline 的 API 公開了用於調用入站和出站操作的附加方法。

總結一下:
 ChannelPipeline 保存了與 Channel 相關聯的 ChannelHandler;
 ChannelPipeline 可以根據需要,通過添加或者刪除 ChannelHandler 來動態地修改;

ChannelHandlerContext 接口

ChannelHandlerContext 代表了 ChannelHandler 和 ChannelPipeline 之間的關 聯,每當有 ChannelHandler 添加到 ChannelPipeline 中時,都會創建 ChannelHandler- Context。

ChannelHandlerContext 的主要功能是管理它所關聯的 ChannelHandler 和在 同一個 ChannelPipeline 中的其他 ChannelHandler 之間的交互。

ChannelHandlerContext 有很多的方法,其中一些方法也存在於 Channel 和 Channel- Pipeline 本身上,但是有一點重要的不同。如果調用 Channel 或者 ChannelPipeline 上的這 些方法,它們將沿着整個 ChannelPipeline 進行傳播。而調用位於 ChannelHandlerContext 上的相同方法,則將從當前所關聯的 ChannelHandler 開始,並且只會傳播給位於該 ChannelPipeline 中的下一個能夠處理該事件的 ChannelHandler。

當使用 ChannelHandlerContext 的 API 的時候,請牢記以下兩點:
 ChannelHandlerContext 和 ChannelHandler 之間的關聯(綁定)是永遠不會改變的,所以緩存對它的引用是安全的;
 如同我們在本節開頭所解釋的一樣,相對於其他類的同名方法,ChannelHandlerContext的方法將產生更短的事件流,應該儘可能地利用這個特性來獲得最大的性能。

要想調用從某個特定的 ChannelHandler 開始的處理過程,必須獲取到在Channel
Pipeline上該 ChannelHandler 之前的 ChannelHandler 所關聯的 ChannelHandlerContext。這個 ChannelHandlerContext 將調用和它所關聯的 ChannelHandler 之後的 ChannelHandler。

因爲一個 ChannelHandler 可以從屬於多個 ChannelPipeline,所以它也可以綁定到多 個 ChannelHandlerContext 實例。對於這種用法指在多個 ChannelPipeline 中共享同一 個 ChannelHandler,對應的 ChannelHandler 必須要使用@Sharable 註解標註;否則, 試圖將它添加到多個 ChannelPipeline 時將會觸發異常。顯而易見,爲了安全地被用於多個 併發的 Channel(即連接),這樣的 ChannelHandler 必須是線程安全的。

爲何要共享同一個ChannelHandler?
在多個ChannelPipeline中安裝同一個ChannelHandler的一個常見的原因是用於收集跨越多個 Channel 的統計信息。

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