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 [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";
}

版本語法

// 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 至版本

本節顯示 proto3 檔案,以及執行 Prototiller 工具以將定義檔案變更為使用 Protobuf 版本語法後的外觀。

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 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;
}

詞法範圍

版本語法支援詞法範圍,以及每個特性的允許目標清單。例如,在第一個版本中,特性只能在檔案層級或最細微的層級指定。詞法範圍的實作讓您可以為整個檔案的特性設定預設行為,然後在訊息、欄位、列舉、列舉值、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

當 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 訊息編碼來使用。

required 標籤。 版本中無法使用僅在 proto2 中可用的 required 標籤。底層功能仍然可以透過使用 features.field_presence=LEGACY_REQUIRED 來使用。