Netty關鍵組件

Netty中抽象的接口

主要有三種類型接口

  • Channel ------ Socket
  • EventLoop ------ 控制流、多線程和併發
  • ChannelFuture ------ 異步通知

另外還包含邏輯處理(Handler)和處理數據流(Pipe)接口

一、Channel接口

Channel的產生是爲了降低網絡傳輸變成的複雜性,它是傳入傳出數據的載體,可以打開或者關閉,連接或斷開。可以當做它是Socket的升級,大大降低了直接與 Socket 進行操作的複雜性。

  1. EmbeddedChannel ----- Embedded傳輸,一般用於測試ChannelHandller
  2. LocalServerChannel ----- Local傳輸,在VM內部通過管道進行通信的本地傳輸
  3. NioDatagramChannel ----- UDP協議NIO傳輸
  4. NioSctpChannel ----- SCTP協議NIO傳輸(基於Session)
  5. 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 來服務的。所以有如下約定俗成的關係(非常重要):

  1. 一個EventLoopGroup包含一個或多個EventLoop
  2. 一個EventLoop在其生命週期內只能和一個Thread綁定
  3. 由EventLoop處理的I/O事件都由它綁定的Thread處理
  4. 一個Channel在其生命週期內,只能註冊於一個EventLoop
  5. 一個EventLoop可能被分配處理多個Channel。也就是EventLoop與Channel是1:n的關係
  6. 一個Channel上的所有ChannelHandler的事件由綁定的EventLoop中的I/O線程處理
  7. 不要阻塞Channel的I/O線程,可能會影響該EventLoop中其他Channel事件處理

source: 【死磕Netty】-----Netty的核心組件)

三、 ChannelFuture 接口

Netty中所有的I/O操作都是異步的,該異步操作可能無法立即得到返回。Netty提供addListener()方法註冊回調函數——ChannelFutureListener,當操作執行成功或者失敗時,監聽就會自動觸發返回結果。

  1. 可以將ChannelFuture看作是將來要執行的操作的結果佔位符,什麼時候被執行,不知道。但肯定會被執行
  2. 屬於同一個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提供了抽象基類ChannelInboundHandlerAdapterChannelOutboundHandlerAdapter

可以使用ChannelInboundHandlerAdapterChannelOutboundHandlerAdapter類作爲自己的ChannelHandler 的起始點。這兩個適配器分別提供了ChannelInboundHandler和ChannelOutboundHandler 的基本實現。通過擴展抽象類ChannelHandlerAdapter,它們獲得了它們共同的超接口ChannelHandler 的方法。

ChannelHandlerAdapter 還提供了實用方法isSharable()。如果其對應的實現被標註爲Sharable,那麼這個方法將返回true,表示它可以被添加到多個ChannelPipeline。

source: Netty基礎:細說ChannelHandler和ChannelPipeLine

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