使用Netty通信遇到了編解碼問題?試試MessagePack如何解決

使用Netty主要是為了進行網絡通信,而網絡通信就要涉及到傳輸數據,數據是不能直接傳遞的,需要進行一系列處理。java序列化就是其中一種處理方式,但是由於各種各樣的缺點,一般不會用,在這裡我們介紹一個比較優秀的編碼解碼技術MessagePack。

這篇文章是我的《搞懂Netty》系列的第三篇,也是在前兩篇文章的基礎之上進行講解的。我們使用的是Springboot整合的Netty。

一、為什麼不用java序列化

不用java進行序列化,大體上可以歸結為以下幾條。

1、無法跨語言

比如說java編碼後的數據,C++不認識,也不能解碼。

2、性能低

編碼解碼的速度太慢。

3、碼流太大

編碼之後增加了很多其他的數據,佔據空間。

4、開發難度大

對開發人員不友好。

以上幾條隨便一條都是極大地缺點,因此我們在這裡先介紹其中一種編碼和解碼的技術,叫MessagePack。為什麼要使用這個呢?因為以上四條都能很好的解決,所以要用它。當然還有很多其他的優秀技術,比如Protobuf等,以後用到的時候再說,我目前的項目由於是C++和java通信,因此選擇了這個框架。

既然這個MessagePack這麼好,下面我們就直接來看如何在Netty中使用。

二、MessagePack編解碼

1、前提

在前面已經提到了,使用的是Springboot開發的,因此我們首先添加依賴,如果你沒有使用maven直接下載相關的jar包導入也可以。

另外,本實例代碼在Idea中已經運行通過,而且你最好建兩個moducle跑一下,客戶端一個,服務端一個。

使用Netty通信遇到了編解碼問題?試試MessagePack如何解決

導入了jar包之後,下面我們就來看看客戶端和服務端的代碼。

客戶端和服務端實現的功能很簡單,那就是在建立連接的時候,客戶端給服務端發送10個Student對象數據。

2、服務端代碼

(1)定義pojo對象Student類

使用Netty通信遇到了編解碼問題?試試MessagePack如何解決

在這裡有兩點需要我們注意:

第一點:添加@Message註解,表明可以對此進行序列化。

第二點:一定要有一個默認的構造器。在0.7的版本中據說已經修復了,不過形式上看起來有點麻煩。而且你用0.7的版本之後,上面的@Message註解不再有了。比較麻煩。還不如記住這兩條,有註解和默認構造器即可。

(2)編碼器MsgPackEncoder

使用Netty通信遇到了編解碼問題?試試MessagePack如何解決

這裡面的代碼很簡單,只有三行,新建一個MessagePack對象,將對象o轉化為byte保存在ByteBuf中。

(3)解碼器MsgPackDecoder

使用Netty通信遇到了編解碼問題?試試MessagePack如何解決

在這裡我們繼承了MessageToMessageDecoder,然後重寫了decode方法,在裡面通過MessagePack再將緩衝區的byte轉化為對象。

(4)服務端Server類

使用Netty通信遇到了編解碼問題?試試MessagePack如何解決

在這裡面基本上全是套路代碼,你直接拿來用即可,裡面核心的就是try代碼塊。我們最關注的是下面這一部分:

使用Netty通信遇到了編解碼問題?試試MessagePack如何解決

我們來分析一下這些代碼:

第一個類:LengthFieldBasedFrameDecoder

參數(1)maxFrameLength:表示的是包的最大長度,超出會做一些特殊處理。

參數(2)lengthFieldOffset:指定長度域的偏移量,表示跳過指定長度個字節才是長度域;

參數(3)lengthFieldLength:本數據幀的長度;

參數(4)lengthAdjustment:該字段加長度字段等於數據幀的長度,包體長度調整的大小。

參數(5)initialBytesToStrip:獲取完一個完整的數據包之後,忽略前面的指定的位數個字節。

第二個類:MsgPackDecoder

這個類就是我們更改創建的解碼類

第三個類:LengthFieldPrepender

客戶端使用LengthFieldPrepender給數據添加報文頭Length字段,接受方使用LengthFieldBasedFrameDecoder進行解碼。

第四個類:MsgPackEncoder

這是我們剛剛創建的編碼器類。

第五個類:ServerUAVHandler

這是我們的業務處理類,在這個類中我們處理客戶端的各種事件。

從上面五個類可以看到,LengthFieldBasedFrameDecoder和LengthFieldPrepender,可以自動屏蔽TCP底層的拆包和粘包問題,因此在這裡加上,也是為了解決粘包和拆包問題。

下面看看我們的ServerUAVHandler類,著重看看我們的業務處理類

(5)handler類

使用Netty通信遇到了編解碼問題?試試MessagePack如何解決

這裡面很簡單,在channelActive方法可以處理客戶端的連接請求,在channelRead裡面可以讀取客戶端發來的數據。我們使用counter變量記錄客戶端發了幾條數據,不過有個坑需要我們去注意。

注意:注意:List<>中一定得是Object類,而不能是我們的Student。

現在我們的服務端基本上就寫完了,寫完之後應該是這樣的結構:

使用Netty通信遇到了編解碼問題?試試MessagePack如何解決

接下來就可以看看客戶端了。

3、客戶端代碼

(1)定義pojo對象Student類:和服務端的Student類一樣

(2)編碼器MsgPackEncoder:和服務端的一樣

(3)解碼器MsgPackDecoder:和服務端的一樣

(4)客戶端client類

使用Netty通信遇到了編解碼問題?試試MessagePack如何解決

由於和服務器的一樣,我們也已經分析了,因此來看看裡面不一樣的clientHandler類實現吧。

使用Netty通信遇到了編解碼問題?試試MessagePack如何解決

在這裡面客戶端和服務端建立連接的時候就發送是個對象數據。此時服務端肯定也是會收到10個。客戶端也寫完了,寫完之後應該是下面的結構:

使用Netty通信遇到了編解碼問題?試試MessagePack如何解決

4、試驗驗證

現在先運行服務端,再運行客戶端看看輸出結果。這裡給出服務端。

使用Netty通信遇到了編解碼問題?試試MessagePack如何解決

我們會看到服務端接收到了10調數據,而且沒有粘包的現象。

對於這個MessagePack的方式和其他的框架綜合對比之後,性能也不算是最優的,但是相對於java序列化機制那就好太多了,我之前曾經寫了一篇protobuf的文章,有興趣可以在我主頁看看。

現在我們不僅可以解決粘包拆包的問題,而且也能處理編解碼的問題,一個新問題又隨之出來了,服務端給客戶端主動推送消息該怎麼辦呢?這時候傳統的HTTP協議,顯然已經不能滿足我們的需求。缺點太多了。於是有一個新的技術隨之而生,叫做websocket,下一篇再敘述。感謝支持。


分享到:


相關文章: