Java網絡通信 —— 序列化問題


Java序列化的目的主要有兩個:

1.網絡傳輸

2.對象持久化

當選行遠程跨迸程服務調用時,需要把被傳輸的Java對象編碼為字節數組或者ByteBuffer對象。而當遠程服務讀取到ByteBuffer對象或者字節數組時,需要將其解碼為發送時的Java 對象。這被稱為Java對象編解碼技術。

Java序列化僅僅是Java編解碼技術的一種,由於它的種種缺陷,衍生出了多種編解碼技術和框架

Java序列化的缺點

Java序列化從JDK1.1版本就已經提供,它不需要添加額外的類庫,只需實現java.io.Serializable並生成序列ID即可,因此,它從誕生之初就得到了廣泛的應用。

但是在遠程服務調用(RPC)時,很少直接使用Java序列化進行消息的編解碼和傳輸,這又是什么原因呢?下面通過分析.Tava序列化的缺點來找出答案。

1  無法跨語言

對於跨進程的服務調用,服務提供者可能會使用C十+或者其他語言開發,當我們需要和異構語言進程交互時Java序列化就難以勝任。由於Java序列化技術是Java語言內部的私有協議,其他語言並不支持,對於用戶來說它完全是黑盒。對於Java序列化后的字節數組,別的語言無法進行反序列化,這就嚴重阻礙了它的應用。

2  序列化后的碼流太大

通過一個實例看下Java序列化后的字節數組大小。

3序列化性能太低

無論是序列化后的碼流大小,還是序列化的性能,JDK默認的序列化機制表現得都很差。因此,我們邊常不會選擇Java序列化作為遠程跨節點調用的編解碼框架。

 

序列化 – 內置和第三方的MessagePack實戰

內置

Netty內置了對JBoss MarshallingProtocol Buffers的支持

 

集成第三方MessagePack實戰(LengthFieldBasedFrame詳解)

LengthFieldBasedFrame詳解

maxFrameLength:表示的是包的最大長度,

lengthFieldOffset:指的是長度域的偏移量,表示跳過指定個數字節之后的才是長度域;

lengthFieldLength:記錄該幀數據長度的字段,也就是長度域本身的長度;

lengthAdjustment:長度的一個修正值,可正可負;

initialBytesToStrip:從數據幀中跳過的字節數,表示得到一個完整的數據包之后,忽略多少字節,開始讀取實際我要的數據

failFast:如果為true,則表示讀取到長度域,TA的值的超過maxFrameLength,就拋出一個 TooLongFrameException,而為false表示只有當真正讀取完長度域的值表示的字節之后,才會拋出 TooLongFrameException,默認情況下設置為true,建議不要修改,否則可能會造成內存溢出。

數據包大小: 14B = 長度域2B + "HELLO, WORLD"(單詞HELLO+一個逗號+一個空格+單詞WORLD

 

 

長度域的值為12B(0x000c)。希望解碼后保持一樣,根據上面的公式,參數應該為:

1. lengthFieldOffset = 0

2. lengthFieldLength = 2

3. lengthAdjustment  無需調整

4. initialBytesToStrip = 0 - 解碼過程中,沒有丟棄任何數據

 

數據包大小: 14B = 長度域2B + "HELLO, WORLD"

 

 

長度域的值為12B(0x000c)。解碼后,希望丟棄長度域2B字段,所以,只要initialBytesToStrip = 2即可。

1. lengthFieldOffset = 0

2. lengthFieldLength = 2

3. lengthAdjustment  無需調整

4. initialBytesToStrip = 2 解碼過程中,丟棄2個字節的數據

 

數據包大小: 14B = 長度域2B + "HELLO, WORLD"。長度域的值為14(0x000E)

 

 

長度域的值為14(0x000E),包含了長度域本身的長度。希望解碼后保持一樣,根據上面的公式,參數應該為:

1. lengthFieldOffset = 0

2. lengthFieldLength = 2

3. lengthAdjustment = -2  因為長度域為14,而報文內容為12,為了防止讀取報文超出報文本體,和將長度字段一起讀取進來,需要告訴netty,實際讀取的報文長度比長度域中的要少212-14=-2

4. initialBytesToStrip = 0 - 解碼過程中,沒有丟棄任何數據

 

在長度域前添加2個字節的Header。長度域的值(0x00000C) = 12。總數據包長度: 17=Header(2B) + 長度域(3B) + "HELLO, WORLD" 

 

 

長度域的值為12B(0x000c)。編碼解碼后,長度保持一致,所以initialBytesToStrip = 0。參數應該為:

1. lengthFieldOffset = 2

2. lengthFieldLength = 3

3. lengthAdjustment = 0無需調整

4. initialBytesToStrip = 0 - 解碼過程中,沒有丟棄任何數據

 

Header與長度域的位置換了。總數據包長度: 17=長度域(3B) + Header(2B) + "HELLO, WORLD"

 

 

長度域的值為12B(0x000c)。編碼解碼后,長度保持一致,所以initialBytesToStrip = 0。參數應該為:

1. lengthFieldOffset = 0

2. lengthFieldLength = 3

3. lengthAdjustment = 2  因為長度域為12,而報文內容為12,但是我們需要把Header的值一起讀取進來,需要告訴netty,實際讀取的報文內容長度比長度域中的要多212+2=14

4. initialBytesToStrip = 0 - 解碼過程中,沒有丟棄任何數據

帶有兩個headerHDR1 丟棄,長度域丟棄,只剩下第二個header和有效包體,這種協議中,一般HDR1可以表示magicNumber,表示應用只接受以該magicNumber開頭的二進制數據,rpc里面用的比較多。總數據包長度: 16=HDR1(1B)+長度域(2B) +HDR2(1B) + "HELLO, WORLD"

 

 

長度域的值為12B(0x000c)

1. lengthFieldOffset = 1 (HDR1的長度)

2. lengthFieldLength = 2

3. lengthAdjustment =1  因為長度域為12,而報文內容為12,但是我們需要把HDR2的值一起讀取進來,需要告訴netty,實際讀取的報文內容長度比長度域中的要多112+1=13

4. initialBytesToStrip = 3  丟棄了HDR1和長度字段

帶有兩個headerHDR1 丟棄,長度域丟棄,只剩下第二個header和有效包體總數據包長度: 16=HDR1(1B)+長度域(2B) +HDR2(1B) + "HELLO, WORLD"

 

 

長度域的值為16B(0x0010)長度為2HDR1的長度為1HDR2的長度為1,包體的長度為121+1+2+12=16

1. lengthFieldOffset = 1

2. lengthFieldLength = 2

3. lengthAdjustment = -3因為長度域為16,需要告訴netty,實際讀取的報文內容長度比長度域中的要 少313-16= -3

4. initialBytesToStrip = 3丟棄了HDR1和長度字段

MessagePack集成

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM