protobuf數據描述語言


1.簡介

 

Protocol Buffers是Google開發的一種數據描述語言,能夠將數據進行序列化,可用於數據存儲、通信協議等方面。

可以理解成更快、更簡單、更小的JSON或者XML,區別在於Protocol Buffers是二進制格式,而JSON和XML是文本格式。

相對於XML,Protocol Buffers有如下幾個優點:

1.簡潔。

2.體積小,消息大小只有XML的1/10到1/3。

3.速度快,解析速度比XML快20~100倍。

4.使用Protocol Buffers的編譯器,可以生成更容易在編程中使用的數據訪問代碼。

5.更好的兼容性,Protocol Buffers設計的一個原則就是要能夠很好的支持向下或向上兼容。

 

使用不同的數據描述語言序列化后的字節個數比對:

 

使用不同的數據描述語言進行序列化以及反序列化的響應時間比對:

 

 

*數據在網絡進行傳輸時要經歷三個階段: 發送方對數據進行序列化、網絡中傳輸、接收方反序列化。

將對象序列化成protobuf、xml、json結構時,protobuf所占的字節數量最少、有效數據的比重最大、總數據最少,因此決定了數據在網絡進行傳輸時所耗費的時間最少。

將對象序列化成protobuf、xml、json結構以及反序列化成對象時,protobuf所耗費的時間最少。

 

結論:數據使用protobuf序列化格式能夠大大提高生產效率(服務的響應時間)。 

 

 

2.proto文件的語法規則 

 

字段類型

 

*目前有v2、v3版本,不同版本的語法稍微有些不同,會額外進行說明,以下是v2版本的語法規則。

 

2.1 消息

 

1.使用message關鍵字定義消息,並指定消息的名稱(取一個有意義的名字)

2.指定字段的類型和名稱

3.添加字段的約束

4.定義字段的編號(從1開始,其中19000~19999被Protocol Buffers作為保留字段)

 

 最基本的message

message User{ required int32 id = 1; required string username = 2; required string password = 3; optional string email = 4; }

 

字段約束

required指定該字段必須賦值。

optional表示該字段允許為空,可以使用[default]指定默認值,如果沒有指定默認值則會使用字段類型的默認值。

  • 對於strings ,默認是一個空string。
  • 對於bytes ,默認是一個空的bytes。
  • 對於bools ,默認是false。
  • 對於數值類型 ,默認是0。
  • 對於枚舉,默認是第一個定義的枚舉值,必須為0。

repeated指定字段為集合。

oneof指定一組字段中必須有一個字段要賦值。

 

*在一個proto文件中可以同時定義多個message類型,生成代碼時根據生成代碼的目標語言不同,處理的方式不太一樣( 對於Java, 每個proto文件都生成一個類,即一個.java文件,每個message、enum類型都是該類的靜態內部類 )

message User{ required int32 id = 1; //username或email之間必須有一個字段要賦值
 oneof login{ string username = 3; string email = 4; } required string password = 2; } message Admin{ required int32 id = 1; required string username = 2; required string password = 3; }

 

*可以指定字段的類型為其他的message類型。

message Course{ required User user = 1; required string cour_name = 2; } message User{ required int32 id = 1; required string username = 2; required string password = 3; optional string email = 4; }

 

*在proto文件中支持類型的嵌套,即定義的message類型僅作為包含其message類型的字段類型( 此時Course靜態內部類中包含User靜態內部類 )

message Course{ message User{ required int32 id = 1; required string username = 2; required string password = 3; optional string email = 4; } required User user = 1; required string cour_name = 2; }

 

*使用extensions關鍵字預留消息類型的字段編號,通過extend關鍵字繼續定義。

message User{ //30~100編號為User類型私有.
    extensions 30 to 100 } extend User{ required int32 id = 1; required string username = 2; required string password = 3; optional string email = 4; }

 

2.2 枚舉

 

1.使用enum關鍵字定義枚舉,並指定枚舉的名稱(取一個有意義的名字)

2.設置枚舉可能包含的值並定義編號(從1開始,其中19000~19999被Protocol Buffers作為保留字段)

 

