protocolbuffer(以下簡稱PB)是google 的一種數據交換的格式,它獨立於語言,獨立於平臺。google 提供了多種語言的實現:java、c#、c++、go 和 python,每一種實現都包含了相應語言的編譯器以及庫文件。由於它是一種二進制的格式,比使用 xml 進行數據交換快許多。可以把它用於分佈式應用之間的數據通信或者異構環境下的數據交換。作爲一種效率和兼容性都很優秀的二進制數據傳輸格式,可以用於諸如網絡傳輸、配置文件、數據存儲等諸多領域。
1.ProtoBuf協議說明
proto文件定義了協議數據中的實體結構(message ,field)
- 關鍵字message: 代表了實體結構,由多個消息字段(field)組成。
- 消息字段(field): 包括數據類型、字段名、字段規則、字段唯一標識、默認值
- 數據類型:如下圖所示
- 字段規則:
required:必須初始化字段,如果沒有賦值,在數據序列化時會拋出異常
optional:可選字段,可以不必初始化。
repeated:數據可以重複(相當於java 中的Array或List)
字段唯一標識:序列化和反序列化將會使用到。
- 默認值:在定義消息字段時可以給出默認值。
數據類型
2.ProtoBuf的使用流程
1.定義.proto文件
首先我們需要編寫一個 proto 文件,定義我們程序中需要處理的結構化數據,在 protobuf 的術語中,結構化數據被稱爲 Message。proto 文件非常類似 java 或者 C 語言的數據定義。下面代碼顯示了例子應用中的 proto 文件內容:
package lm;
message helloworld
{
required int32 id = 1; // ID
required string str = 2; // str
optional int32 opt = 3; //optional field
}
一個比較好的習慣是認真對待 proto 文件的文件名。比如將命名規則定於如下:
packageName.MessageName.proto
在上例中,package 名字叫做 lm,定義了一個消息 helloworld,該消息有三個成員,類型爲 int32 的 id,另一個爲類型爲 string 的成員 str。opt 是一個可選的成員,即消息中可以不包含該成員。
2.編譯.proto文件
寫好 proto 文件之後就可以用 Protobuf 編譯器將該文件編譯成目標語言了。可以根據不同的語言來選擇不同的編譯方式
在項目中我使用到的是分別是JavaScript和Java,前者是客戶端腳本語言,後者服務端語言。將proto文件編譯生成java文件,這裏有一個很好的案例:《Protobuf協議的Java應用例子》,當然在項目中一般是不會這麼幹的,
3.序列化和反序列化
public class Test {
public static void main(String[] args) throws IOException {
//模擬將對象轉成byte[],方便傳輸
PersonEntity.Person.Builder builder = PersonEntity.Person.newBuilder();
builder.setId(1);
builder.setName("ant");
builder.setEmail("[email protected]");
PersonEntity.Person person = builder.build();
System.out.println("before :"+ person.toString());
System.out.println("===========Person Byte==========");
for(byte b : person.toByteArray()){
System.out.print(b);
}
System.out.println();
System.out.println(person.toByteString());
System.out.println("================================");
//模擬接收Byte[],反序列化成Person類
byte[] byteArray =person.toByteArray();
Person p2 = Person.parseFrom(byteArray);
System.out.println("after :" +p2.toString());
}
}
輸出結果圖
從上面可以總結出protobuf的使用過程可以分爲以下三個,準備好數據,通過build()方法來組裝成protobuf包,然後通過toByteArray()來將protobuf轉換成二進制序列流文件(序列化)。
反序列化的過程剛好與之相反,接收到的二進制數據轉換成二進制數組byte[],然後調用protobuf的parseFrom()方法即可實現反序列化。
序列化流程圖
3.protoBuf數據協議的優勢
- 平臺無關,語言無關,可擴展;
- 提供了友好的動態庫,使用簡單;
- 解析速度快,比對應的XML快約20-100倍;
- 序列化數據非常簡潔、緊湊,與XML相比,其序列化之後的數據量約爲1/3到1/10。
說明:
數據量小是因爲,Protobuf 序列化後所生成的二進制消息非常緊湊,這得益於 Protobuf 採用的非常巧妙的little-endian編碼方法。
轉換速度快。首先我們來了解一下 XML 的封解包過程。XML 需要從文件中讀取出字符串,再轉換爲 XML 文檔對象結構模型。之後,再從 XML 文檔對象結構模型中讀取指定節點的字符串,最後再將這個字符串轉換成指定類型的變量。這個過程非常複雜,其中將 XML 文件轉換爲文檔對象結構模型的過程通常需要完成詞法文法分析等大量消耗 CPU 的複雜計算。
反觀 Protobuf,它只需要簡單地將一個二進制序列,按照指定的格式讀取到 C++ 對應的結構類型中就可以了。從上一節的描述可以看到消息的 decoding 過程也可以通過幾個位移操作組成的表達式計算即可完成。速度非常快。