Netty學習知識點筆記

1.Channel、 EventLoop 和 ChannelFuture的關係

在這裏插入圖片描述

  • EventLoop 定義了 Netty 的核心抽象, 用於處理連接的生命週期中所發生的事件
  • 一個 EventLoopGroup 包含一個或者多個 EventLoop
  • 一個 EventLoop 在它的生命週期內只和一個 Thread 綁定
  • 所有由 EventLoop 處理的 I/O 事件都將在它專有的 Thread 上被處理
  • 一個 Channel 在它的生命週期內只註冊於一個 EventLoop
  • 一個 EventLoop 可能會被分配給一個或多個 Channel
  • 一個給定 Channel 的 I/O 操作都是由相同的 Thread 執行的, 實際
    上消除了對於同步的需要
  • Netty 中所有的 I/O 操作都是異步的。因爲一個操作可能不會
    立即返回,所以我們需要一種用於在之後的某個時間點確定其結果的方法。爲此, Netty 提供了
    ChannelFuture 接口,其 addListener()方法註冊了一個 ChannelFutureListener,以
    便在某個操作完成時(無論是否成功)得到通知

2. ChannelHandler的執行順序

在這裏插入圖片描述

  • 由ChannelPipeline控的添加順序控制
   bootstrap.handler(new ChannelInitializer<SocketChannel>() {//ChannelInitializer是用於配置通道
                @Override
                public void initChannel(SocketChannel ch) {
                    ch.pipeline().addLast(new SDataEncoder());//出站
                    ch.pipeline().addLast(new SDataDecoder());//入站
                    ch.pipeline().addLast(new SIdleStateHandler(0, 0, SIdleTime));//in
                    ch.pipeline().addLast(clientDataHandler);//in
                }
            });

如這裏代碼,入站的順序是SDataDecoder(解密)–》clientDataHandler(客戶端數據處理)

public class SDataDecoder extends ByteToMessageDecoder {
   
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
    
       //在入棧解碼器中,需要將入的in數據,轉換成我們想要的數據對象outObject
       //然後把這個數據添加到out數組裏面
       out.add(outObject)
       //完成後,調用,讓流程走向下個channelhandler
        ctx.fireChannelReadComplete();
            
     }
  }

//SDataDecoder 的下個channelhandler,如下面這個,就需要指定爲解碼後的數據類型,

public class ClientDataHandlerextends SimpleChannelInboundHandler<outObject> {
//在這個方法中,就能處理我們想要服務器返回的入站的數據了
 @Override
    protected void channelRead0(ChannelHandlerContext ctx, SMessage s) {
}

在這裏插入圖片描述

在這裏插入圖片描述

3. 粘包、拼包原因

由服務器發送的消息可能會被分塊接收,也可能分塊發送。 也就是說,如果服務器發送了 5 字節, 那麼不
能保證這 5 字節會被一次性接收。 即使是對於這麼少量的數據, channelRead0()方法也可能
會被調用兩次,第一次使用一個持有 3 字節的 ByteBuf(Netty 的字節容器),第二次使用一個
持有 2 字節的 ByteBuf。作爲一個面向流的協議, TCP 保證了字節數組將會按照服務器發送它們的順序被接收。所以只要按照特定的長度或者分隔符處理,就能拼接到完整的數據包

設計時,不同的消息除了劃分不同的消息類型,還需要針對每個消息一個唯一的消息id,不然同類型消息,只按照消息類型接收,可能會出現發送一次請求,同時受到多條回覆(其他線程也發送了類似的消息)

4. ChannelHandler 的典型用途包括

