protobuf3筆記

Protobuf3筆記

文件後綴

定義Proto的文件應以.proto爲後綴。html

語法版本

Proto文件的首行應指定語法版本:java

syntax = "proto3"; // "proto2"

定義字段

在消息中,每一個字段如下列方式定義:git

type filed "=" tag ";"

如:github

message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
}

標籤數字

出於性能考慮,在消息中,經常使用字段的標籤最好小於15。這樣能夠下降消息序列化後的體積。golang

多消息

一個Proto文件中,能夠定義多個消息。如:app

message SearchRequest {
// ...
}
message SearchResponse {
// ...
}

註釋

Proto使用C風格的註釋。maven

編譯輸出

對於C++,protobuf爲每一個消息生成一個類,爲每一個proto文件生成一個.h頭文件和一個.cc源代碼文件。性能

對Java,protobuf爲每一個消息生成一個類和一個Builder類。gradle

對於Go,protobuf爲每一個消息生成一個.pb.go源代碼文件和一個結構體。ui

對Objective-C,protobuf爲每一個proto文件生成一個pbobjc.h頭文件和一個pbobjc.m文件,爲每一個消息生成一個類。

經常使用標量類型

bool
string
bytes
int32
int64
uint32
uint64
double
float

默認值

類型 默認值
bool false
bytes []
numeric 0
enum FirstElement
Field null

枚舉類型

message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
  enum Corpus {
    UNIVERSAL = 0;
    WEB = 1;
    IMAGES = 2;
    LOCAL = 3;
    NEWS = 4;
    PRODUCTS = 5;
    VIDEO = 6;
  }
  Corpus corpus = 4;
}

message EnumAllowingAlias {
  option allow_alias = true;
  UNKNOWN = 0;
  STARTED = 1;
  RUNNING = 1;
}

引用其餘消息類型

message SearchResponse {
  repeated Result results = 1;
}

message Result {
  string url = 1;
  string title = 2;
  repeated string snippets = 3;
}

引用其餘proto文件

import "myproject/other_protos.proto";

protoc參數

-I/–proto_path 指定proto文件目錄。

內置類型

message SearchResponse {
    message Result {
    string url = 1;
    string title = 2;
    repeated string snippets = 3;
  }
  repeated Result results = 1;
}

message SomeOtherMessage {
  SearchResponse.Result result = 1;
}

更新消息

字段標籤

不得改動已存在的字段標籤。新生成的代碼能夠解析舊消息。新增的字段會被設置爲默認值。舊代碼也能夠解析新消息,新增的字段會被忽略。

刪除字段

字段能夠被刪除。但已使用過的標籤不得重複使用。

bytes和string

當字符串是UTF-8編碼時,bytes和string能夠兼容。

Any消息

Any消息是一個佔位符,表示任意類型。使用Any消息時,須要引用google/protobuf/any.proto。

import "google/protobuf/any.proto";

message ErrorStatus {
  string message = 1;
  repeated google.protobuf.Any details = 2;
}

NetworkErrorDetails details = ...;
ErrorStatus status;
status.add_details()->PackFrom(details);

ErrorStatus status = ...;
for (const Any& detail : status.details()){
  if (detail.Is<NetworkErrorDetails>()){
    NetworkErrorDetails network_error;
    detail.UnpackTo(&network_error);
    ...
  }
}

OneOf消息

OneOf提供了一種相似C語言union結構體的機制,來下降存儲體積。

message SampleMessage {
  oneof test_oneof {
    string name = 4;
    SubMessage sub_message = 9;
  }
}

SampleMessage message;
message.set_name("Joe");
assert(message.has_name());

Map消息

Map能夠定義一組鍵值對。

map<string, Project> projects = 3;

聲明包。

package foo.bar;

定義服務

service SearchService {
  rpc Search (SearchRequest) returns (SearchResponse);
}

在Go中使用protobuf

示例proto

package tutorial;

option java_package = "com.example.tutorial";
option java_outer_classname = "AddressBookProtos";

message Person {
  required string name = 1;
  required int32 id = 2;
  string email = 3;

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

  message PhoneNumber {
    required string number = 1;
    optional PhoneType type = 2 [default = HOME];
  }
  repeated PhoneNumber phone = 4;
}

生成的代碼

調用方法

import (
  "github.com/golang/protobuf/proto"
  pb "path/to/generated/pb/file"
)
// ...

p := &pb.Person {
  Id: 1234,
  Name: "John Doe",
  Email: "jdoe@example.com",
  Phones: []*pb.Person_PhoneNumber {
    {Number: "555-4321", Type: pb.Person_HOME},
  },
}
out, err := proto.Marshal(p)
q := &pb.Person{}
err := proto.Unmarshal(in, q)

