protobuf3笔记

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

Protobuf3笔记

文件后缀

定义Proto的文件应以.proto为后缀。

语法版本

Proto文件的首行应指定语法版本:

syntax = "proto3"; // "proto2"

定义字段

在消息中,每个字段以下列方式定义:

type filed "=" tag ";"

如:

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

标签数字

出于性能考虑,在消息中,常用字段的标签最好小于15。这样可以降低消息序列化后的体积。

多消息

一个Proto文件中,可以定义多个消息。如:

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

注释

Proto使用C风格的注释。

编译输出

对于C++,protobuf为每个消息生成一个类,为每个proto文件生成一个.h头文件和一个.cc源代码文件。

对Java,protobuf为每个消息生成一个类和一个Builder类。

对于Go,protobuf为每个消息生成一个.pb.go源代码文件和一个结构体。

对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 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 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: "[email protected]",
  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 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 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 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日 修正错别字;修改日期格式。

转载于:https://my.oschina.net/u/131191/blog/730815

你可能感兴趣的:(golang,java,python)