【Protocol Buffer】Protobuf的序列化和反序列化

前言

目前主流的幾種數據交互的格式主要有xml、json、protobuf等等。一般的web項目中,最流行的主要還是json。因爲瀏覽器對於json數據支持非常好,有很多內建的函數支持。xml數據格式在webservice中應用最爲廣泛,但是相比於json,它的數據更加冗餘,因爲需要成對的閉合標籤。json使用了鍵值對的方式,不僅壓縮了一定的數據空間,同時也具有可讀性。protobuf是後起之秀,是谷歌開源的一種數據格式,適合高性能,對響應速度有要求的數據傳輸場景。因爲profobuf是二進制數據格式,需要編碼和解碼。數據本身不具有可讀性。因此只能反序列化之後得到真正可讀的數據。

protobuf具有以下幾個主要的優點:

1:序列化後體積相比Json和XML很小,適合網絡傳輸
2:支持跨平臺多語言
3:消息格式升級和兼容性還不錯
4:序列化反序列化速度很快,快於Json的處理速速

在網絡中,關於數據的傳輸使用方面的性能主要有兩個方面:數據的網絡傳輸消耗的時間和數據的生成和解析所消耗的時間。
關於json和protobuf的對比,可以參考其他博客:
https://tech.meituan.com/serialization_vs_deserialization.html
http://www.infoq.com/cn/articles/json-is-5-times-faster-than-protobuf

在一個需要大量的數據傳輸的場景中,如果數據量很大,那麼選擇protobuf可以明顯的減少數據量,減少網絡IO,從而減少網絡傳輸所消耗的時間。

那麼,學習protobuf的第一步就是如何安裝和配置protobuf?

1,下載protobuf的jar包以及編譯器

jar包地址:

http://mvnrepository.com/artifact/com.google.protobuf/protobuf-java

如果想自己打包,源碼地址:

https://github.com/google/protobuf

編譯器地址:

https://github.com/google/protobuf/releases

上面編譯器有exe形式的編譯程序。

這裏寫圖片描述

壓縮包中有一個protoc.exe,這個是protobuf的編譯器

2,配置環境變量

如果你需要在任意目錄下都可以調用protobuf的編譯器,可以在環境變量中配置path變量,在其中加入protoc.exe的目錄。
如下圖:

這裏寫圖片描述

這樣的話,就可以使用命令行來進行proto文件的編譯了。但是本文主要講eclipse環境下的protobuf的使用,此處不詳述。

配置好了環境變量後,再dos窗口下測試一下是否可以使用
如下:

這裏寫圖片描述

3,eclispe安裝protobuf相關的插件

在eclipse marketplace上下載protobuf插件並安裝。

這裏寫圖片描述

安裝後重啓eclipse,然後按照下面的方法配置好相應的環境

這裏寫圖片描述

main標籤頁定義protoc.exe的路徑

這裏寫圖片描述

options標籤頁定義生成的java文件的路徑

4 protobuf序列化和反序列化

4.1 定義proto文件

proto文件的後綴名爲.proto。該文件是一個定義數據格式的文件,相當於一個元數據文件。
proto文件有自己的一套語法,語法的具體規則請查看:
https://developers.google.com/protocol-buffers/
下面用一個簡單的例子,演示一下:

定義一個數據文件:addressbook.proto

這個文件是官網上給的一個示例,內容如下:

// [START declaration]
syntax = "proto3";
package tutorial;
// [END declaration]

// [START java_declaration]
option java_package = "com.example.tutorial";
option java_outer_classname = "AddressBookProtos";
// [END java_declaration]

// [START messages]
message Person {
  string name = 1;
  int32 id = 2;  // Unique ID number for this person.
  string email = 3;

  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }

  message PhoneNumber {
    string number = 1;
  }

  repeated PhoneNumber phones = 4;
}

// Our address book file is just one of these.
message AddressBook {
  repeated Person people = 1;
}
// [END messages]

利用.proto文件可以生成java文件。這個java文件是編譯器生成的,它會生成相應的類。這個類裏面會有相應的方法,下面會解釋。
注:proto文件的說明

syntax = “proto3”;是用來定義使用的proto的版本。如果不定義的話,按照官網的說法,默認proto2。
package tutorial;生成的類被放置在一個基於 java_package 選項的java package中。如果這個選項缺失,將替代使用 package 定義。例如,如果 .proto 文件包含:

