# ChannelPipeline
如果我們認為 ChannelPipeline 只是一系列 ChannelHandler 實例,用于攔截 流經一個 Channel 的入站和出站事件,然后很容易理解這些 ChannelHandler 可以提供的交互的核心應用程序的數據和事件處理邏輯。
每一個創建新 Channel ,分配一個新的 ChannelPipeline。這個關聯是永久性的;Channel 既不能附上另一個 ChannelPipeline 也不能分離當前這個。這是一個 Netty 的固定方面的組件生命周期,開發人員無需特別處理。
根據它的起源,一個事件將由 ChannelInboundHandler 或ChannelOutboundHandler 處理。隨后它將調用 ChannelHandlerContext 實現轉發到下一個相同的超類型的處理程序。
*ChannelHandlerContext*
*一個 ChannelHandlerContext 使 ChannelHandler 與 ChannelPipeline 和其他處理程序交互。一個處理程序可以通知下一個 ChannelPipeline 中的 ChannelHandler 甚至動態修改 ChannelPipeline 的歸屬。*
下圖展示了用于入站和出站 ChannelHandler 的 典型 ChannelPipeline 布局。

Figure 6.2 ChannelPipeline and ChannelHandlers
上圖說明了 ChannelPipeline 主要是一系列 ChannelHandler。通過ChannelPipeline ChannelPipeline 還提供了方法傳播事件本身。如果一個入站事件被觸發,它將被傳遞的從 ChannelPipeline 開始到結束。舉個例子,在這個圖中出站 I/O 事件將從 ChannelPipeline 右端開始一直處理到左邊。
*ChannelPipeline 相對論*
*你可能會說,從 ChannelPipeline 事件傳遞的角度來看,ChannelPipeline 的“開始” 取決于是否入站或出站事件。然而,Netty 總是指 ChannelPipeline 入站口(圖中的左邊)為“開始”,出站口(右邊)作為“結束”。當我們完成使用 ChannelPipeline.add*() 添加混合入站和出站處理程序,每個 ChannelHandler 的“順序”是它的地位從“開始”到“結束”正如我們剛才定義的。因此,如果我們在圖6.1處理程序按順序從左到右第一個ChannelHandler被一個入站事件將是#1,第一個處理程序被出站事件將是#5*
隨著管道傳播事件,它決定下個 ChannelHandler 是否是相匹配的方向運動的類型。如果沒有,ChannelPipeline 跳過 ChannelHandler 并繼續下一個合適的方向。記住,一個處理程序可能同時實現ChannelInboundHandler 和 ChannelOutboundHandler 接口。
### 修改 ChannelPipeline
ChannelHandler 可以實時修改 ChannelPipeline 的布局,通過添加、移除、替換其他 ChannelHandler(也可以從 ChannelPipeline 移除 ChannelHandler 自身)。這個 是 ChannelHandler 重要的功能之一。
Table 6.6 ChannelHandler methods for modifying a ChannelPipeline
| 名稱 | 描述 |
|-----|-----|
| addFirst addBefore addAfter addLast | 添加 ChannelHandler 到 ChannelPipeline. |
| Remove | 從 ChannelPipeline 移除 ChannelHandler. |
| Replace | 在 ChannelPipeline 替換另外一個 ChannelHandler |
下面展示了操作
Listing 6.5 Modify the ChannelPipeline
~~~
ChannelPipeline pipeline = null; // get reference to pipeline;
FirstHandler firstHandler = new FirstHandler(); //1
pipeline.addLast("handler1", firstHandler); //2
pipeline.addFirst("handler2", new SecondHandler()); //3
pipeline.addLast("handler3", new ThirdHandler()); //4
pipeline.remove("handler3"); //5
pipeline.remove(firstHandler); //6
pipeline.replace("handler2", "handler4", new ForthHandler()); //6
~~~
1. 創建一個 FirstHandler 實例
1. 添加該實例作為 "handler1" 到 ChannelPipeline
1. 添加 SecondHandler 實例作為 "handler2" 到 ChannelPipeline 的第一個槽,這意味著它將替換之前已經存在的 "handler1"
1. 添加 ThirdHandler 實例作為"handler3" 到 ChannelPipeline 的最后一個槽
1. 通過名稱移除 "handler3"
1. 通過引用移除 FirstHandler (因為只有一個,所以可以不用關聯名字 "handler1").
1. 將作為"handler2"的 SecondHandler 實例替換為作為 "handler4"的 FourthHandler
以后我們將看到,這種輕松添加、移除和替換 ChannelHandler 能力,適合非常靈活的實現邏輯。
*ChannelHandler 執行 ChannelPipeline 和阻塞*
*通常每個 ChannelHandler 添加到 ChannelPipeline 將處理事件傳遞到 EventLoop( I/O 的線程)。至關重要的是不要阻塞這個線程,它將會負面影響的整體處理I/O。有時可能需要使用阻塞 api 接口來處理遺留代碼。對于這個情況下,ChannelPipeline 已有 add() 方法,它接受一個EventExecutorGroup。如果一個定制的EventExecutorGroup 傳入事件將由含在這個 EventExecutorGroup 中的 EventExecutor之一來處理,并且從 Channel 的 EventLoop本身離開。一個默認實現,稱為來自 Netty 的 DefaultEventExecutorGroup *
除了上述操作,其他訪問 ChannelHandler 的方法如下:
Table 6.7 ChannelPipeline operations for retrieving ChannelHandlers
| 名稱 | 描述 |
|-----|-----|
| get(...) | Return a ChannelHandler by type or name |
| context(...) | Return the ChannelHandlerContext bound to a ChannelHandler. |
| names() iterator() | Return the names or of all the ChannelHander in the ChannelPipeline. |
### 發送事件
ChannelPipeline API 有額外調用入站和出站操作的方法。下表列出了入站操作,用于通知 ChannelPipeline 中 ChannelInboundHandlers 正在發生的事件
Table 6.8 Inbound operations on ChannelPipeline
| 名稱 | 描述 |
|-----|-----|
| fireChannelRegistered | Calls channelRegistered(ChannelHandlerContext) on the next |
ChannelInboundHandler in the ChannelPipeline.fireChannelUnregistered | Calls channelUnregistered(ChannelHandlerContext) on the nextChannelInboundHandler in the ChannelPipeline.fireChannelActive | Calls channelActive(ChannelHandlerContext) on the nextChannelInboundHandler in the ChannelPipeline.fireChannelInactive | Calls channelInactive(ChannelHandlerContext)on the nextChannelInboundHandler in the ChannelPipeline.fireExceptionCaught | Calls exceptionCaught(ChannelHandlerContext, Throwable) on thenext ChannelHandler in the ChannelPipeline.fireUserEventTriggered | Calls userEventTriggered(ChannelHandlerContext, Object) on thenext ChannelInboundHandler in the ChannelPipeline.fireChannelRead | Calls channelRead(ChannelHandlerContext, Object msg) on the nextChannelInboundHandler in the ChannelPipeline.fireChannelReadComplete | Calls channelReadComplete(ChannelHandlerContext) on the nextChannelStateHandler in the ChannelPipeline.
在出站方面,處理一個事件將導致底層套接字的一些行動。下表列出了ChannelPipeline API 出站的操作。
Table 6.9 Outbound operations on ChannelPipeline
| 名稱 | 描述 |
|-----|-----|
| bind | Bind the Channel to a local address. This will call |
bind(ChannelHandlerContext, SocketAddress, ChannelPromise) on the next ChannelOutboundHandler in the ChannelPipeline.connect | Connect the Channel to a remote address. This will call connect(ChannelHandlerContext, SocketAddress,ChannelPromise) on the next ChannelOutboundHandler in theChannelPipeline.disconnect | Disconnect the Channel. This will calldisconnect(ChannelHandlerContext, ChannelPromise) on the next ChannelOutboundHandler in the ChannelPipeline.close | Close the Channel. This will call close(ChannelHandlerContext,ChannelPromise) on the next ChannelOutboundHandler in the ChannelPipeline.deregister | Deregister the Channel from the previously assigned EventExecutor (the EventLoop). This will call deregister(ChannelHandlerContext,ChannelPromise) on the next ChannelOutboundHandler in the ChannelPipeline.flush | Flush all pending writes of the Channel. This will call flush(ChannelHandlerContext) on the next ChannelOutboundHandler in the ChannelPipeline.write | Write a message to the Channel. This will callwrite(ChannelHandlerContext, Object msg, ChannelPromise) on the next ChannelOutboundHandler in the ChannelPipeline.Note: this does not write the message to the underlying Socket, but only queues it. To write it to the Socket call flush() or writeAndFlush().writeAndFlush | Convenience method for calling write() then flush().read | Requests to read more data from the Channel. This will call read(ChannelHandlerContext) on the next ChannelOutboundHandler in the ChannelPipeline.
總結下:
- 一個 ChannelPipeline 是用來保存關聯到一個 Channel 的ChannelHandler
- 可以修改 ChannelPipeline 通過動態添加和刪除 ChannelHandler
- ChannelPipeline 有著豐富的API調用動作來回應入站和出站事件。
- Introduction
- 開始
- Netty-異步和數據驅動
- Netty 介紹
- 構成部分
- 關于本書
- 第一個 Netty 應用
- 設置開發環境
- Netty 客戶端/服務端 總覽
- 寫一個 echo 服務器
- 寫一個 echo 客戶端
- 編譯和運行 Echo 服務器和客戶端
- 總結
- Netty 總覽
- Netty 快速入門
- Channel, Event 和 I/O
- 什么是 Bootstrapping 為什么要用
- ChannelHandler 和 ChannelPipeline
- 近距離觀察 ChannelHandler
- 總結
- 核心功能
- Transport(傳輸)
- 案例研究:Transport 的遷移
- Transport API
- 包含的 Transport
- Transport 使用情況
- 總結
- Buffer(緩沖)
- Buffer API
- ByteBuf - 字節數據的容器
- 字節級別的操作
- ByteBufHolder
- ByteBuf 分配
- 總結
- ChannelHandler 和 ChannelPipeline
- ChannelHandler 家族
- ChannelPipeline
- ChannelHandlerContext
- 總結
- Codec 框架
- 什么是 Codec
- Decoder(解碼器)
- Encoder(編碼器)
- 抽象 Codec(編解碼器)類
- 總結
- 提供了的 ChannelHandler 和 Codec
- 使用 SSL/TLS 加密 Netty 程序
- 構建 Netty HTTP/HTTPS 應用
- 空閑連接以及超時
- 解碼分隔符和基于長度的協議
- 編寫大型數據
- 序列化數據
- 總結
- Bootstrap 類型
- 引導客戶端和無連接協議
- 引導服務器
- 從 Channel 引導客戶端
- 在一個引導中添加多個 ChannelHandler
- 使用Netty 的 ChannelOption 和屬性
- 關閉之前已經引導的客戶端或服務器
- 總結
- 引導
- Bootstrap 類型
- 引導客戶端和無連接協議
- 引導服務器
- 從 Channel 引導客戶端
- 在一個引導中添加多個 ChannelHandler
- 使用Netty 的 ChannelOption 和屬性
- 關閉之前已經引導的客戶端或服務器
- 總結
- NETTY BY EXAMPLE
- 單元測試
- 總覽
- 測試 ChannelHandler
- 測試異常處理
- 總結
- WebSocket
- WebSocket 程序示例
- 添加 WebSocket 支持
- 測試程序
- 總結
- SPDY
- SPDY 背景
- 示例程序
- 實現
- 啟動 SpdyServer 并測試
- 總結
- 通過 UDP 廣播事件
- UDP 基礎
- UDP 廣播
- UDP 示例
- EventLog 的 POJO
- 寫廣播器
- 寫監視器
- 運行 LogEventBroadcaster 和 LogEventMonitor
- 總結
- 高級主題
- 實現自定義的編解碼器
- 編解碼器的范圍
- 實現 Memcached 編解碼器
- 了解 Memcached 二進制協議
- Netty 編碼器和解碼器
- 測試編解碼器
- EventLoop 和線程模型
- 線程模型的總覽
- EventLoop
- EventLoop
- I/O EventLoop/Thread 分配細節
- 總結
- 用例1:Droplr Firebase 和 Urban Airship
- 用例2:Facebook 和 Twitter