protobuf 簡要介紹及應用

官方文檔:https://developers.google.cn/protocol-buffers/docs/overview

簡介:

protocol buffers – a language-neutral, platform-neutral, extensible way of serializing structured data for use in communications protocols, data storage, and more.

一種與語言無關,平臺無關,可擴展的序列化結構化數據的方法,用於通信協議,數據存儲等。

 

What are protocol buffers? 什麼是protocol buffer?

Protocol buffers are a flexible, efficient, automated mechanism for serializing structured data – think XML, but smaller, faster, and simpler. You define how you want your data to be structured once, then you can use special generated source code to easily write and read your structured data to and from a variety of data streams and using a variety of languages. You can even update your data structure without breaking deployed programs that are compiled against the "old" format.

Protocol buffers 是一種靈活,高效,自動化的機制,用於序列化結構化數據 - 想想XML,但更小,更快,更簡單。您可以定義數據的結構化結構,然後使用特殊生成的源代碼輕鬆地將結構化數據寫入和讀取各種數據流,並使用各種語言。您甚至可以更新數據結構,而不會破壞根據“舊”格式編譯的已部署程序。

 

How do they work? protobuf 是如何工作的?

上面的都是protocol buffer的官方資料,下面的...也是基於官方資料的。

基本約定:

1.我們使用的是protobuf 3.0版本協議,理由:簡介,新一代,還有其他好處

2.對於3.0和2.0的區別大家可以在網上找資料查閱,這裏不作介紹

3.閱讀下面文字之前你最好先看下官網

protobuf數據類型轉換

.proto Type Notes C++ Type Java Type Python Type[2] Go Type Ruby Type C# Type PHP Type Dart Type
double   double double float float64 Float double float double
float   float float float float32 Float float float double
int32 Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead. int32 int int int32 Fixnum or Bignum (as required) int integer int
int64 Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead. int64 long int/long[3] int64 Bignum long integer/string[5] Int64
uint32 Uses variable-length encoding. uint32 int[1] int/long[3] uint32 Fixnum or Bignum (as required) uint integer int
uint64 Uses variable-length encoding. uint64 long[1] int/long[3] uint64 Bignum ulong integer/string[5] Int64
sint32 Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. int32 int int int32 Fixnum or Bignum (as required) int integer int
sint64 Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. int64 long int/long[3] int64 Bignum long integer/string[5] Int64
fixed32 Always four bytes. More efficient than uint32 if values are often greater than 228. uint32 int[1] int/long[3] uint32 Fixnum or Bignum (as required) uint integer int
fixed64 Always eight bytes. More efficient than uint64 if values are often greater than 256. uint64 long[1] int/long[3] uint64 Bignum ulong integer/string[5] Int64
sfixed32 Always four bytes. int32 int int int32 Fixnum or Bignum (as required) int integer int
sfixed64 Always eight bytes. int64 long int/long[3] int64 Bignum long integer/string[5] Int64
bool   bool boolean bool bool TrueClass/FalseClass bool boolean bool
string A string must always contain UTF-8 encoded or 7-bit ASCII text. string String str/unicode[4] string String (UTF-8) string string String
bytes May contain any arbitrary sequence of bytes. string ByteString str []byte String (ASCII-8BIT) ByteString string

protobuf依賴maven包:

 

<dependency>
 <groupId>com.google.protobuf</groupId>
 <artifactId>protobuf-java</artifactId>
 <version>3.8.0</version>
</dependency>

一.創建proto協議文件

要想使用protobuf ,我們首先要按照protobuf的格式寫一個.proto的消息協議文件

油耗上報協議文件brakingInfoReport.proto

//協議版本號
syntax = "proto3";
//引用外部proto協議文件
import "common.proto";
//協議所屬包名
package coom.lyh.protobuf.demo.protos;

//對外輸出 java包名
option java_package = "com.lyh.protobuf.demo.business";
//對外輸出 java類名
option java_outer_classname = "BrakingInfoReportProto";

//定義上報消息實體
message BrakingInfoReport{
  
    message BrakingInfo{
        /**
        * 事件產生的時間(單位毫秒)
        */
        //private Long rts;
        int64 rts = 1;
       
        //private Float bol;
        float bol = 2;
    }
    //消息類型 在common協議裏面定義
    JsonMessageType jsonMessageType = 1;
    //對象類型 在common協議裏面定義
    ObjectMessageType objectMessageType = 2;
    //mqtt協議版本號
    string protocol = 3 ;
  
   string sn = 4;

   string carModelId = 5;
    /**
     * 消息傳輸時間(單位毫秒)
     **/
    int64 ts = 6;
    /**
     * 時間類型
     **/
    string et = 7;

    /**
    * 行程ID
    */
    int32 tripId = 8;

    //信息list
    repeated BrakingInfo data = 9;

}

