Netty中抽象的接口
主要有三種類型接口
- Channel ------ Socket
- EventLoop ------ 控制流、多線程和併發
- ChannelFuture ------ 異步通知
另外還包含邏輯處理(Handler)和處理數據流(Pipe)接口
一、Channel接口
Channel的產生是爲了降低網絡傳輸變成的複雜性,它是傳入傳出數據的載體,可以打開或者關閉,連接或斷開。可以當做它是Socket的升級,大大降低了直接與 Socket 進行操作的複雜性。
- EmbeddedChannel ----- Embedded傳輸,一般用於測試ChannelHandller
- LocalServerChannel ----- Local傳輸,在VM內部通過管道進行通信的本地傳輸
- NioDatagramChannel ----- UDP協議NIO傳輸
- NioSctpChannel ----- SCTP協議NIO傳輸(基於Session)
- NioSocketChannel ----- TCP協議NIO傳輸,使用Java提供的NIO作爲基礎,基於選擇器的方式(重點)
二、EventLoop 接口
Channel 爲Netty 網絡操作抽象類,EventLoop 主要是爲Channel 處理 I/O 操作,兩者配合參與 I/O 操作。
下圖是Channel、EventLoop、Thread、EventLoopGroup之間的關係(摘自《Netty In Action》)
當一個連接到達時,Netty 就會註冊一個 Channel,然後從 EventLoopGroup 中分配一個 EventLoop 綁定到這個Channel上,在該Channel的整個生命週期中都是有這個綁定的 EventLoop 來服務的。所以有如下約定俗成的關係(非常重要):
- 一個EventLoopGroup包含一個或多個EventLoop
- 一個EventLoop在其生命週期內只能和一個Thread綁定
- 由EventLoop處理的I/O事件都由它綁定的Thread處理
- 一個Channel在其生命週期內,只能註冊於一個EventLoop
- 一個EventLoop可能被分配處理多個Channel。也就是EventLoop與Channel是1:n的關係
- 一個Channel上的所有ChannelHandler的事件由綁定的EventLoop中的I/O線程處理
- 不要阻塞Channel的I/O線程,可能會影響該EventLoop中其他Channel事件處理
source: 【死磕Netty】-----Netty的核心組件)
三、 ChannelFuture 接口
Netty中所有的I/O操作都是異步的,該異步操作可能無法立即得到返回。Netty提供addListener()
方法註冊回調函數——ChannelFutureListener
,當操作執行成功或者失敗時,監聽就會自動觸發返回結果。
- 可以將ChannelFuture看作是將來要執行的操作的結果佔位符,什麼時候被執行,不知道。但肯定會被執行
- 屬於同一個Channel的操作(回調函數)都被保證將按照註冊的順序執行。
比如有直接阻塞獲取的方式:
/*綁定到端口,阻塞等待直到連接完成*/
ChannelFuture f = b.bind().sync();
異步監聽的方式
serverBootstrap.bind(port).addListener(future -> {
if (future.isSuccess()) {
System.out.println(new Date() + ": 端口[" + port + "]綁定成功!");
} else {
System.err.println("端口[" + port + "]綁定失敗!");
}
四、ChannelHandler接口
ChannelHandler 爲 Netty 中最核心的組件,它充當了所有處理入站和出站數據的應用程序邏輯的容器。ChannelHandler 主要用來處理各種事件,這裏的事件很廣泛,比如可以是連接、數據接收、異常、數據轉換等。
ChannelHandler 有兩個核心子類 ChannelInboundHandler 和 ChannelOutboundHandler,其中 ChannelInboundHandler 用於接收、處理入站數據和事件,而 ChannelOutboundHandler 則相反。Handler用適配器模式的Adapter對Handler接口進行了空實現,類圖關係如下:
1.ChannelHandler的生命週期
- handlerAdded 當把ChannelHandler 添加到ChannelPipeline 中時被調用
- handlerRemoved 當從ChannelPipeline 中移除ChannelHandler 時被調用
- exceptionCaught 當處理過程中在ChannelPipeline 中有錯誤產生時被調用
2.ChannelInboundHandler 接口<重點>
- channelRegistered,當Channel 已經註冊到它的EventLoop 並且能夠處理I/O 時被調用
- channelUnregistered,當Channel 從它的EventLoop 註銷並且無法處理任何I/O 時被調用
- channelActive,當Channel 處於活動狀態時被調用;Channel 已經連接/綁定並且已經就緒
- channelInactive,當Channel 離開活動狀態並且不再連接它的遠程節點時被調用
- channelReadComplete,當Channel上的一個讀操作完成時被調用①
- channelRead,當從Channel 讀取數據時被調用
- ChannelWritability-Changed,當Channel 的可寫狀態發生改變時被調用。用戶可以確保寫操作不會完成得太快(以避免發生OutOfMemoryError)或者可以在Channel 變爲再次可寫時恢復寫入。可以通過調用Channel 的isWritable()方法來檢測Channel 的可寫性。與可寫性相關的閾值可以通過Channel.config().setWriteHighWaterMark()和Channel.config().setWriteLowWater-Mark()方法來設置。
- userEventTriggered,當ChannelnboundHandler.fireUserEventTriggered()方法被調用時被調用。
當某個ChannelInboundHandler 的實現重寫channelRead()方法時,它要負責顯式地釋放與池化的ByteBuf 實例相關的內存,Netty 爲此提供了一個實用方法ReferenceCount-Util.release()
。當然還有一個更加簡單的方式是使用Simple-ChannelInboundHandler,SimpleChannelInboundHandler
會自動釋放資源。
@Sharable
public class DiscardHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ReferenceCountUtil.release(msg);
}
3.ChannelOutboundHandler 接口
-
bind(ChannelHandlerContext,SocketAddress,ChannelPromise)
當請求將Channel 綁定到本地地址時被調用 -
connect(ChannelHandlerContext,SocketAddress,SocketAddress,ChannelPromise)
當請求將Channel 連接到遠程節點時被調用 -
disconnect(ChannelHandlerContext,ChannelPromise)
當請求將Channel 從遠程節點斷開時被調用 - close(ChannelHandlerContext,ChannelPromise) 當請求關閉Channel 時被調用
-
deregister(ChannelHandlerContext,ChannelPromise)
當請求將Channel 從它的EventLoop 註銷時被調用 - read(ChannelHandlerContext) 當請求從Channel 讀取更多的數據時被調用
- flush(ChannelHandlerContext) 當請求通過Channel 將入隊數據沖刷到遠程節點時被調用
- write(ChannelHandlerContext,Object,ChannelPromise) 當請求通過Channel 將數據寫到遠程節點時被調用
4.ChannelHandler的Adapter適配器
有一些適配器類可以將編寫自定義的ChannelHandler所需要的努力降到最低限度,因爲它們提供了定義在對應接口中的所有方法的默認實現,因爲有時會忽略那些不感興趣的事件,所以Netty提供了抽象基類ChannelInboundHandlerAdapter 和ChannelOutboundHandlerAdapter。
可以使用ChannelInboundHandlerAdapter
和ChannelOutboundHandlerAdapter
類作爲自己的ChannelHandler 的起始點。這兩個適配器分別提供了ChannelInboundHandler和ChannelOutboundHandler 的基本實現。通過擴展抽象類ChannelHandlerAdapter,它們獲得了它們共同的超接口ChannelHandler 的方法。
ChannelHandlerAdapter 還提供了實用方法isSharable()。如果其對應的實現被標註爲Sharable,那麼這個方法將返回true,表示它可以被添加到多個ChannelPipeline。