Language Guide (proto3)
本指南介绍了如何使用 protocol buffer 语言来构建protocol buffer数据,包括.proto文件语法以及如何从.proto 文件生成数据访问类。它涵盖了proto3 版本的协议缓冲语言:有关proto2语法的信息,请参阅proto2语言指南。
文章目录
- Language Guide (proto3)
-
- @[toc]
- 定义一个消息类型 Defining A Message Type
- 指定字段类型 Specifying Field Types
- 指定字段编号 Assigning Field Numbers
- 重复使用字段号的后果 Consequences of Reusing Field Numbers
- 指定字段标签 Specifying Field Labels
- 格式良好的消息 Well-formed Message
- 添加更多的消息类型 Adding More Message Types
- 添加注释 Adding Comments
- 删除字段 Deleting Fields
- 保留字段 Reserved Fields
- 从 .proto 中生成了什么?
- 标量值类型 Scalar Value Types
- 默认值
- 枚举
-
- 使用其他消息类型 Using Other Message Type
-
- 嵌套类型 Nested Types
- 更新一个消息类型 Updating A Message Type
- 未知字段 Unknown Fields
- Any
- Oneof
-
- Maps
-
- Packages
-
- 定义服务 Defining Services
- JSON 映射
-
- 选项
-
- 生成类 Generating Your Classes
- 文件未知 File location
-
- 支持的平台
文章目录
- Language Guide (proto3)
-
- @[toc]
- 定义一个消息类型 Defining A Message Type
- 指定字段类型 Specifying Field Types
- 指定字段编号 Assigning Field Numbers
- 重复使用字段号的后果 Consequences of Reusing Field Numbers
- 指定字段标签 Specifying Field Labels
- 格式良好的消息 Well-formed Message
- 添加更多的消息类型 Adding More Message Types
- 添加注释 Adding Comments
- 删除字段 Deleting Fields
- 保留字段 Reserved Fields
- 从 .proto 中生成了什么?
- 标量值类型 Scalar Value Types
- 默认值
- 枚举
- 使用其他消息类型 Using Other Message Type
- 嵌套类型 Nested Types
- 更新一个消息类型 Updating A Message Type
- 未知字段 Unknown Fields
- Any
- Oneof
- Maps
- Packages
- 定义服务 Defining Services
- JSON 映射
- 选项
- 生成类 Generating Your Classes
- 文件未知 File location
- 支持的平台
定义一个消息类型 Defining A Message Type
首先我们来看一个非常简单的例子。假设您想要定义一个搜索请求消息格式,其中每个搜索请求都有一个查询字符串、您感兴趣的特定页面的结果以及每页的结果数。这是用于定义消息类型的.proto文件。
syntax = "proto3";
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 results_per_page = 3;
}
- 文件的第一行指定您正在使用proto3语法:如果您不这样做,protocol buffer编译器将假设您正在使用proto2。这必须是文件的第一个非空、非注释行。
- SearchRequest消息定义指定了三个字段(名称/值对),每个字段对应要包含在此类消息中的每条数据。每个字段都有一个名称和一个类型。
指定字段类型 Specifying Field Types
在前面的示例中,所有字段都是标量类型:两个整数(page_number 和 results_per_page)和一个字符串(query)。您还可以像为字段指定其他消息类型一样,指定枚举和复合类型。
指定字段编号 Assigning Field Numbers
您必须为消息定义中的每个字段提供一个介于 1 和 536870911 之间的数字,但有以下限制:
- 给定的数字在该消息的所有字段中必须是唯一的。
- 字段号 19000 至 19999 保留用于protocol buffer的实现。如果您在消息中使用了这些保留字段号中的一个,则protocol buffer编译器会抱怨。
- 您不能使用任何以前保留的字段号或已分配给扩展名的任何字段号。
一旦使用消息类型,就不能更改此数字,因为它以消息有线格式标识字段。“更改”字段编号相当于删除该字段并创建一个具有相同类型但具有新编号的新字段。请参阅删除字段以了解如何正确执行此操作。
字段号不应重复使用。永远不要将字段号从保留列表中删除,以便与新的字段定义一起重用。请参阅重复使用字段编号的后果。
对于最频繁设置的字段,应使用字段编号 1 到 15。较低的字段编号值在导线格式中占用的空间较小。例如,范围为1到15的字段编号需要一个字节进行编码。在 16 到 2047 范围内的字段编号占用两个字节。您可以在Protocol Buffer Encoding 中找到更多关于这方面的信息。
重复使用字段号的后果 Consequences of Reusing Field Numbers
重复使用字段号会使线性格式(wire format)消息的解码变得不明确。
protobuf-wire格式是精简的,不能提供一种方法来检测使用一种定义编码并使用另一种定义解码的字段。
使用一个定义对字段进行编码,然后使用不同的定义对同一字段进行解码,可能会导致:
- 开发人员在调试过程中损失的时间
- 解析/合并错误(最佳情况)
- 泄露的 PII/SPII
- 数据损坏
字段号重复使用的常见原因:
- 对字段重新编号(有时这样做是为了使字段的编号顺序更美观)。重新编号可以有效地删除和重新添加重新编号中涉及的所有字段,从而导致不兼容的线性格式更改。
- 删除字段而不保留数字以防止将来重复使用。
最大字段是29位,而不是更典型的32位,因为三个较低的位用于线性格式。有关此方面的详细信息,请参阅编码主题。
指定字段标签 Specifying Field Labels
消息字段可以是以下字段之一:
optional: 可选字段处于两种可能状态之一:
- 字段已设置,并且包含从线性显式设置或解析的值。它将被串行化到线上。
- 该字段未设置,将返回默认值。它不会被序列化到线上。
您可以检查该值是否已显式设置。
repeated:此字段类型可以在格式良好的消息中重复零次或多次。重复值的顺序将被保留。
map:这是一个成对的键/值字段类型。有关此字段类型的详细信息,请参见 Maps。
如果未应用显式字段标签,则假定为默认字段标签,称为“隐式字段存在”。(不能将字段显式设置为此状态。)格式正确的消息可以有零个或一个字段(但不能超过一个)。您也无法确定是否从线分析了此类型的字段。除非隐式存在字段是默认值,否则它将被序列化到线。有关此主题的更多信息,请参阅Field Presence。
在proto3中,标量数字类型的重复字段默认使用压缩编码。您可以在 Protocol Buffer Encoding 中找到有关压缩编码的更多信息。
格式良好的消息 Well-formed Message
当应用于protobuf消息时,术语“格式良好”指的是序列化/反序列化的字节。proto解析器验证给定的proto定义文件是可解析的。
在可选字段有多个值的情况下,protoc解析器将接受输入,但只使用最后一个字段。因此,“字节”可能不是“格式良好”的,但生成的消息只有一个,并且是“格式良好的”(但不会往返相同的消息)。
添加更多的消息类型 Adding More Message Types
在一个.proto文件中可以定义多个消息类型。如果您正在定义多个相关消息,这将非常有用,例如,如果您想定义与SearchResponse消息类型相对应的回复消息格式,可以将其添加到同一格式中。proto:
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 results_per_page = 3;
}
message SearchResponse {
...
}
组合消息会导致膨胀虽然可以在单个.proto文件中定义多个消息类型(如消息、枚举和服务),但当在单个文件中定义大量具有不同依赖关系的消息时,也会导致依赖关系膨胀。建议每个.proto文件包含尽可能少的消息类型。
添加注释 Adding Comments
要在.proto文件中添加注释,请使用C/C++样式 //
和 /* ... */
语法。
/*
* 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 results_per_page = 3; // Number of results to return per page.
}
删除字段 Deleting Fields
如果操作不当,删除字段可能会导致严重问题。
当您不再需要字段并且所有引用都已从客户端代码中删除时,您可以从消息中删除字段定义。但是,您必须保留已删除的字段编号。如果不保留字段编号,开发人员将来可能会重用该编号。
您还应该保留字段名,以允许消息的 JSON 和 TextFormat 编码继续解析。
保留字段 Reserved Fields
如果通过完全删除字段或注释掉字段来更新消息类型,未来的开发人员可以在对该类型进行自己的更新时重用字段号。这可能会导致严重的问题,如重复使用字段编号的后果中所述。
要确保不会发生这种情况,请将已删除的字段号添加到保留列表中。为了确保消息的JSON和TextFormat实例仍然可以解析,还可以将删除的字段名添加到保留列表中。
如果将来的开发人员试图使用这些保留的字段号或名称,protocol buffer编译器会抱怨。
message Foo {
reserved 2, 15, 9 to 11;
reserved "foo", "bar";
}
保留字段编号范围包括在内(9到11与9、10、11相同)。请注意,不能在同一保留语句中混用字段名和字段号。
从 .proto 中生成了什么?
当你在.proto上运行protocol buffer编译器时,编译器会用你选择的语言生成代码,你需要使用文件中描述的消息类型,包括获取和设置字段值,将消息序列化到输出流,以及从输入流解析消息。
- 对于C++,编译器从每个.proto生成一个.h和.cc文件,文件中描述的每个消息类型都有一个类。
- 对于Java<