最基本的枚舉:

enum Course{ Chinese = 1; Mathematics = 2; English = 3; }

 

*可以使用import關鍵字導入其他proto文件。

*可以使用option java_package設置生成java類的包名。

*可以使用option java_outer_classname設置生成java類的類名。

import "other.proto"
option java_package = "com.zht.protobuf";
option java_outer_classname = "UserModel";
message User{
    required int32 id = 1;
    required string username = 2;
    required string password = 3;
    optional string email = 4;
}

 

 

3.proto2與proto3的不同

 

1.proto文件的第一行必須使用syntax屬性指定使用的protobuf版本:proto2、proto3。

2.移除了 “required” 字段約束。

3.“optional”字段約束改名為 “singular”。

4.在 proto2 中, "optional" 約束可以使用 default 指定字段的默認值(不指定也不賦值則跟隨系統), 在 proto3 中, 字段的默認值只能根據字段類型由系統決定。

*字段被設置為默認值時, 該字段不會被序列化, 提高效率。

5.枚舉類型的第一個字段的編號必須為 0 。

 

 

4.protobuf的使用

 

1.環境的准備

 

在github下載對應操作環境的protobuf工具包: https://github.com/google/protobuf/releases

windows用戶選擇: protoc-3.5.1-win32.zip

解壓后配置環境變量PATH,使其在上下文能直接搜索 protoc.exe。

 

2.編寫.proto文件

 

E:\proto\user.proto

內容如下:

#v3版本需要在proto文件的第一行使用syntax屬性指定proto文件使用的語法的版本
syntax = "proto2"; option java_package = "com.zht.protobuf"; option java_outer_classname = "UserModel"; message User{ required int32 id = 1; required string username = 2; required string password = 3; optional string email = 4; }

 

3.使用protoc.exe命令生成實體

 

protoc.exe -I [proto文件所在目錄] --java_out  [JAVA類存放目錄]  [proto文件絕對路徑]

 

 

 

4.將實體放入工程進行實體的構造和賦值

 

將實體放入工程:

 

構造並且賦值:

 

public class Main {

    public static void main(String[] args) throws InvalidProtocolBufferException {
        //獲取構造器並進行賦值
        UserModel.User.Builder builder = UserModel.User.newBuilder();
        builder.setId(1);
        builder.setUsername("zhuanght");
        builder.setPassword("123456");
        builder.setEmail("aiuzht119@163.com");
        
        //獲取實體
        UserModel.User user = builder.build();
        
        System.out.println("源數據:\r"+ user.toString());
        System.out.println("序列化后:"+Arrays.toString(user.toByteArray()));

        //模擬接收Byte[],反序列化成User實體
        byte[] data =user.toByteArray();
        User u = User.parseFrom(data);
        System.out.println("\r解析:\r" +u.toString());
    }
    
}

 

 

 

 

打印結果 :

源數據:
id: 1
username: "zhuanght"
password: "123456"
email: "aiuzht119@163.com"

序列化后:[8, 1, 18, 8, 122, 104, 117, 97, 110, 103, 104, 116, 26, 6, 49, 50, 51, 52, 53, 54, 34, 17, 97, 105, 117, 122, 104, 116, 49, 49, 57, 64, 49, 54, 51, 46, 99, 111, 109]

解析:
id: 1
username: "zhuanght"
password: "123456"
email: "aiuzht119@163.com"

  

5.獲取序列化后的字節數組在網絡中進行傳輸

 

 

5.protobuf實例與json進行轉換

 

導入相關依賴

<dependency>
    <groupId>com.googlecode.protobuf-java-format</groupId>
    <artifactId>protobuf-java-format</artifactId>
    <version>1.4</version>
</dependency>

 

protobuf實例序列化為json格式

//user為上面例子的UserModel.User消息實例
String json = JsonFormat.printToString(user)

 

json序列化為protobuf實例

//builder為上面例子的UserModel.User.Builder構造器實例
JsonFormat.merge(json ,builder); //此時再使用構建器創建的實例就包含轉換后的數據
builder.build();

 


免責聲明!

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



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