前言

继上一节介绍了 Protocol Buffers 的相关信息后,我们了解到协议的一些特性,优势。

接下来让我们简单学习下 Protocol Buffers 的语法知识。

正文

1. 注释

使用 C/C++ 语法的注释风格 //(单行注释)和 /* ... */(多行注释)向 proto 文件中添加注释。

2. 语法说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 使用 proto3 语法
syntax = "proto3";


// 定义 SearchRequest 协议缓冲区数据结构(后文简称消息消息)
message SearchRequest {
string query = 1; // 指定 query 参数的类型为 string,并分配字段编号为 1.
int32 page_number = 2; // 指定 page_number 参数的类型为 int32,并分配字段编号为 2.
int32 result_per_page = 3; // 指定 result_per_page 参数的类型为 int32,并分配字段编号为 3.
}

// 定义 SearchResponse 消息
message SearchResponse {
...
}

更多参数类型详见 协议缓冲区语言指引第三版 Scalar Value Types(标量值类型)部分内容。

注:

  • 每一个字段都有自己唯一的字段编号,这些字段编号用于二进制格式中标识你的字段信息。
  • 字段编号的分配从 1 开始。
  • 字段编号 1 ~ 15 采用 1 个字节进行编码,16 ~ 2047 采用 2 个字节进行编码。
  • 多语言类型可以添加在单个 .proto 文件中。
  • 消息类型可以进行嵌套。
  • 每个消息中的参数可以有 0 个或 1 个信息,这是 proto3 的默认语法。
  • repeated 表明指定字段可以重复任意次数(包括 0 次)。
  • reserved 指定保留已移除的字段编号或字段名(见下方示例)。
  • 标量值类型与语言对应参考表见附录 协议缓冲区语言指引第三版 语言指引 Scalar Value Types(标量值类型)部分内容。
  • 消息的默认值通常是 空值(string)、空字节(bytes)、零值(numeric)、假值(bool)、
  • 枚举值的第一个值为默认值,且必须定义为 0 值,便于兼容 proto2 协议枚举值语法规则。
  • 重复字段默认是空的,通常为对应语言的空列表。
  • 布尔值的默认值为假值,无法判断是否显示(开发者主动将该值)设置为假值,请尽量避免这种情况的出现,如有需要,请考虑使用其他类型值进行参数定义。
  • 标量的类型值为默认值时,不会对该字段进行序列化操作。
  • 在枚举类型中,指定 allow_alias 为 true,允许该枚举类型中出现同一字段值。
  • import 可以导入其他消息定义。
  • 消息定义中可以使用其他消息做类型。
  • 消息协议的更新有着一些默认规则,详见 协议缓冲区语言指引第三版 语言指引 Updating A Message Type 部分内容。
  • 未知类型总是会被序列化输出,在 proto3 协议中,未知类型会被忽略,在 3.5 及更高的版本中,解析期间未知字段会被保留。
  • Any 任意类型需要导入 import "google/protobuf/any.proto"; 协议文件。
  • oneof 可以指定字段为多种类型
  • oneof 不可以指定 repeated 重复。
  • oneof 在更新值时,已有的值会被清空。
  • 关联映射类型不可以指定 repeated 重复。
  • 添加包名有助于避免生成的源代码冲突,在生成源代码时,报名会被翻译成对应语言的命名空间(模块、包)。
  • JSON 类型映射默认使用小驼峰命名规则进行消息序列化,在字段定义时使用 json_name 可更改为制定的 JSON 字段名。
  • 通过定义 service 可以开启 消息协议的 RPC(Remote Procedure Call)调用。
  • php 的 rpc 服务端目前还没有较为合适的实现,暂时做 rpc 客户端使用。
  • 一些其他语言的可选选项详见附录 协议缓冲区语言指引第三版 语言指引 Options 部分内容。

3. 消息示例

保留值与保留类型示例

1
2
3
4
5
// 定义 Foo 协议缓冲区数据结构
message Foo {
reserved 2, 15, 9 to 11; // 通过字段编号指定保留字段信息
reserved "foo", "bar"; // 通过字段名自定保留字段信息
}

枚举类型示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
// 定义枚举类型 Corpus
enum Corpus {
UNIVERSAL = 0; // 定义默认值 UNIVERSAL(默认值为 0,且必须是枚举类型第一个元素)
WEB = 1;
IMAGES = 2;
LOCAL = 3;
NEWS = 4;
PRODUCTS = 5;
VIDEO = 6;
}
Corpus corpus = 4; // 定义参数 corpus 的类型为枚举类型 Corpus,分配字段编号为 4。
}

枚举类型别名示例

1
2
3
4
5
6
7
8
9
10
11
12
enum EnumAllowingAlias {
option allow_alias = true; // 允许枚举类型 EnumAllowingAlias 出现别名(字段值编号相同)。
UNKNOWN = 0;
STARTED = 1;
RUNNING = 1;
}

enum EnumNotAllowingAlias {
UNKNOWN = 0;
STARTED = 1;
// RUNNING = 1; // 取消注释后,源代码生产工具将会发生编译错误,并提示错误信息。
}

消息导入示例

1
import "myproject/other_protos.proto";

嵌套消息类型定义示例

1
2
3
4
5
6
7
8
message SearchResponse {
message Result {
string url = 1;
string title = 2;
repeated string snippets = 3;
}
repeated Result results = 1;
}

任意值类型示例

1
2
3
4
5
6
import "google/protobuf/any.proto";

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

Oneof 示例

1
2
3
4
5
6
message SampleMessage {
oneof test_oneof {
string name = 4;
SubMessage sub_message = 9;
}
}

指定 json_name 示例

1
2
3
4
5
6
7
8
// 第三方 app 登录响应
message OtherAppLoginResponse {
Response response = 1; // 接口响应信息
int32 user_id = 2 [json_name="user_id"]; // 用户 id,json 输出为 user_id
string access_token = 3; // 登录 token,json 输出为 accessToken
string token_type = 4; // 刷新 token,json 输出为 tokenType
string expire_at = 5; // 失效时间,json 输出为 expireAt
}

3. 附录

协议缓冲区语言指引第三版:https://developers.google.cn/protocol-buffers/docs/proto3