这是我参与「第五届青训营 」伴学笔记创作活动的第 11 天
今天学了如何使用协议缓冲区语言(包括 .proto 文件语法)构建协议缓冲区数据,以及如何从 .proto 文件生成数据访问类。它涵盖协议缓冲区语言的 proto3 版本:
定义消息类型
我们先来看一个非常简单的示例。假设您要定义搜索请求消息格式,其中每个搜索请求都有一个查询字符串、您感兴趣的特定结果页以及每页的结果数。以下是您定义消息类型的 .proto 文件。
syntax = "proto3";
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
}
- 文件的第一行指定您使用
proto3语法:如果您不执行此操作,协议缓冲区编译器会假定您使用的是 proto2。此文件必须是文件的第一行非空行。 SearchRequest消息定义指定三个字段(名称/值对),每个字段对应于要包含在此类消息中的每段数据。每个字段都有一个名称和类型。
指定字段类型
在上面的示例中,所有字段都是标量类型:两个整数(page_number 和 result_per_page)和一个字符串 (query)。不过,您也可以为字段指定复合类型,包括枚举和其他消息类型。
分配字段编号
如您所见,消息定义中的每个字段都有一个唯一编号。这些字段编号用于标识消息二进制格式中的字段,且在您的消息类型被使用后不应改变。请注意,范围 1 到 15 中的字段编号使用一个字节进行编码,包括字段编号和字段类型(如需了解详情,请参阅协议缓冲区编码。16 至 2047 范围内的字段编号占用两个字节。因此,您应为出现频率较高的消息元素预留 1 到 15 之间的数字。请务必为将来可能添加的常见元素留出一些空间。
您可以指定的最小字段编号为 1,最大值为 229 - 1,即 536870911。您也无法使用 19000 到 19999 之间的数字(FieldDescriptor::kFirstReservedNumber 到 FieldDescriptor::kLastReservedNumber),因为它们专用于协议缓冲区实现。如果您在 .proto 中使用其中一个预留数字,协议缓冲区编译器会抱怨。同样,您也不能使用以前预留的任何字段编号。
指定字段规则
消息字段可以是以下字段之一:
-
singular:格式正确的消息可以有零个或一个字段(但不能超过一个)。使用 proto3 语法时,如果未为给定字段指定其他字段规则,则这是默认字段规则。您无法确定它是否已通过连接线解析。它将被序列化为电汇,除非它是默认值。如需详细了解此主题,请参阅字段存在。 -
optional:与singular相同,不过您可以检查该值是否明确设置。optional字段处于以下两种可能状态之一:- 该字段已设置,并包含从传输中明确设置或解析的值。它会序列化到线路。
- 未设置字段,将返回默认值。它不会序列化到有线网络。
-
repeated:在格式正确的消息中,此字段类型可以重复零次或多次。系统会保留重复值的顺序。 -
map:这是一个成对的键值对字段。如需详细了解此字段类型,请参阅地图。 -
默认值
解析消息后,如果经过编码的消息不包含特定单数元素,则解析对象中的相应字段将设置为该字段的默认值。这些默认值因类型而异:
- 对于字符串,默认值为空字符串。
- 对于字节,默认值为空字节。
- 对于布尔值,默认值为 false。
- 对于数值类型,默认值为零。
- 对于枚举,默认值为第一个定义的枚举值,必须为 0。
- 对于消息字段,系统不会设置此字段。其确切值取决于语言。如需了解详情,请参阅生成的代码指南。
重复字段的默认值为空(通常,采用相应语言的空列表)。
请注意,对于标量消息字段,在解析消息后,就无从判断字段是明确设为默认值(例如布尔值是否设为 false)还是根本不设置:在定义消息类型时,您应该记住这一点。例如,如果您不想让某项行为默认发生,可设置一个布尔值,将其设为 false 时开启该行为。另请注意,如果标量消息字段****已设置为默认值,则该值不会通过序列化进行序列化。
导入定义
在上面的示例中,Result 消息类型与 SearchResponse 在同一文件中定义 - 如果要用作字段类型的消息类型已经在其他 .proto 文件中定义,该怎么办?
您可以通过导入其他 .proto 文件中的定义来使用这些文件。如需导入另一个 .proto 的定义,您可以在文件顶部添加 import 语句:
import "myproject/other_protos.proto";
定义服务
如果要将消息类型与 RPC(远程过程调用)系统配合使用,您可以在 .proto 文件中定义 RPC 服务接口,协议缓冲区编译器会生成以您选择的语言编写的服务接口代码和存根。例如,如果您想使用接受 SearchRequest 并返回 SearchResponse 的方法定义 RPC 服务,可以在 .proto 文件中定义它,如下所示:
service SearchService {
rpc Search(SearchRequest) returns (SearchResponse);
}