package foo.bar;

那麼生成的java類將放置在Java package foo.bar中。不過,如果 .proto 文件也包含一個 java_package 選項,像這樣:

package foo.bar;
option java_package = "com.example.foo.bar";

所以上面生成的java文件會在com.example.tutorial的java package中。
option java_outer_classname = “AddressBookProtos”;定義了生成的java類的名字。
message可以把他看做java中的一個類,優點類似於java bean。裏面會定義成員變量,成員變量有三種形式,repeated,optional,required。repeated代表該值有多個,optional可選字段,required代表必選字段。具體含義如下:

required: a value for the field must be provided, otherwise the message will be considered "uninitialized". Trying to build an uninitialized message will throw a RuntimeException. Parsing an uninitialized message will throw an IOException. Other than this, a required field behaves exactly like an optional field.

optional: the field may or may not be set. If an optional field value isn't set, a default value is used. For simple types, you can specify your own default value, as we've done for the phone number type in the example. Otherwise, a system default is used: zero for numeric types, the empty string for strings, false for bools. For embedded messages, the default value is always the "default instance" or "prototype" of the message, which has none of its fields set. Calling the accessor to get the value of an optional (or required) field which has not been explicitly set always returns that field's default value.

repeated: the field may be repeated any number of times (including zero). The order of the repeated values will be preserved in the protocol buffer. Think of repeated fields as dynamically sized arrays.

下面是proto文件中數據類型和java中數據類型對應的關係:
這裏寫圖片描述

4.2 生成一個java文件

由於安裝了protobuf插件,該插件會自動編譯proto文件,並在相應的package下生成相應的類。
具體的工程目錄如下:

這裏寫圖片描述

本項目是用maven創建的webapp項目。如果讀者對maven不是很瞭解可以可以查看我的另一篇博客。
生成的java文件中的成員變量和方法如下:

這裏寫圖片描述

每個message裏面都會有相應的成員變量和方法,而且protoc會編譯之後,成員變量和方法的名字有自己的命名規則,滿足駝峯命名規則。

具體的命名規則請查看:
https://skyao.gitbooks.io/learning-proto3/content/reference/java/fileds.html

4.3 序列化和反序列化

package proto;

import com.example.tutorial.AddressBookProtos.AddressBook;
import com.example.tutorial.AddressBookProtos.Person;
import com.example.tutorial.AddressBookProtos.Person.PhoneNumber;
import com.google.protobuf.InvalidProtocolBufferException;

public class ProtoTest {
    public static void main(String[] args) {
        //序列化
        Person.Builder person=Person.newBuilder();
        person.setEmail("[email protected]").setId(1).setName("張三");

        PhoneNumber.Builder number=PhoneNumber.newBuilder();
        number.setNumber("XXXXXXXX");
        person.addPhones(number);

        AddressBook.Builder address=AddressBook.newBuilder();
        address.addPeople(person);
        address.addPeople(person);

        AddressBook book=address.build();
        byte[] result=book.toByteArray();//序列化

//      //反序列化
        AddressBook read;
        try {
            read = AddressBook.parseFrom(result);
            System.out.println(read.getPeopleList().get(0).getNameBytes().toStringUtf8());  
            //System.out.println(read);  
        } catch (InvalidProtocolBufferException e) {
            e.printStackTrace();
        } 
    }
}

運行後輸出:

張三

如果取消註釋,運行System.out.println(read);
可以看到輸出的結果是:

people {
  name: "\345\274\240\344\270\211"
  id: 1
  email: "[email protected]"
  phones {
    number: "XXXXXXXX"
  }
}
people {
  name: "\345\274\240\344\270\211"
  id: 1
  email: "[email protected]"
  phones {
    number: "XXXXXXXX"
  }
}

如果需要轉成中文,需要使用toStringUtf8()方法。

上面便是整個流程。

5 前後端的交互解析

因爲protobuf傳輸是二進制數據,因此在前後端交互中都需要進行解析。
對於前端的protobuf的解析,參看
http://dcode.io/protobuf.js/
————————————————————————————————————————————
參考:
https://developers.google.com/protocol-buffers/docs/javatutorial
https://github.com/google/protobuf/releases/tag/v3.3.0
http://dcode.io/protobuf.js/
https://github.com/google/protobuf/releases
http://www.infoq.com/cn/articles/json-is-5-times-faster-than-protobuf

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章