使用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如何解決](http://p2.ttnews.xyz/loading.gif)
導入了jar包之後,下面我們就來看看客戶端和服務端的代碼。
客戶端和服務端實現的功能很簡單,那就是在建立連接的時候,客戶端給服務端發送10個Student對象數據。
2、服務端代碼
(1)定義pojo對象Student類
![使用Netty通信遇到了編解碼問題?試試MessagePack如何解決](http://p2.ttnews.xyz/loading.gif)
在這裡有兩點需要我們注意:
第一點:添加@Message註解,表明可以對此進行序列化。
第二點:一定要有一個默認的構造器。在0.7的版本中據說已經修復了,不過形式上看起來有點麻煩。而且你用0.7的版本之後,上面的@Message註解不再有了。比較麻煩。還不如記住這兩條,有註解和默認構造器即可。
(2)編碼器MsgPackEncoder
這裡面的代碼很簡單,只有三行,新建一個MessagePack對象,將對象o轉化為byte保存在ByteBuf中。
(3)解碼器MsgPackDecoder
在這裡我們繼承了MessageToMessageDecoder,然後重寫了decode方法,在裡面通過MessagePack再將緩衝區的byte轉化為對象。
(4)服務端Server類
在這裡面基本上全是套路代碼,你直接拿來用即可,裡面核心的就是try代碼塊。我們最關注的是下面這一部分:
我們來分析一下這些代碼:
第一個類:LengthFieldBasedFrameDecoder
參數(1)maxFrameLength:表示的是包的最大長度,超出會做一些特殊處理。
參數(2)lengthFieldOffset:指定長度域的偏移量,表示跳過指定長度個字節才是長度域;
參數(3)lengthFieldLength:本數據幀的長度;
參數(4)lengthAdjustment:該字段加長度字段等於數據幀的長度,包體長度調整的大小。
參數(5)initialBytesToStrip:獲取完一個完整的數據包之後,忽略前面的指定的位數個字節。
第二個類:MsgPackDecoder
這個類就是我們更改創建的解碼類
第三個類:LengthFieldPrepender
客戶端使用LengthFieldPrepender給數據添加報文頭Length字段,接受方使用LengthFieldBasedFrameDecoder進行解碼。
第四個類:MsgPackEncoder
這是我們剛剛創建的編碼器類。
第五個類:ServerUAVHandler
這是我們的業務處理類,在這個類中我們處理客戶端的各種事件。
從上面五個類可以看到,LengthFieldBasedFrameDecoder和LengthFieldPrepender,可以自動屏蔽TCP底層的拆包和粘包問題,因此在這裡加上,也是為了解決粘包和拆包問題。
下面看看我們的ServerUAVHandler類,著重看看我們的業務處理類
(5)handler類
這裡面很簡單,在channelActive方法可以處理客戶端的連接請求,在channelRead裡面可以讀取客戶端發來的數據。我們使用counter變量記錄客戶端發了幾條數據,不過有個坑需要我們去注意。
注意:注意:List<>中一定得是Object類,而不能是我們的Student。現在我們的服務端基本上就寫完了,寫完之後應該是這樣的結構:
接下來就可以看看客戶端了。
3、客戶端代碼
(1)定義pojo對象Student類:和服務端的Student類一樣
(2)編碼器MsgPackEncoder:和服務端的一樣
(3)解碼器MsgPackDecoder:和服務端的一樣
(4)客戶端client類
由於和服務器的一樣,我們也已經分析了,因此來看看裡面不一樣的clientHandler類實現吧。
在這裡面客戶端和服務端建立連接的時候就發送是個對象數據。此時服務端肯定也是會收到10個。客戶端也寫完了,寫完之後應該是下面的結構:
4、試驗驗證
現在先運行服務端,再運行客戶端看看輸出結果。這裡給出服務端。
我們會看到服務端接收到了10調數據,而且沒有粘包的現象。
對於這個MessagePack的方式和其他的框架綜合對比之後,性能也不算是最優的,但是相對於java序列化機制那就好太多了,我之前曾經寫了一篇protobuf的文章,有興趣可以在我主頁看看。
現在我們不僅可以解決粘包拆包的問題,而且也能處理編解碼的問題,一個新問題又隨之出來了,服務端給客戶端主動推送消息該怎麼辦呢?這時候傳統的HTTP協議,顯然已經不能滿足我們的需求。缺點太多了。於是有一個新的技術隨之而生,叫做websocket,下一篇再敘述。感謝支持。
閱讀更多 Java的架構師技術棧 的文章