  • 將數據從一種格式轉換爲另一種格式;
  • 提供異常的通知;
  • 提供 Channel 變爲活動的或者非活動的通知;
  • 提供當 Channel 註冊到 EventLoop 或者從 EventLoop 註銷時的通知;
  • 提供有關用戶自定義事件的通知。

攔截過濾器 ChannelPipeline 實現了一種常見的設計模式—攔截過濾器(Intercepting Filter)。 UNIX 管道是另外一個熟悉的例子: 多個命令被鏈接在一起,其中一個命令的輸出端將連
接到命令行中下一個命令的輸入端。

5. chanel 的是線程安全的

Netty 的 Channel 實現是線程安全的,因此你可以存儲一個到 Channel 的引用,並且每當
你需要向遠程節點寫數據時,多個線程都使用這個 Channel寫數據,都是沒問題的

6. EventLoop的是線程管理

Netty線程模型的卓越性能取決於對於當前執行的Thread的身份的確定 ,確定它是否是分配給當前Channel以及它的EventLoop的那一個線程(因爲當前不同的線程會搶用cpu)。如果(當前)調用線程正是支撐 EventLoop 的線程, 那麼所提交的代碼塊將會被(直接)執行。否則,EventLoop 將調度該任務以便稍後執行,並將它放入到內部隊列中。當 EventLoop下次處理它的事件時, 它會執行隊列中的那些任務/事件。這也就解釋了任何的 Thread 是如何與 Channel 直接交互而無需在 ChannelHandler 中進行額外同步的

在這裏插入圖片描述

7. EventLoop/線程的分配

1. 異步傳輸
在這裏插入圖片描述
一旦一個 Channel 被分配給一個 EventLoop, 它將在它的整個生命週期中都使用這個EventLoop(以及相關聯的 Thread)。請牢記這一點,因爲它可以使你從擔憂你的 ChannelHandler 實現中的線程安全和同步問題中解脫出來。

2. 阻塞傳輸
在這裏插入圖片描述

但是, 正如同之前一樣, 得到的保證是每個 Channel 的 I/O 事件都將只會被一個 Thread(用於支撐該 Channel 的 EventLoop 的那個 Thread) 處理。

8. 引導

引導客戶端:
在這裏插入圖片描述
代碼示例:
在這裏插入圖片描述
在這裏插入圖片描述
引導服務器:

在這裏插入圖片描述
代碼示例:
在這裏插入圖片描述

9. 使用 Netty 的 ChannelOption 和屬性

在這裏插入圖片描述
在這裏插入圖片描述

10. 關閉

需要關閉 EventLoopGroup, 它將處理任何掛起的事件和任務,並且隨後
釋放所有活動的線程。這就是調用 EventLoopGroup.shutdownGracefully()方法的作用。這個方法調用將會返回一個 Future,這個 Future 將在關閉完成時接收到通知。需要注意的是,shutdownGracefully()方法也是一個異步的操作,所以你需要阻塞等待直到它完成,或者向所返回的 Future 註冊一個監聽器以在關閉完成時獲得通知

11. 解碼器

  • 抽象類 ByteToMessageDecoder

在這裏插入圖片描述
雖然 ByteToMessageDecoder 使得可以很簡單地實現這種模式,但是你可能會發現,在調用 readInt()方法前不得不驗證所輸入的 ByteBuf 是否具有足夠的數據有點繁瑣。ReplayingDecoder,它是一個特殊的解碼器,以少量的開銷消除了這個步驟。

  • 抽象類 ReplayingDecoder
    在這裏插入圖片描述和之前一樣,從ByteBuf中提取的int將會被添加到List中。如果沒有足夠的字節可用,這個readInt()方法的實現將會拋出一個Error,其將在基類中被捕獲並處理。當有更多的數據可供讀取時,該decode()方法將會被再次調用
  • 抽象類 MessageToMessageDecoder
    在這裏插入圖片描述
  • 在這裏插入圖片描述

12. 編碼器

抽象類 MessageToByteEncoder
在這裏插入圖片描述
抽象類 MessageToMessageEncoder
在這裏插入圖片描述

13.空閒的連接和超時

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

14.解碼基於分隔符的協議和基於長度的協議

基於分隔符的(delimited) 消息協議使用定義的字符來標記的消息或者消息段(通常被稱爲幀)的開頭或者結尾。
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
如果你正在使用除了行尾符之外的分隔符分隔的幀,那麼你可以以類似的方式使用 DelimiterBasedFrameDecoder,只需要將特定的分隔符序列指定到其構造函數即可
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
基於長度的協議通過將它的長度編碼到幀的頭部來定義幀,而不是使用特殊的分隔符來標記
它的結束。
在這裏插入圖片描述
在這裏插入圖片描述
你將經常會遇到被編碼到消息頭部的幀大小不是固定值的協議。爲了處理這種變長幀,你可以使用 LengthFieldBasedFrameDecoder, 它將從頭部字段確定幀長,然後從數據流中提取指定的字節數
在這裏插入圖片描述

發佈了175 篇原創文章 · 獲贊 113 · 訪問量 29萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章