當前位置:編程學習大全網 - 源碼下載 - 序列化的原理

序列化的原理

XML 序列化的好處在於可讀性好,方便閱讀和調試。但是序列化以後的字節碼文件比較大,而且效率不高,適用於對性能不高,而且 QPS 較低的企業級內部系統之間的數據交換的場景,同時 XML 又具有語言無關性,所以還可以用於異構系統之間的數據交換和協議。比如我們熟知的 webservice,就是采用 XML 格式對數據進行序列化的。XML 序列化/反序列化的實現方式有很多,熟知的方式有 XStream 和 Java 自帶的 XML 序列化和反序列化兩種

JSON(JavaScript Object Notation)是壹種輕量級的數據交換格式,相對於 XML 來說,JSON的字節流更小,而且可讀性也非常好。現在 JSON 數據格式在企業運用是最普遍的JSON 序列化常用的開源工具有很多

這幾種 json 序列化工具中,Jackson 與 fastjson 要比 GSON 的性能要好,但是 Jackson、GSON 的穩定性要比 Fastjson 好。而 fastjson 的優勢在於提供的 api 非常容易使用

Hessian 是壹個支持跨語言傳輸的二進制序列化協議,相對於 Java 默認的序列化機制來說,Hessian 具有更好的性能和易用性,而且支持多種不同的語言

實際上 Dubbo 采用的就是 Hessian 序列化來實現,只不過 Dubbo 對 Hessian 進行了重構,性能更高

Avro 是壹個數據序列化系統,設計用於支持大批量數據交換的應用。它的主要特點有:支持二進制序列化方式,可以便捷,快速地處理大量數據;動態語言友好,Avro 提供的機制使動態語言可以方便地處理 Avro 數據

Kryo 是壹種非常成熟的序列化實現,已經在 Hive、Storm)中使用得比較廣泛,不過它不能跨語言. 目前 dubbo 已經在 2.6 版本支持 kyro 的序列化機制。它的性能要優於之前的hessian2

Protobuf 是 Google 的壹種數據交換格式,它獨立於語言、獨立於平臺。Google 提供了多種語言來實現,比如 Java、C、Go、Python,每壹種實現都包含了相應語言的編譯器和庫文件,Protobuf 是壹個純粹的表示層協議,可以和各種傳輸層協議壹起使用。

Protobuf 使用比較廣泛,主要是空間開銷小和性能比較好,非常適合用於公司內部對性能要求高的 RPC 調用。 另外由於解析性能比較高,序列化以後數據量相對較少,所以也可以應用在對象的持久化場景中但是要使用 Protobuf 會相對來說麻煩些,因為他有自己的語法,有自己的編譯器,如果需要用到的話必須要去投入成本在這個技術的學習中

protobuf 有個缺點就是要傳輸的每壹個類的結構都要生成對應的 proto 文件,如果某個類發生修改,還得重新生成該類對應的 proto 文件

各個序列化技術的性能比較

這個地址有針對不同序列化技術進行性能比較: /eishay/jvm-serializers/wiki

使用 protobuf 開發的壹般步驟是

編寫 proto 文件

數據類型

string / bytes / bool / int32(4 個字節)

/int64/float/double

enum 枚舉類

message 自定義類

修飾符

required 表示必填字段

optional 表示可選字段

repeated 可重復,表示集合

1,2,3,4 需要在當前範圍內是唯壹的,表示順序

生成實體類

實現序列化

輸出結果:10 3 77 105 99 16 -84 2

可以看到,序列化出來的數字基本看不懂,但是序列化以後的數據確實很小,那我們來了解壹下底層的原理

正常來說,要達到最小的序列化結果,壹定會用到壓縮的技術,而 protobuf 裏面用到了兩種

壓縮算法,壹種是 varint,另壹種是 zigzag

-varint

先來看 age=300 這個數字是如何被壓縮的

這兩個字節字節分別的結果是:-84 、2

-84 怎麽計算來的呢? 我們知道在二進制中表示負數的方法,高位設置為 1, 並且是對應數字的二進制取反以後再計算補碼表示(補碼是反碼+1)

所以如果要反過來計算

字符如何轉化為編碼

“Mic”這個字符,需要根據 ASCII 對照表轉化為數字。

M =77、i=105、c=99

所以結果為 77 105 99

這裏的結果為什麽直接就是 ASCII 編碼的值呢?怎麽沒有做壓縮呢?

原因是,varint 是對字節碼做壓縮,但是如果這個數字的二進制只需要壹個字節表示的時候,其實最終編碼出來的結果是不會變化的

還有兩個數字,3 和 16 代表什麽呢?那就要了解 protobuf 的存儲格式了

存儲格式

protobuf 采用 T-L-V 作為存儲方式

tag 的計算方式是 field_number(當前字段的編號) << 3 | wire_type

比如 Mic 的字段編號是 1 ,類型 wire_type 的值為 2 所以 : 1 <<3 | 2 =10

age=300 的字段編號是 2,類型 wire_type 的值是 0, 所以 : 2<<3|0 =16

第壹個數字 10,代表的是 key,剩下的都是 value

負數的存儲

在計算機中,負數會被表示為很大的整數,因為計算機定義負數符號位為數字的最高位,所以如果采用 varint 編碼表示壹個負數,那麽壹定需要 5 個比特位。所以在 protobuf 中通過sint32/sint64 類型來表示負數,負數的處理形式是先采用 zigzag 編碼(把符號數轉化為無符號數),再采用 varint 編碼。

sint32:(n << 1) ^ (n >> 31)

sint64:(n << 1) ^ (n >> 63)

比如存儲壹個(-300)的值

-300

原碼:0001 0010 1100

取反:1110 1101 0011

加 1 :1110 1101 0100

n<<1: 整體左移壹位,右邊補 0 -> 1101 1010 1000

n>>31: 整體右移 31 位,左邊補 1 -> 1111 1111 1111

n<<1 ^ n >>31

1101 1010 1000 ^ 1111 1111 1111 = 0010 0101 0111

十進制: 0010 0101 0111 = 599

varint 算法: 從右往做,選取 7 位,高位補 1/0(取決於字節數)

得到兩個字節

1101 0111 和 0000 0100

-41 和 4

Protocol Buffer 的性能好,主要體現在 序列化後的數據體積小 & 序列化速度快,最終使得傳輸效率高,其原因如下:

  • 上一篇:妳好,刷臉支付代理的相關咨詢
  • 下一篇:找到固態硬盤的工作原理
  • copyright 2024編程學習大全網