Protobuf 版本概觀

Protobuf 版本功能的概觀。

Protobuf 版本取代了我們用於 Protocol Buffers 的 proto2 和 proto3 指示。您不再需要在 proto 定義檔案的頂端加入 syntax = "proto2"syntax = "proto3",而是使用版本號碼 (例如 edition = "2023") 來指定檔案的預設行為。版本可以讓語言隨著時間逐步演變。

版本代表一組 功能,而非舊版本硬式編碼的行為,每個功能都有一個預設值 (行為)。功能是檔案、訊息、欄位、列舉等等的選項,用來指定 protoc、程式碼產生器和 protobuf 執行階段的行為。當您的需求與您所選版本預設行為不符時,您可以在不同層級 (檔案、訊息、欄位等等) 明確覆寫行為。您也可以覆寫您的覆寫。本主題稍後的「詞法作用域」章節會更詳細地說明。

最新發佈的版本是 2023。

功能的生命週期

版本為功能的生命週期提供基本增量。功能具有預期的生命週期:導入、變更其預設行為、棄用,然後移除。例如:

  1. 版本 2031 建立 feature.amazing_new_feature,其預設值為 false。此值與所有較早版本維持相同的行為。也就是說,預設沒有影響。

  2. 開發人員將其 .proto 檔案更新為 edition = "2031"

  3. 較晚的版本,例如版本 2033,將 feature.amazing_new_feature 的預設值從 false 切換為 true。這是所有 proto 的期望行為,也是 protobuf 團隊建立此功能的原因。

    使用 Prototiller 工具將較早版本的 proto 檔案遷移到版本 2033 會根據需要加入明確的 feature.amazing_new_feature = false 項目,以繼續保留先前的行為。開發人員會在希望新行為套用至其 .proto 檔案時移除這些新加入的設定。

  1. 在某個時間點,feature.amazing_new_feature 會在某個版本中被標記為已棄用,並在較晚的版本中移除。

    當移除某項功能時,該行為的程式碼產生器和支援它的執行階段程式庫也可能會被移除。不過,時間軸會很寬鬆。依照生命週期較早步驟的範例,棄用可能會在版本 2034 發生,但直到版本 2036 才移除,大約在兩年後。移除某項功能一律會啟動主要版本更新。

由於此生命週期,任何未使用已棄用功能的 .proto 檔案,從一個版本升級到下一個版本都將是無作業的。您將有完整的 Google 遷移期,加上棄用期,才能升級程式碼。

先前的生命週期範例使用布林值來表示功能,但功能也可以使用列舉。例如,features.field_presence 具有值 LEGACY_REQUIREDEXPLICITIMPLICIT

遷移至 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;如果未設定,則預設為 EXPLICITPay_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