本文主要介紹如下幾個相關類:
• ChannelPipeline
• ChannelHandlerContext
• ChannelHandler
• Inbound vs outbound(入站和出站)java
接受客戶端的鏈接和建立鏈接只是應用程序中的一步,更加劇要的仍是處理傳入傳出的數據。netty提供了強大的事件處理機制,容許用戶自定義ChannelHandler的實現來處理數據。安全
ChannelPipeline是ChannelHandler實例的列表(或則說是容器),用於處理或截獲通道的接收和發送數據。ChannelPipeline提供了一種高級的截取過濾器模式,讓用戶能夠在ChannelPipeline中徹底控制一個事件及如何處理ChannelHandler與ChannelPipeline的交互。ide
能夠這樣說,一個新的通道就對應一個新的ChannelPipeline並附加至通道。一旦鏈接,通道Channel和ChannelPipeline之間的耦合是永久性的。通道Channel不能附加其餘的ChannelPipeline或從ChannelPipeline分離。oop
下圖顯示了ChannelHandler在ChannelPipeline中的IO處理示意圖:
this
很明顯,ChannelPipeline裏面就是一個ChannelHandler的列表。若是一個入站IO事件被觸發,這個事件會從第一個開始依次經過ChannelPipeline中的ChannelHandler。如果一個入站I/O事件,則會從最後一個開始依次經過ChannelPipeline中的ChannelHandler。ChannelHandler能夠處理事件並檢查類型,若是某個ChannelHandler不能處理則會跳過,並將事件傳遞到下一個ChannelHandler。ChannelPipeline能夠動態添加、刪除、替換其中的ChannelHandler,這樣的機制能夠提升靈活性。spa
修改ChannelPipeline的方法:
• addFirst(…),添加ChannelHandler在ChannelPipeline的第一個位置
• addBefore(…),在ChannelPipeline中指定的ChannelHandler名稱以前添加ChannelHandler
• addAfter(…),在ChannelPipeline中指定的ChannelHandler名稱以後添加ChannelHandler
• addLast(ChannelHandler…),在ChannelPipeline的末尾添加ChannelHandler
• remove(…),刪除ChannelPipeline中指定的ChannelHandler
• replace(…),替換ChannelPipeline中指定的ChannelHandler線程
好比:netty
ChannelPipeline pipeline = ch.pipeline(); FirstHandler firstHandler = new FirstHandler(); pipeline.addLast("handler1", firstHandler); pipeline.addFirst("handler2", new SecondHandler()); pipeline.addLast("handler3", new ThirdHandler()); pipeline.remove("「handler3「"); pipeline.remove(firstHandler); pipeline.replace("handler2", "handler4", new FourthHandler());
被添加到ChannelPipeline的ChannelHandler將經過IO-Thread處理事件,這意味了必須不能有其餘的IO-Thread阻塞來影響IO的總體處理;有時候可能須要阻塞,例如JDBC。所以netty容許經過一個EventExecutorGroup到每個ChannelPipeline.add*方法,自定義的事件會被包含在EventExecutorGroup中的EventExecutor來處理,默認的實現是DefaultEventExecutorGroup。code
每一個ChannelHandler被添加到ChannelPipeline後,都會建立一個ChannelHandlerContext並與之建立的ChannelHandler關聯綁定。ChannelHandlerContext容許ChannelHandler與其餘的ChannelHandler實現進行交互。ChannelHandlerContext不會改變添加到其中的ChannelHandler,所以它是安全的。對象
下圖顯示了ChannelHandlerContext、ChannelHandler、ChannelPipeline的關係:
若是咱們想有一些事件流所有經過ChannelPipeline,有兩個不一樣的方法能夠作到:
• 調用Channel的方法
• 調用ChannelPipeline的方法
這兩個方法均可以讓事件流所有經過ChannelPipeline。不管從頭部仍是尾部開始,由於它主要依賴於事件的性質。若是是一個「入站」事件,它開始於頭部;如果一個「出站」事件,則開始於尾部。
下面的代碼顯示了一個寫事件如何經過ChannelPipeline從尾部開始:
protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new ChannelInboundHandlerAdapter() { @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { //Event via Channel Channel channel = ctx.channel(); channel.write(Unpooled.copiedBuffer("netty in action", CharsetUtil.UTF_8)); //Event via ChannelPipeline ChannelPipeline pipeline = ctx.pipeline(); pipeline.write(Unpooled.copiedBuffer("netty in action", CharsetUtil.UTF_8)); } }); }
下圖表示經過Channel或ChannelPipeline的通知:
調用ChannelHandlerContext的pipeline()方法能訪問ChannelPipeline,能在運行時動態的增長、刪除、替換ChannelPipeline中的ChannelHandler。咱們能夠保持ChannelHandlerContext以供之後使用,如外部Handler方法觸發一個事件,甚至從一個不一樣的線程觸發。
下面代碼顯示了保存ChannelHandlerContext供以後使用或其餘線程使用:
public class WriteHandler extends ChannelHandlerAdapter { private ChannelHandlerContext ctx; @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { this.ctx = ctx; } public void send(String msg){ ctx.write(msg); } }
請注意,ChannelHandler實例若是帶有@Sharable註解則能夠被添加到多個ChannelPipeline。也就是說單個ChannelHandler實例能夠有多個ChannelHandlerContext,所以能夠調用不一樣ChannelHandlerContext獲取同一個ChannelHandler。若是添加不帶@Sharable註解的ChannelHandler實例到多個ChannelPipeline則會拋出異常;使用@Sharable註解後的ChannelHandler必須在不一樣的線程和不一樣的通道上安全使用。怎麼是不安全的使用?看下面代碼:
@Sharable public class NotSharableHandler extends ChannelInboundHandlerAdapter { private int count; @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { count++; System.out.println("channelRead(...) called the " + count + " time「"); ctx.fireChannelRead(msg); } }
上面是一個帶@Sharable註解的Handler,它被多個線程使用時,裏面count是不安全的,會致使count值錯誤。
爲何要共享ChannelHandler?使用@Sharable註解共享一個ChannelHandler在一些需求中仍是有很好的做用的,如使用一個ChannelHandler來統計鏈接數或來處理一些全局數據等等。
Netty有一個簡單但強大的狀態模型,並完美映射到ChannelInboundHandler的各個方法。下面是Channel生命週期四個不一樣的狀態:
1. channelUnregistered
2. channelRegistered
3. channelActive
4. channelInactive
Channel的狀態在其生命週期中變化,由於狀態變化須要觸發,下圖顯示了Channel狀態變化:
先看一些類的繼承圖:
Netty定義了良好的類型層次結構來表示不一樣的處理程序類型,全部的類型的父類是ChannelHandler。ChannelHandler提供了在其生命週期內添加或從ChannelPipeline中刪除的方法。
1. handlerAdded,ChannelHandler添加到實際上下文中準備處理事件
2. handlerRemoved,將ChannelHandler從實際上下文中刪除,再也不處理事件
3. exceptionCaught,處理拋出的異常
netty還提供了一個實現了ChannelHandler的抽象類ChannelHandlerAdapter。ChannelHandlerAdapter實現了父類的全部方法,基本上就是傳遞事件到ChannelPipeline中的下一個ChannelHandler直到結束。咱們也能夠直接繼承於ChannelHandlerAdapter,而後重寫裏面的方法。
ChannelInboundHandler提供了一些方法再接收數據或Channel狀態改變時被調用。下面是ChannelInboundHandler的一些方法:
1. channelRegistered,ChannelHandlerContext的Channel被註冊到EventLoop;
2. channelUnregistered,ChannelHandlerContext的Channel從EventLoop中註銷
3. channelActive,ChannelHandlerContext的Channel已激活
4. channelInactive,ChannelHanderContxt的Channel結束生命週期
5. channelRead,從當前Channel的對端讀取消息
6. channelReadComplete,消息讀取完成後執行
7. userEventTriggered,一個用戶事件被處罰
8. channelWritabilityChanged,改變通道的可寫狀態,可使用Channel.isWritable()檢查
9. exceptionCaught,重寫父類ChannelHandler的方法,處理異常
netty提供了一個實現了ChannelInboundHandler接口並繼承ChannelHandlerAdapter的類:ChannelInboundHandlerAdapter。ChannelInboundHandlerAdapter實現了ChannelInboundHandler的全部方法,做用就是處理消息並將消息轉發到ChannelPipeline中的下一個ChannelHandler。ChannelInboundHandlerAdapter的channelRead方法處理完消息後不會自動釋放消息,若想自動釋放收到的消息,可使用SimpleChannelInboundHandler。
看下面的代碼:
/** * 實現ChannelInboundHandlerAdapter的Handler,不會自動釋放接收的消息對象 * @author c.k * */ public class DiscardHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { //手動釋放消息 ReferenceCountUtil.release(msg); } }
SimpleChannelInboundHandler會自動釋放消息
/** * 繼承SimpleChannelInboundHandler,會自動釋放消息對象 * @author c.k * */ public class SimpleDiscardHandler extends SimpleChannelInboundHandler<Object> { @Override protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { //不須要手動釋放 } }
ChannelInitializer用來初始化ChannelHandler,將自定義的各類ChannelHandler添加到ChannelPipeline中。