分类:proto| 发布时间:2025-01-13 08:47:00
原文: Protobuf Editions Overview
Protobuf Editions 功能概述。
Protobuf Editions 取代了我们用于 Protocol Buffers 的 proto2 和 proto3 名称。
您无需在 proto 定义文件的顶部添加 syntax = "proto2"
或 syntax = "proto3"
,而是使用版本号(例如 edition = "2023"
)来指定文件将具有的默认行为。
版本使语言能够随着时间的推移逐步发展。
Editions 代表一组功能,每个功能都有一个默认值(行为),而不是旧版本所具有的硬编码行为。 功能是文件、消息、字段、枚举等的选项,用于指定 protoc、代码生成器和 protobuf 运行时的行为。 当您的需求与所选版本的默认行为不匹配时,您可以在这些不同级别(文件、消息、字段等)明确覆盖行为。 您还可以覆盖您的覆盖。本主题后面关于词法作用域的部分将对此进行更详细的介绍。
截至目前(2025-01-13),最后一个发布版本是 2023。
Editions 为功能的生命周期提供了基本增量。 功能具有预期的生命周期:引入它、更改其默认行为、弃用它,然后删除它。例如:
feature.amazing_new_feature
,默认值为 false
。此值保持与所有早期版本相同的行为。
也就是说,它默认为无影响。.proto
文件更新为 edition ="2031"
。feature.amazing_new_feature
的默认值从 false
切换为 true
。
这是所有 proto 的期望行为,也是 protobuf 团队创建该功能的原因。
使用 Prototiller 工具将早期版本的 proto 文件迁移到版本 2033 会根据需要添加显式 feature.amazing_new_feature = false
条目以继续保留以前的行为。
当开发人员希望将新行为应用于他们的 .proto 文件时,他们会删除这些新添加的设置。feature.amazing_new_feature
在某个版本中被标记为弃用,并在后续版本中删除。
删除某个功能时,该行为的代码生成器和支持该行为的运行时库也可能会删除。不过,时间安排会很宽松。
按照生命周期早期步骤中的示例,弃用可能发生在 2034 版,但直到大约两年后的 2036 版才会被删除。
删除功能总是会引发主要版本升级。由于此生命周期,任何未使用弃用功能的 .proto
文件都可以从一个版本升级到下一个版本,无需操作。
您将有足够的时间来升级您的代码,包括 Google 迁移的整个窗口期以及弃用期。
前面的生命周期示例对功能使用了布尔值,但功能也可以使用枚举。
例如,features.field_presence
具有值 LEGACY_REQUIRED
、EXPLICIT
和 IMPLICIT
。
Editions 不会破坏现有二进制文件,也不会更改消息的二进制、文本或 JSON 序列化格式。 第一版的破坏性尽可能小。 第一版建立了基线,并将 proto2 和 proto3 定义合并为新的单一定义格式。
当后续版本发布时,功能的默认行为可能会发生变化。 您可以让 Prototiller 对 .proto 文件进行无操作转换,也可以选择接受部分或全部新行为。 计划大约每年发布一次版本。
本节展示了一个 proto2 文件,以及运行 Prototiller 工具将定义文件更改为使用 Protobuf Editions 语法后它可能是什么样子。
Proto2 语法
// proto2 file
syntax = "proto2";
package com.example;
message Player {
// in proto2, optional fields have explicit presence
optional string name = 1 [default = "N/A"];
// proto2 still supports the problematic "required" field rule
required int32 id = 2;
// in proto2 this is not packed by default
repeated int32 scores = 3;
enum Handed {
HANDED_UNSPECIFIED = 0;
HANDED_LEFT = 1;
HANDED_RIGHT = 2;
HANDED_AMBIDEXTROUS = 3;
}
// in proto2 enums are closed
optional Handed handed = 4;
reserved "gender";
}
Editions 语法
// Edition version of proto2 file
edition = "2023";
package com.example;
option features.utf8_validation = NONE;
message Player {
// fields have explicit presence, so no explicit setting needed
string name = 1 [default = "N/A"];
// to match the proto2 behavior, LEGACY_REQUIRED is set at the field level
int32 id = 2 [features.field_presence = LEGACY_REQUIRED];
// to match the proto2 behavior, EXPANDED is set at the field level
repeated int32 scores = 3 [features.repeated_field_encoding = EXPANDED];
enum Handed {
// this overrides the default edition 2023 behavior, which is OPEN
option features.enum_type = CLOSED;
HANDED_UNSPECIFIED = 0;
HANDED_LEFT = 1;
HANDED_RIGHT = 2;
HANDED_AMBIDEXTROUS = 3;
}
Handed handed = 4;
reserved gender;
}
本节展示了一个 proto3 文件,以及运行 Prototiller 工具将定义文件更改为使用 Protobuf Editions 语法后它可能是什么样子。
Proto3 语法
// proto3 file
syntax = "proto3";
package com.example;
message Player {
// in proto3, optional fields have explicit presence
optional string name = 1 [default = "N/A"];
// in proto3 no specified field rule defaults to implicit presence
int32 id = 2;
// in proto3 this is packed by default
repeated int32 scores = 3;
enum Handed {
HANDED_UNSPECIFIED = 0;
HANDED_LEFT = 1;
HANDED_RIGHT = 2;
HANDED_AMBIDEXTROUS = 3;
}
// in proto3 enums are open
optional Handed handed = 4;
reserved "gender";
}
Editions 语法
// Editions version of proto3 file
edition = "2023";
package com.example;
message Player {
// fields have explicit presence, so no explicit setting needed
string name = 1 [default = "N/A"];
// to match the proto3 behavior, IMPLICIT is set at the field level
int32 id = 2 [features.field_presence = IMPLICIT];
// PACKED is the default state, and is provided just for illustration
repeated int32 scores = 3 [features.repeated_field_encoding = PACKED];
enum Handed {
HANDED_UNSPECIFIED = 0;
HANDED_LEFT = 1;
HANDED_RIGHT = 2;
HANDED_AMBIDEXTROUS = 3;
}
Handed handed = 4;
reserved gender;
}
Editions 语法支持词汇作用域,每个功能都有一个允许的目标列表。
例如,在第一版中,只能在文件级别或最低粒度级别指定功能。
词汇作用域的实现使您能够在整个文件中设置功能的默认行为,然后在消息、字段、枚举、枚举值、oneof、服务或方法级别覆盖该行为。
当在同一范围(字段、枚举值)内未进行任何设置时,将应用在更高级别(文件、消息)进行的设置。
任何未明确设置的功能都符合 .proto
文件使用的版本中定义的行为。
以下代码示例显示了在文件、字段和枚举级别设置的一些功能。 设置位于突出显示的行中:
edition = "2023";
option features.enum_type = CLOSED;
message Person {
string name = 1;
int32 id = 2 [features.field_presence = IMPLICIT];
enum Pay_Type {
PAY_TYPE_UNSPECIFIED = 1;
PAY_TYPE_SALARY = 2;
PAY_TYPE_HOURLY = 3;
}
enum Employment {
option features.enum_type = OPEN;
EMPLOYMENT_UNSPECIFIED = 0;
EMPLOYMENT_FULLTIME = 1;
EMPLOYMENT_PARTTIME = 2;
}
Employment employment = 4;
}
在前面的例子中,存在功能设置为 IMPLICIT
;如果未设置,则默认为 EXPLICIT
。
Pay_Type 枚举将处于 CLOSED
状态,因为它应用了文件级设置。
但是,Employment
枚举将处于 OPEN
状态,因为它是在枚举内设置的。
目前,所有版本格式的转换均由 Protobuf 团队处理。
当转换为自助服务模式时,我们将提供迁移指南和迁移工具,以简化版本之间的迁移。 该工具名为 Prototiller,可让您:
我们正在开发 Protobuf Editions,力求对现有系统影响最小。 例如,您可以将 proto2 和 proto3 的定义文件导入到 editions 版本的定义文件中,反之亦然:
// file myproject/foo.proto
syntax = "proto2";
enum Employment {
EMPLOYMENT_UNSPECIFIED = 0;
EMPLOYMENT_FULLTIME = 1;
EMPLOYMENT_PARTTIME = 2;
}
// file myproject/edition.proto
edition = "2023";
import "myproject/foo.proto";
尽管从 proto2 或 proto3 迁移到 editions 版本时生成的代码可能会有所不同,但其底层的数据传输格式保持不变。 因此,您仍然可以使用 editions 语法定义的 proto 文件来读取和写入 proto2 或者 proto3 格式的数据文件或数据流。
与 proto2 和 proto3 相比,editions 版本有一些语法上的变化。
语法说明: 不再使用 syntax
元素,而是使用 edition
元素:
syntax = "proto2";
syntax = "proto3";
edition = "2028";
保留名称: 保留字段名和枚举值名时,不再需要使用引号:
reserved foo, bar;
群组语法: proto2 中可用的群组语法在 editions 中已被移除。群组使用的特殊传输格式仍然可以通过使用 DELIMITED
消息编码来获得。
Required 标签: 仅在 proto2 中可用的 required 标签在 editions 中不可用。
底层功能仍然可用(但不推荐),可以通过使用 features.field_presence=LEGACY_REQUIRED
来实现。