1. 程式人生 > >Netty 源碼 ChannelHandler(四)編解碼技術

Netty 源碼 ChannelHandler(四)編解碼技術

它的 大小限制 緩沖 進程 個數字 實現 節點 特性 阻塞

Netty 源碼 ChannelHandler(四)編解碼技術

Netty 系列目錄(https://www.cnblogs.com/binarylei/p/10117436.html)

一、拆包與粘包問題

由於 TCP 是面向字節流的,什麽意思呢:雖然應用程序和 TCP 的交互是一次一個數據塊(大小不等),但 TCP 把應用程序交下來的數據僅僅看成式一連串的無結構的字節流。TCP 並不知道所傳送的字節流的含義。

因此 TCP 不保證接收方應用程序所收到的數據塊和發送方應用程序所發出的數據塊具有對應大小的關系(例如,發送方應用程序交給發送方的 TCP 共 10 個數據塊,但接收方的 TCP 可能只用了 4 個就把收到的字節流交付上層的應用程序)。

同時,TCP 不關心應用進程一次把多長的報文發送到 TCP 的 緩存 中,而是根據對方給出的窗口值和當前網絡阻塞的程度來決定一個報文段應包含多少個字節(UDP 發送的報文長度是應用進程給出的)。如果應用進程傳送到 TCP 緩存的數據塊太長,TCP 就可以把他劃分短一點再傳送。如果應用程序一次只發來一個字節,TCP 也可以等待積累有足夠多的字節後再構成報文段發送出去。

TCP 發送報文一般是 3 個時機:

  • 緩沖區數據達到 最大報文長度 MSS;
  • 由發送端的應用進程指明要求發送報文段,即 TCP 支持的推送(push)操作;
  • 當發送方的一個計時器期限到了,即使長度不超過 MSS ,也發送。

以上引自《計算機網絡-----謝希仁》。

說了這麽多,TCP 的這種機制,會導致什麽問題呢?粘包問題。有了粘包,就需要拆包。

如何區分一個整包消息,業界通常有如下 4 種做法:

  1. 固定長度,例如每 120 個字節代表一個整包消息,不足的前面補零。解碼器在處理這類定常消息的時候比較簡單,每次讀到指定長度的字節後再進行解碼。

  2. 通過回車換行符區分消息,例如 FTP 協議。這類區分消息的方式多用於文本協議。

  3. 通過分隔符區分整包消息。

  4. 通過指定長度來標識整包消息。

Netty 作為一個網絡框架,對 TCP 連接中的問題都做了全面的考慮,比如粘包拆包導致的半包問題,如何編解碼,如何實現私有協議,序列化等等。本文主要針對這些問題做一個簡單介紹,目的是想對整個 Netty 的編解碼框架做一個全盤的審視,以確保在後面的源碼學習中不會一葉障目不見泰山。

二、基於長度編解碼器的具體實現

基於長度的實現有 2 個現成的類:

  • FixedLengthFrameDecoder 基於構造函數中的固定長度。
    該類很簡單,構造方法中,傳入一個整數,該解碼器就會按照這個數字對累積區的字節進行切分。

  • LengthFieldBasedFrameDecoder 基於流中動態的長度
    該類比較復雜。構造函數參數多達 6 個,在構建私有協議棧時大有用處。

三、基於分割符的編解碼器

同樣有 2 個:

  • DelimiterBasedFrameDecoder 用戶提供分割符。
    該類比較簡單,根據用戶提供的分割符對累積區的內容進行分割。性能相對不是那麽完美。

  • LineBasedFrameDecoder 基於換行符,支持多種換行符 \n \r\n 速度相比自定義較快。
    該類使用更簡單,根據換行符進行拆包粘包。

四、google 的 ProtobufDecoder ProtobufEncoder 序列化介紹

Netty 中有很多序列化工具,比如 Jboss 的 Marshalling,同時也支持 Java 標準的序列化。 但我們重點關註 google 的 protobuf 庫。因為它的性能最高。

上面的 4 個解碼器都是基於 ByteToMessageDecoder,將粘包的字節轉為用戶需要的字節。而 ProtobufDecoder 不是繼承自 ByteToMessageDecoder,而是繼承自 MessageToMessageDecoder,名字都不同。MessageToMessageDecoder 的作用是什麽呢?

從名字上看,該類用於將兩個消息進行轉換(比如一種 POJO 轉成另一種)。後面我們將花大篇幅講述這個類庫。

五、其他的

(1) TooLongFrameException

由於 Netty 是一個異步框架,所以需要在字節可以解碼之前在內存中緩沖他們。因此不能讓解碼器緩沖大量的數據以至於耗盡可用的內存。為了解決這個問題,Netty 提供了 TooLongFrameException 類,其將由解碼器在幀超出指定的大小限制時拋出異常。

你可以設置一個最大的閾值,當超過該閾值,這拋出異常。

(2) 寫大型數據的 FileRegion

有時候你可能需要寫一個大型的數據,如果不停的寫入,可能導致 OOM,所以在寫大型數據時,需要準備好處理到遠程節點的連接時慢速連接的情況,這種情況會導致內存釋放的延遲。

我們可以使用 NIO 的零拷貝特性,這種特性消除了將文件內容從文件系統移動到網絡棧的復制過程。而我們所需要做的就是使用一個 FileRegion 接口的實現。

官方定義:

通過支持零拷貝的文件傳輸的 Channel 來發送的文件區域。

六、總結

本文並沒有刨析源碼,主要是針對 Netty 中現有的或者設計的編解碼,序列化等工具做一個介紹,方便後面有條不紊的按照這個路線研究他們的具體實現。

參考:

  1. 《Netty 粘包 & 拆包 & 編碼 & 解碼 & 序列化 介紹》:http://www.cnblogs.com/stateis0/p/9062162.html

每天用心記錄一點點。內容也許不重要,但習慣很重要!

Netty 源碼 ChannelHandler(四)編解碼技術