一.Protocol Buffers應用場景
1.序列化的應用場景
PB要解決的是序列化的問題,所以我們從序列化的角度去理解PB。
序列化就是將一個數據結構(通常是對象)轉換爲二進制的行式。
反序列化則是將二進制數據轉換爲數據結構或對象。
因爲在內存中最容易處理的數據行式是對象,而在存儲和通信時最容易處理的是二進制數據,因此在面臨存儲對象和傳輸對象的場景時,就會需要解決序列化和序列化的問題。
就像ORM解決面向對象和關係數據庫的匹配問題一樣,序列化也是解決面向對象和存儲/通信的匹配問題。
2.序列化面臨的問題
2.1.定義目標對象
此步驟包括兩個問題:1.定義哪些對象可以被序列化; 2.定義對象的哪些字段可以被序列化
假設我們要實現一個自定義協議,報文和通信部分都要自己完成. 那麼其實報文的組裝和解析就是一個序列化/反序列化的過程
此時,比較簡單的實現,就是讓需要序列化的對象實現以下接口
interface ICode_Decode {
byte[] serialize(object o);
Objcec deSerialize(byte[] bytes);
}
這種解決方式對於個性化的序列化要求比較適用,但是對於通用的序列化則不太現實,因爲不可能讓所有需要序列化的對象都實現一遍這兩個方法.
所謂通用的序列化,就是把對象理解爲由一系列屬性名--屬性值組成的Key--Value對.
①JDK和.net Framework對於對象的序列化都有其內部的實現。大致的實現方式都是通過接口或者特性來對類及其字段進行標註,然後進行序列化,因爲都是自帶的內部實現,因此這些實現對類本身的特性很熟悉,序列化時會有各自語言特有的內容。
②跨平臺的XML和json序列化實現。
③根據配置文件生成類。即Protocol Buffers的實現。
syntax = "proto3";
package mypackname;
option java_package "org.feng.wildland";
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
Corpus corpus = 4;
}
enum Corpus {
UNIVERSAL = 0;
WEB = 1;
IMAGES = 2;
LOCAL = 3;
NEWS = 4;
PRODUCTS = 5;
VIDEO = 6;
}
以上文件即爲.proto的一個示例,通過代碼生成工具,將會生成 org.feng.wildland.mypackagename.SearchRequest的類,並生成相應的用於序列化的方法。
2.2編碼方式
①如XML和Json,字符串的編碼方式
②二進制的編碼方式
通常生成的json字符串是自包含的。但是pb不是。
pb序列化後的數據中不包括變量名,而只有變量編號,即.proto文件中每個變量後面定義的數字
pb編碼後,第一個字節包括變量編號+變量類型,後面是變量的值;如果變量類型爲不定長的類型,那麼第二個字節是變量長度。
2.3版本號問題
我們的代碼時不斷迭代更新的,那麼爲對象添加刪除字段也是很正常的事情。
①java中需要序列化的類需要添加private static final long serialVersionUID ,如果反序列化時,發現該字段不一致,則報錯,如果該字段一致,但是新添了字段,那麼該字段設置爲默認值;
@Protocol Buffers中,沒有 版本號的概念。在更新了代碼後,再反序列化,會遇到以下情況
如果新添了字段,則該字段爲默認值;如果新添了repeat 類型字段,則爲空
如果刪除了字段,則爲不可識別的字段