這一小節,咱們將會學習 Netty 裏面一大核心組件: Pipeline 與 ChannelHandlerjava
Netty 中的 pipeline 和 channelHandler 經過責任鏈設計模式來組織代碼邏輯,而且可以支持邏輯的動態添加和刪除 ,Netty 可以支持各種協議的擴展,好比 HTTP,Websocket,Redis,靠的就是 pipeline 和 channelHandler,下面,咱們就來一塊兒學習一下這部份內容。設計模式
不管是從服務端來看,仍是客戶端來看,在 Netty 整個框架裏面,一條鏈接對應着一個 Channel,這條 Channel 全部的處理邏輯都在一個叫作 ChannelPipeline 的對象裏面,ChannelPipeline 是一個雙向鏈表結構,他和 Channel 之間是一對一的關係。api
ChannelPipeline 裏面每一個節點都是一個 ChannelHandlerContext 對象,這個對象可以拿到和 Channel 相關的全部的上下文信息,而後這個對象包着一個重要的對象,那就是邏輯處理器 ChannelHandler。promise
接下來,咱們再來看一下 ChannelHandler 有哪些分類。微信
能夠看到 ChannelHandler 有兩大子接口:框架
第一個子接口是 ChannelInboundHandler
,從字面意思也能夠猜到,他是處理讀數據的邏輯,好比,咱們在一端讀到一段數據,首先要解析這段數據,而後對這些數據作一系列邏輯處理,最終把響應寫到對端, 在開始組裝響應以前的全部的邏輯,均可以放置在 ChannelInboundHandler
裏處理,它的一個最重要的方法就是 channelRead()
。讀者能夠將 ChannelInboundHandler
的邏輯處理過程與 TCP 的七層協議的解析聯繫起來,收到的數據一層層從物理層上升到咱們的應用層。socket
第二個子接口 ChannelOutBoundHandler
是處理寫數據的邏輯,它是定義咱們一端在組裝完響應以後,把數據寫到對端的邏輯,好比,咱們封裝好一個 response
對象,接下來咱們有可能對這個 response
作一些其餘的特殊邏輯,而後,再編碼成 ByteBuf
,最終寫到對端,它裏面最核心的一個方法就是 write()
,讀者能夠將 ChannelOutBoundHandler
的邏輯處理過程與 TCP 的七層協議的封裝過程聯繫起來,咱們在應用層組裝響應以後,經過層層協議的封裝,直到最底層的物理層。tcp
這兩個子接口分別有對應的默認實現,ChannelInboundHandlerAdapter
,和 ChanneloutBoundHandlerAdapter
,它們分別實現了兩大接口的全部功能,默認狀況下會把讀寫事件傳播到下一個 handler
。ide
說了這麼多的理論,其實仍是比較抽象的,下面咱們就用一個具體的 demo 來學習一下這兩大 handler 的事件傳播機制。學習
關於 ChannelInboundHandler
,咱們拿 channelRead()
爲例子,來體驗一下 inbound
事件的傳播。
咱們在服務端的 pipeline
添加三個 ChannelInboundHandler
NettyServer.java
serverBootstrap .childHandler(new ChannelInitializer<NioSocketChannel>() { protected void initChannel(NioSocketChannel ch) { ch.pipeline().addLast(new InBoundHandlerA()); ch.pipeline().addLast(new InBoundHandlerB()); ch.pipeline().addLast(new InBoundHandlerC()); } });
每一個 inBoundHandler 都繼承自 ChannelInboundHandlerAdapter,而後實現了 channelRead() 方法
public class InBoundHandlerA extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { System.out.println("InBoundHandlerA: " + msg); super.channelRead(ctx, msg); } } public class InBoundHandlerB extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { System.out.println("InBoundHandlerB: " + msg); super.channelRead(ctx, msg); } } public class InBoundHandlerC extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { System.out.println("InBoundHandlerC: " + msg); super.channelRead(ctx, msg); } }
在 channelRead()
方法裏面,咱們打印當前 handler
的信息,而後調用父類的 channelRead()
方法,而這裏父類的 channelRead()
方法會自動調用到下一個 inBoundHandler
的 channelRead()
方法,而且會把當前 inBoundHandler
裏處理完畢的對象傳遞到下一個 inBoundHandler
,咱們例子中傳遞的對象都是同一個 msg。
咱們經過 addLast()
方法來爲 pipeline
添加 inBoundHandler
,固然,除了這個方法還有其餘的方法,感興趣的同窗能夠自行瀏覽一下 pipeline
的 api ,這裏咱們添加的順序爲 A -> B -> C
,最後,咱們來看一下控制檯的輸出
能夠看到,inBoundHandler
的執行順序與咱們經過 addLast()
方法 添加的順序保持一致,接下來,咱們再來看一下 outBoundHandler
的事件傳播。
關於 ChanneloutBoundHandler
,咱們拿 write()
爲例子,來體驗一下 outbound
事件的傳播。
咱們繼續在服務端的 pipeline
添加三個 ChanneloutBoundHandler
serverBootstrap .childHandler(new ChannelInitializer<NioSocketChannel>() { protected void initChannel(NioSocketChannel ch) { // inBound,處理讀數據的邏輯鏈 ch.pipeline().addLast(new InBoundHandlerA()); ch.pipeline().addLast(new InBoundHandlerB()); ch.pipeline().addLast(new InBoundHandlerC()); // outBound,處理寫數據的邏輯鏈 ch.pipeline().addLast(new OutBoundHandlerA()); ch.pipeline().addLast(new OutBoundHandlerB()); ch.pipeline().addLast(new OutBoundHandlerC()); } });
每一個 outBoundHandler
都繼承自 ChanneloutBoundHandlerAdapter
,而後實現了 write() 方法
public class OutBoundHandlerA extends ChannelOutboundHandlerAdapter { @Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { System.out.println("OutBoundHandlerA: " + msg); super.write(ctx, msg, promise); } } public class OutBoundHandlerB extends ChannelOutboundHandlerAdapter { @Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { System.out.println("OutBoundHandlerB: " + msg); super.write(ctx, msg, promise); } } public class OutBoundHandlerC extends ChannelOutboundHandlerAdapter { public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { System.out.println("OutBoundHandlerC: " + msg); super.write(ctx, msg, promise); } }
在 write() 方法裏面,咱們打印當前 handler
的信息,而後調用父類的 write()
方法,而這裏父類的 write()
方法會自動調用到下一個 outBoundHandler
的 write()
方法,而且會把當前 outBoundHandler
裏處理完畢的對象傳遞到下一個 outBoundHand
咱們經過 addLast()
方法 添加 outBoundHandler
的順序爲 A -> B -> C
,最後,咱們來看一下控制檯的輸出
能夠看到,outBoundHandler
的執行順序與咱們添加的順序相反,最後,咱們再來看一下 pipeline
的結構和執行順序。
pipeline 的結構
無論咱們定義的是哪一種類型的 handler
, 最終它們都是以雙向鏈表的方式鏈接,這裏實際鏈表的節點是 ChannelHandlerContext
,這裏爲了讓結構清晰突出,能夠直接把節點看做 ChannelHandlerContext
。
pipeline 的執行順序
雖然兩種類型的 handler
在一個雙向鏈表裏,可是這兩類 handler
的分工是不同的,inBoundHandler
的事件一般只會傳播到下一個 inBoundHandler
,outBoundHandler
的事件一般只會傳播到下一個 outBoundHandler
,二者相互不受干擾。
以上內容來源於掘金小冊《Netty 入門與實戰:仿寫微信 IM 即時通信系統》,若想得到更多,更詳細的內容,請用微信掃碼訂閱: