Protobuf 版本概觀
Protobuf 版本取代了我們用於 Protocol Buffers 的 proto2 和 proto3 指示。您不再需要在 proto 定義檔案的頂端加入 syntax = "proto2"
或 syntax = "proto3"
,而是使用版本號碼 (例如 edition = "2023"
) 來指定檔案的預設行為。版本可以讓語言隨著時間逐步演變。
版本代表一組 功能,而非舊版本硬式編碼的行為,每個功能都有一個預設值 (行為)。功能是檔案、訊息、欄位、列舉等等的選項,用來指定 protoc、程式碼產生器和 protobuf 執行階段的行為。當您的需求與您所選版本預設行為不符時,您可以在不同層級 (檔案、訊息、欄位等等) 明確覆寫行為。您也可以覆寫您的覆寫。本主題稍後的「詞法作用域」章節會更詳細地說明。
最新發佈的版本是 2023。
功能的生命週期
版本為功能的生命週期提供基本增量。功能具有預期的生命週期:導入、變更其預設行為、棄用,然後移除。例如:
版本 2031 建立
feature.amazing_new_feature
,其預設值為false
。此值與所有較早版本維持相同的行為。也就是說,預設沒有影響。開發人員將其 .proto 檔案更新為
edition = "2031"
。較晚的版本,例如版本 2033,將
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
。
遷移至 Protobuf 版本
版本不會破壞現有二進位檔,也不會變更訊息的二進位檔、文字或 JSON 序列化格式。第一個版本會盡量降低干擾。第一個版本會建立基準,並將 proto2 和 proto3 定義合併為新的單一定義格式。
當發佈後續版本時,功能的預設行為可能會變更。您可以讓 Prototiller 對您的 .proto 檔案執行無作業轉換,或者您可以選擇接受部分或所有新的行為。版本計劃大約每年發佈一次。
Proto2 至版本
本節顯示一個 proto2 檔案,以及執行 Prototiller 工具將定義檔案變更為使用 Protobuf 版本語法後,它可能看起來的樣子。
Proto2 語法
// proto2 file
syntax = "proto2";
package com.example;
message Player {
// in proto2, optional fields have explicit presence
optional string name = 1;
// 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";
}
版本語法
// Edition version of proto2 file
edition = "2023";
package com.example;
message Player {
// fields have explicit presence, so no explicit setting needed
string name = 1;
// 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 至版本
本節顯示一個 proto3 檔案,以及執行 Prototiller 工具將定義檔案變更為使用 Protobuf 版本語法後,它可能看起來的樣子。
Proto3 語法
// proto3 file
syntax = "proto3";
package com.example;
message Player {
// in proto3, optional fields have explicit presence
optional string name = 1;
// 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 version of proto3 file
edition = "2023";
package com.example;
message Player {
// fields have explicit presence, so no explicit setting needed
string name = 1;
// 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;
}
詞法作用域
版本語法支援詞法作用域,並具有每個功能允許目標的清單。例如,在第一個版本中,功能只能在檔案層級或最低的粒度層級指定。詞法作用域的實作可讓您為整個檔案的功能設定預設行為,然後在訊息、欄位、列舉、列舉值、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
enum
將為 CLOSED
,因為它會套用檔案層級設定。但是,Employment
enum
將為 OPEN
,因為它是在列舉內設定。
Prototiller
目前,所有轉換為版本格式的作業都由 Protobuf 團隊處理。
當此作業轉移為自助式模型時,我們將提供遷移指南和遷移工具,以便輕鬆遷移至版本之間和版本之間。該工具稱為 Prototiller,可讓您
- 大規模地將 proto2 和 proto3 定義檔案轉換為新的版本語法
- 將檔案從一個版本遷移到另一個版本
- 以其他方式操作 proto 檔案
回溯相容性
我們正在建置 Protobuf 版本,使其盡可能降低干擾。例如,您可以將 proto2 和 proto3 定義匯入以版本為基礎的定義檔案,反之亦然
// 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 移至版本時,產生的程式碼會變更,但線路格式不會變更。您仍然可以使用以版本語法為基礎的 proto 定義來存取 proto2 和 proto3 資料檔案或檔案串流。
語法變更
與 proto2 和 proto3 相比,版本中存在一些語法變更。
語法描述。您不再使用 syntax
元素,而是使用 edition
元素
syntax = "proto2";
syntax = "proto3";
edition = "2028";
保留名稱。您在保留欄位名稱和列舉值名稱時,不再將其放在引號中
reserved foo, bar;
群組語法。版本中已移除 proto2 中提供的群組語法。仍然可以使用 DELIMITED
訊息編碼來使用群組使用的特殊線路格式。
必要標籤。版本中無法使用僅在 proto2 中提供的 required
標籤。仍然可以使用基礎功能 (但是不建議),方法是使用 features.field_presence=LEGACY_REQUIRED
。