proto.MessageType(name string) reflect.Type
proto.Clone(pb Message) Message

Any消息在Go中的用法

Any消息能夠表示任意類型的消息。在Go中使用Any消息的示例以下:

import "github.com/golang/protobuf/ptypes"
import "github.com/golang/protobuf/proto"
import "path/to/generated/pb"
// message Foo {
//     google.protobuf.Any bar = 1;
// }
// message Bar {
//     uint32 x = 1;
// }

bar := &pb.Bar{
  X: 1,
}

body, err := ptypes.MarshalAny(bar)
if err != nil {
  log.Fatal(err)
}
foo := &pb.Foo{
  Bar: body,
}

注意事項

  1. 在使用proto.Unmarshal(buf, message)對消息進行反序列化時,緩衝區buf的長度應當等於消息的實際長度。不然會報告以下錯誤消息:

    proto: protocol.Message: illegal tag 0 (wire type 0)

在Java中使用protobuf

示例proto

package tutorial;

option java_package = "com.example.tutorial";
option java_outer_classname = "AddressBookProtos";

message Person {
  required string name = 1;
  required int32 id = 2;
  string email = 3;

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

  message PhoneNumber {
    required string number = 1;
    optional PhoneType type = 2 [default = HOME];
  }
  repeated PhoneNumber phone = 4;
}

生成的代碼

// Person
public boolean hasName();
public String getName();

public boolean hasId();
public int getId();

public boolean hasEmail();
public String getEmail();

public List<PhoneNumber> getPhoneList();
public int getPhoneCount();
public PhoneNumber getPhone(int index);

// Person.Builder
public boolean hasName();
public java.lang.String getName();
public Builder setName(String value);
public Builder clearName();

public List<PhoneNumber> getPhoneList();
public int getPhoneCount();
public PhoneNumber getPhone(int index);
public Builder setPhone(int index, PhoneNumber value);
public Builder addPhone(PhoneNumber value);
public Builder addAllPhone(iterable<PhoneNumber> value);
public Builder clearPhone();

調用方法

Person john = Person.newBuilder()
  .setId(1234)
  .setName("John")
  .addPhone(
    Person.PhoneNumber.newBuilder()
      .setNumber("555-4321")
      .setType(Person.PhoneType.HOME))
  .build();
john.writeTo(outputStream);

Person walliam = Person.parseForm(inputStream);

使用Gradle生成protobuf

google提供了生成protobuf的gradle插件,名稱是com.google.protobuf。在使用時,須要在build.gradle中加入:

buildscript {
  repositories {
    mavenCentral()
  }
  dependencies {
    classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.1'
  }
}

apply plugin: 'java'
apply plugin: 'com.google.protobuf'

protobuf {
  generatedFilesBaseDir = '$projectDir/src'
  protoc {
    // use local protoc
    // path = '/usr/local/bin/protoc'
    // or, get from repo
    artifact = 'com.google.protobuf:protoc:3.3.0'
  }

  plugins {
    grpc {
      artifact = 'io.grpc:protoc-gen-grpc-java:1.4.0'
    }
  }

  generateProtoTasks {
    all()*.plugins {
      grpc {}
    }
  }
}

repositories {
        mavenCentral()
}

dependencies {
        compile 'com.google.protobuf:protobuf-java:3.3.1'

        compile 'io.grpc:grpc-netty:1.4.0'
        compile 'io.grpc:grpc-protobuf:1.4.0'
        compile 'io.grpc:grpc-stub:1.4.0'

        /* for Android client, use
          compile 'io.grpc:grpc-okhttp:1.4.0'
          compile 'io.grpc:grpc-protobuf-lite:1.4.0'
          compile 'io.grpc:grpc-stub:1.4.0'
        */
}

sourceSets {
  main {
    proto {
      srcDir 'src/main/protobuf'
      include '**/*.proto'
    }
  }
}

而後執行:

gradle build

若是隻須要生成java源代碼文件,能夠執行:

gradle generateProto

參考資料

  1. https://developers.google.com/protocol-buffers/docs/javatutorial
  2. http://www.cnblogs.com/resentment/p/6715124.html
  3. https://github.com/grpc/grpc-java/tree/master/compiler
  4. https://github.com/grpc/grpc-java

修訂記錄

  1. 2016年05月03日 創建文檔。
  2. 2016年08月11日 修訂。
  3. 2017年07月28日 改成rst格式。
  4. 2017年07月28日 增長gradle部分。
  5. 2017年08月04日 修訂例子。
  6. 2017年08月07日 增長在Go中使用Any消息的例子。
  7. 2018年08月06日 修正錯別字;修改日期格式。
相關文章
相關標籤/搜索