詳解通訊資料協議ProtoBuf

weixin_34320159發表於2018-03-07

protocolbuffer(以下簡稱PB)是google 的一種資料交換的格式,它獨立於語言,獨立於平臺。google 提供了多種語言的實現:java、c#、c++、go 和 python,每一種實現都包含了相應語言的編譯器以及庫檔案。由於它是一種二進位制的格式,比使用 xml 進行資料交換快許多。可以把它用於分散式應用之間的資料通訊或者異構環境下的資料交換。作為一種效率和相容性都很優秀的二進位制資料傳輸格式,可以用於諸如網路傳輸、配置檔案、資料儲存等諸多領域。

1.ProtoBuf協議說明

proto檔案定義了協議資料中的實體結構(message ,field)

  • 關鍵字message: 代表了實體結構,由多個訊息欄位(field)組成。
  • 訊息欄位(field): 包括資料型別、欄位名、欄位規則、欄位唯一標識、預設值
  • 資料型別:如下圖所示
  • 欄位規則:

required:必須初始化欄位,如果沒有賦值,在資料序列化時會丟擲異常
optional:可選欄位,可以不必初始化。
repeated:資料可以重複(相當於java 中的Array或List)
欄位唯一標識:序列化和反序列化將會使用到。

  • 預設值:在定義訊息欄位時可以給出預設值。
8926909-4d5e55b649406dba.png
資料型別

【protobuf使用和介紹】

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("ghb@soecode.com");
        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());
    }
}
8926909-8133076579a7907e.png
輸出結果圖

從上面可以總結出protobuf的使用過程可以分為以下三個,準備好資料,通過build()方法來組裝成protobuf包,然後通過toByteArray()來將protobuf轉換成二進位制序列流檔案(序列化)。
反序列化的過程剛好與之相反,接收到的二進位制資料轉換成二進位制陣列byte[],然後呼叫protobuf的parseFrom()方法即可實現反序列化。


8926909-240ebf3df00a43a3.png
序列化流程圖

3.protoBuf資料協議的優勢

  • 平臺無關,語言無關,可擴充套件;
  • 提供了友好的動態庫,使用簡單;
  • 解析速度快,比對應的XML快約20-100倍;
  • 序列化資料非常簡潔、緊湊,與XML相比,其序列化之後的資料量約為1/3到1/10。

說明:
資料量小是因為,Protobuf 序列化後所生成的二進位制訊息非常緊湊,這得益於 Protobuf 採用的非常巧妙的little-endian編碼方法。

轉換速度快。首先我們來了解一下 XML 的封解包過程。XML 需要從檔案中讀取出字串,再轉換為 XML 文件物件結構模型。之後,再從 XML 文件物件結構模型中讀取指定節點的字串,最後再將這個字串轉換成指定型別的變數。這個過程非常複雜,其中將 XML 檔案轉換為文件物件結構模型的過程通常需要完成詞法文法分析等大量消耗 CPU 的複雜計算。

反觀 Protobuf,它只需要簡單地將一個二進位制序列,按照指定的格式讀取到 C++ 對應的結構型別中就可以了。從上一節的描述可以看到訊息的 decoding 過程也可以通過幾個位移操作組成的表示式計算即可完成。速度非常快。

《Google Protocol Buffer 的使用和原理》

相關文章