proto3语言指南

原文

翻译+整理,翻阅的时候方便些,只翻译了可能用到的。
这个指南描述怎么使用 protocol buffer 语言来描述数据,包括 proto 文件语法和如何生成数据访问类。支持的是 proto3 版本

定义一个消息类型(Message Type)

先来看一个非常简单的例子。定义一个搜索请求消息格式,每个请求有一个查询字符串,搜索结果的指定页和每页的结果数量。下面是定义消息类型的 .proto 文件
一个 .proto 文件可以定义多个消息类型。

syntax = "proto3";

/* SearchRequest represents a search query, with pagination options to
* indicate which results to include in the response. */
message SearchRequest {
string query = 1;
int32 page_number = 2; // Which page number do we want?
int32 result_per_page = 3; // Number of results to return per page.
}

message SearchResponse {
...
}

第一行指定版本,默认是 proto2,必须是文件的第一个非空,非注释行。
SearchRequest 定义了这个消息所需要的数据,包括三个字段(键值对),每个字段都有一个名字和类型。
使用c/c++风格的注释

指定字段类型

上面的例子里面所有字段都是标量类型:两个整数和一个字符串。这里可以包括复合类型,包括枚举和其他信息类型。

分配标识

上面例子中每个字段都有一个唯一的数字标识。这些标识是用来在二进制格式消息中区分字段的,并且一旦使用就不应该更改。
注意:1-15标识占1个字节,包括标识数字和字段类型。16-2047占2个字节。
你应该保留1-15标识给经常出现的信息元素使用,同时注意留一些标识给将来可能增加的频繁元素。
最小的标识是1,最大的是$2^29 - 1$, 或者 536,870,911,保留标识:19000 - 19999。

指定字段规则

消息字段可以是下面的一种:

  1. singular: 结构良好的消息可以有0-1个这个字段(但是不能多于一个)
  2. repeated: 这个字段可以重复任何次数包括0,重复的值得顺序会被保留。

proto3 中数字类型的 repeated 字段默认使用 packed 编码

保留字段

如果删除了一个字段,最好定义为保留字段,这样就不会被重复使用,造成兼容性问题。

message Foo {
reserved 2, 15, 9 to 11;
reserved "foo", "bar";
}

这里注意不能在一个 reserved 字段混用名字和数字

.proto 文件生成什么

当你使用 protocol Buffer(后面简写pb) 编译器编译 .proto 文件时编译器会生成存取,序列化代码。
对于 go 会生成 .pb.go 文件

默认值

当解析一个消息是,如果编码消息没有指定一个 singular 元素,那么在解析出来的对象中使用默认值。

  • strings 空字符串
  • bytes 空
  • bools false
  • numberic 0
  • enums 第一个定义的enum值,0

注意,解析之后,是无法知道字段是设置为 false 还是因为没有设置才是默认值 false 的。

枚举

定义一个消息类型时,可能想要某个字段只在预定义的范围内取值。例如:在 SearchRequest 增加一个 corpus 字段,取值可以为 UNIVERSAL, WEB, IMAGES, LOCAL, NEWS, PRODUCTS, VIDEO。

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;
}

可以通过定义 allow_alias = true 来支持不同枚举常量可以有相同的值。

enum EnumAllowingAlias {
option allow_alias = true;
UNKNOWN = 0;
STARTED = 1;
RUNNING = 1;
}
enum EnumNotAllowingAlias {
UNKNOWN = 0;
STARTED = 1;
// RUNNING = 1; // Uncommenting this line will cause a compile error inside Google and a warning message outside.
}

枚举常量取值范围在32位置整数内。负值效率不高不推荐。
可以在消息内部定义枚举,也可以在外部定义(可以在 .proto 文件的任何消息中使用)。
也可以在不同消息中引用枚举,语法:MessageType.EnumType

使用其他消息类型

可以使用其他消息类型作为字段。例如:在 SearchResponse 中包含了 Result 字段。

message SearchResponse {
repeated Result results = 1;
}

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

引入定义

使用其他 .proto 文件中的消息定义。
import "myproject/other_protos.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;
}

更新消息类型

Any

可以让你使用消息时作为内嵌类型,不需要定义.proto文件。
包括一个任意序列化消息类型是 bytes,还有一个 URL 作为全局标识符。默认 type.googleapis.com/packagename.messagename
使用 Any 类型,需要引入 import google/protobuf/any.proto

import "google/protobuf/any.proto";

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

Packages

可以在 .proto 文件中增加一个可选的 package 限定符,用来防止协议消息类型间的命名冲突

package foo.bar;
message Open { ... }

在定义消息字段时使用

message Foo {
...
required foo.bar.Open open = 1;
...
}

定义 RPC 服务

定义 RPC 服务。pb编译器会生成服务接口代码。gRPC就是使用pb来实现的。跨语言,跨平台的开源RPC系统。

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

JSON 映射

proto3 支持编码为JSON,
有个对应类型列表,查看原文