common.proto協議定義如下從協議文件中我們從註釋中很容易理解這個消息中的各個量的意義,重點說下油耗消息協議文件引入了一個外部的common.proto協議文件


//proto 協議版本號
syntax = "proto3";

//輸出java包名
option java_package = "com.lyh.protobuf.demo.business";
//輸出java類名
option java_outer_classname = "Common";

//定義公共消息類型
enum JsonMessageType {
    REPORT = 0;
    REQUEST = 1;
    RESPONSE = 2;
}
//定義公共消息傳輸對象類型
enum ObjectMessageType{

    CONNECT_REQUEST= 0 ;
  
   REPORT_BRAKING_INFO= 1 ;
}

二.編譯proto協議到java文件common裏面定義了我們的JsonMessageType 和ObjectMessageType 兩個enum類型以供油耗協議使用,和java很類似,下面我們將這兩個協議轉成java對象

step 1:

 下載官方的編譯器,地址:https://github.com/protocolbuffers/protobuf/releases/tag/v3.8.0

選擇適合自己OS的版本下載。

step 2:

2.1)將解壓後的protoc.exe 添加到系統變量的path路徑裏面 我的電腦右鍵屬性,然後看截圖將你的本地protoc路徑配置上就好了。

2.2)驗證路徑配置 輸入 protoc --version

2.3) 將.proto消息協議文件轉成.java文件

命令行格式:

protoc -I=proto所在的文件地址 --java_out=數據的java類的文件地址  要編譯的proto文件名

編譯common.proto 文件

protoc -I=D:\work\workspace\probuf-demo\src\main\java\com\lyh\protobuf\demo\protos --java_out=D:\work\workspace\probuf-demo\src\main\java  common.proto

上面的指令的含義是:將D:\work\workspace\probuf-demo\src\main\java\com\lyh\protobuf\demo\protos下的common.proto 協議編譯到 D:\work\workspace\probuf-demo\src\main\java 文件夾下,

如果該文件夾下沒有對應的包路徑,則自動創建。

編譯brakingInfoReport.proto 文件

 protoc -I=D:\work\workspace\probuf-demo\src\main\java\com\lyh\protobuf\demo\protos --java_out=D:\work\workspace\probuf-demo\src\main\java  brakingInfoReport.proto

文件編譯完了,我們看下怎麼去使用吧

二.序列化和反序列化

測試樣例如下:

 

@Test
public void testBrakingInfo() throws InvalidProtocolBufferException {
    Common.ObjectMessageType objectMessageType = Common.ObjectMessageType.REPORT_BRAKING_INFO;
    List<BrakingInfoReportProto.BrakingInfoReport.BrakingInfo> brakingInfoList = new ArrayList<>();
    brakingInfoList.add(BrakingInfoReportProto.BrakingInfoReport.BrakingInfo.newBuilder()
            .setBol(0.77F)
            .setRts(System.currentTimeMillis())
            .build());
    brakingInfoList.add(BrakingInfoReportProto.BrakingInfoReport.BrakingInfo.newBuilder()
            .setBol(0.88F)
            .setRts(System.currentTimeMillis())
            .build());
    BrakingInfoReportProto.BrakingInfoReport report = BrakingInfoReportProto.BrakingInfoReport
            .newBuilder()
            .setJsonMessageType(Common.JsonMessageType.REPORT)
            .setObjectMessageType(objectMessageType)
            .setEt(ObjectMessageType.getObjectMessageTypeByIndex(objectMessageType.getNumber())==null?""
                    :ObjectMessageType.getObjectMessageTypeByIndex(objectMessageType.getNumber()).getValue())
            .addData(BrakingInfoReportProto.BrakingInfoReport.BrakingInfo.newBuilder()
                    .setBol(0.55F)
                    .setRts(System.currentTimeMillis())
                    .build())
            .addData(BrakingInfoReportProto.BrakingInfoReport.BrakingInfo.newBuilder()
                    .setBol(0.555F)
                    .setRts(System.currentTimeMillis())
                    .build())
            .addData(BrakingInfoReportProto.BrakingInfoReport.BrakingInfo.newBuilder()
                    .setBol(0.66F)
                    .setRts(System.currentTimeMillis())
                    .build())
            //還可以用這種方式加list
            .addAllData(brakingInfoList)
            .build();
    System.out.println("序列化前:"+report);
    //序列化對象,序列化之後的數組就是交互的數據報文
    byte[] reportBytes = report.toByteArray();
    //反序列化
    BrakingInfoReportProto.BrakingInfoReport brakingInfoReport = BrakingInfoReportProto.BrakingInfoReport.parseFrom(reportBytes);
    System.out.println("反序列化:"+brakingInfoReport);
}


https://github.com/leeahua/protobuf-demo.git對應的maven項目地址:

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