概覽
它類似於 JSON,但更小更快,並且生成原生語言繫結。你只需定義一次資料的結構方式,然後就可以使用專門生成的原始碼,輕鬆地將結構化資料寫入和讀取到各種資料流中,並使用各種語言。
Protocol Buffers 是定義語言(在 .proto 檔案中建立)、proto 編譯器生成的用於與資料互動的程式碼、特定於語言的執行時庫、寫入檔案(或透過網路連線傳送)的資料序列化格式以及序列化資料的組合。
Protocol Buffers 解決了哪些問題?
Protocol Buffers 為大小不超過幾兆位元組的型別化結構化資料包提供了一種序列化格式。該格式適用於短暫的網路流量和長期資料儲存。Protocol Buffers 可以用新資訊進行擴充套件,而無需使現有資料失效或要求更新程式碼。
Protocol Buffers 是 Google 最常用的資料格式。它們廣泛用於伺服器間通訊以及磁碟上資料的歸檔儲存。Protocol Buffer 訊息 和 服務 由工程師編寫的 .proto 檔案描述。以下顯示了一個示例 message
edition = "2023";
message Person {
string name = 1;
int32 id = 2;
string email = 3;
}
proto 編譯器在構建時對 .proto 檔案進行呼叫,以生成各種程式語言的程式碼(本主題後面 跨語言相容性 中涵蓋),以操作相應的 protocol buffer。每個生成的類都包含每個欄位的簡單訪問器以及用於將整個結構序列化和解析為原始位元組的方法。以下向您展示了一個使用這些生成方法的示例
Person john = Person.newBuilder()
.setId(1234)
.setName("John Doe")
.setEmail("jdoe@example.com")
.build();
output = new FileOutputStream(args[0]);
john.writeTo(output);
由於 protocol buffers 在 Google 的各種服務中廣泛使用,並且其中的資料可能會持續一段時間,因此保持向後相容性至關重要。Protocol buffers 允許無縫支援對任何 protocol buffer 的更改,包括新增新欄位和刪除現有欄位,而不會破壞現有服務。有關此主題的更多資訊,請參閱本主題後面的 在不更新程式碼的情況下更新 Proto 定義。
使用 Protocol Buffers 的好處是什麼?
Protocol buffers 非常適合任何需要以與語言無關、與平臺無關、可擴充套件的方式序列化結構化、記錄式、型別化資料的情況。它們最常用於定義通訊協議(與 gRPC 一起)和資料儲存。
使用 protocol buffers 的一些優點包括
- 緊湊的資料儲存
- 快速解析
- 支援多種程式語言
- 透過自動生成的類最佳化功能
跨語言相容性
相同的訊息可以由用任何支援的程式語言編寫的程式碼讀取。你可以讓一個平臺上的 Java 程式從一個軟體系統捕獲資料,根據 .proto 定義對其進行序列化,然後在一個在另一個平臺上執行的單獨的 Python 應用程式中從該序列化資料中提取特定值。
以下語言直接在 protocol buffers 編譯器 protoc 中受支援
以下語言由 Google 支援,但專案的原始碼位於 GitHub 儲存庫中。protoc 編譯器對這些語言使用外掛
其他語言不受 Google 直接支援,而是由其他 GitHub 專案支援。這些語言涵蓋在 Protocol Buffers 的第三方附加元件 中。
跨專案支援
你可以透過在特定專案程式碼庫之外的 .proto 檔案中定義 message 型別來跨專案使用 protocol buffers。如果你正在定義你預計會在你的直接團隊之外廣泛使用的 message 型別或列舉,你可以將它們放在自己的檔案中,不帶任何依賴項。
Google 內部廣泛使用的 proto 定義的幾個示例是 timestamp.proto 和 status.proto。
在不更新程式碼的情況下更新 Proto 定義
軟體產品通常是向後相容的,但向前相容則不那麼常見。只要在更新 .proto 定義時遵循一些 簡單實踐,舊程式碼將毫無問題地讀取新訊息,忽略任何新新增的欄位。對於舊程式碼,已刪除的欄位將具有其預設值,已刪除的重複欄位將為空。有關“重複”欄位的資訊,請參閱本主題後面的 Protocol Buffers 定義語法。
新程式碼也將透明地讀取舊訊息。新欄位將不會出現在舊訊息中;在這些情況下,protocol buffers 提供一個合理的預設值。
Protocol Buffers 不適合什麼時候?
Protocol Buffers 並不適合所有資料。特別是
- Protocol Buffers 傾向於假設整個訊息可以一次性載入到記憶體中,並且不大於物件圖。對於超過幾兆位元組的資料,請考慮不同的解決方案;處理更大的資料時,由於序列化副本,您最終可能會有效地獲得資料的幾個副本,這可能會導致記憶體使用量出現驚人的峰值。
- 當 protocol buffers 序列化時,相同的資料可以有許多不同的二進位制序列化。您無法在不完全解析它們的情況下比較兩個訊息是否相等。
- 訊息未壓縮。雖然訊息可以像其他檔案一樣進行 zip 或 gzip 壓縮,但像 JPEG 和 PNG 使用的專用壓縮演算法將為適當型別的資料生成更小的檔案。
- 對於許多涉及大型多維浮點數陣列的科學和工程用途,Protocol Buffer 訊息在大小和速度方面都不是最高效的。對於這些應用程式,FITS 和類似格式的開銷較小。
- Protocol buffers 在科學計算中流行的非面嚮物件語言(如 Fortran 和 IDL)中支援不佳。
- Protocol buffer 訊息本身不描述其資料,但它們具有一個完全反射的模式,您可以使用它來實現自描述。也就是說,如果沒有對其相應
.proto檔案的訪問許可權,您無法完全解釋它。 - Protocol Buffers 不是任何組織的正式標準。這使得它們不適合在具有法律或其他要求以標準為基礎構建的環境中使用。
誰在使用 Protocol Buffers?
許多專案都使用 protocol buffers,包括以下
Protocol Buffers 是如何工作的?
下圖展示瞭如何使用 protocol buffers 處理資料。

圖 1. Protocol buffers 工作流程
protocol buffers 生成的程式碼提供了實用方法,用於從檔案和流中檢索資料、從資料中提取單個值、檢查資料是否存在、將資料序列化迴文件或流以及其他有用的功能。
以下程式碼示例向您展示了 Java 中的此流程示例。如前所示,這是一個 .proto 定義
message Person {
string name = 1;
int32 id = 2;
string email = 3;
}
編譯此 .proto 檔案會建立一個 Builder 類,您可以使用它來建立新例項,如以下 Java 程式碼所示
Person john = Person.newBuilder()
.setId(1234)
.setName("John Doe")
.setEmail("jdoe@example.com")
.build();
output = new FileOutputStream(args[0]);
john.writeTo(output);
然後,您可以使用 protocol buffers 在其他語言(如 C++)中建立的方法來反序列化資料
Person john;
fstream input(argv[1], ios::in | ios::binary);
john.ParseFromIstream(&input);
int id = john.id();
std::string name = john.name();
std::string email = john.email();
Protocol Buffers 定義語法
定義 .proto 檔案時,您可以指定基數(單數或重複)。在 proto2 和 proto3 中,您還可以指定欄位是否可選。在 proto3 中,將欄位設定為可選會將其從隱式存在更改為顯式存在。
設定欄位的基數後,您指定資料型別。Protocol buffers 支援常見的原始資料型別,例如整數、布林值和浮點數。有關完整列表,請參閱標量值型別。
欄位也可以是
message型別,以便您可以巢狀定義的各個部分,例如用於重複的資料集。enum型別,以便您可以指定一組值以供選擇。oneof型別,當訊息有許多可選欄位且同時最多設定一個欄位時,可以使用此型別。map型別,用於向您的定義新增鍵值對。
訊息可以允許擴充套件在訊息本身之外定義欄位。例如,protobuf 庫的內部訊息模式允許為自定義、特定用途的選項提供擴充套件。
有關可用選項的更多資訊,請參閱 proto2、proto3 或 2023 版 的語言指南。
設定基數和資料型別後,您為欄位選擇一個名稱。設定欄位名稱時需要記住一些事項
- 在生產中使用後,有時很難甚至不可能更改欄位名稱。
- 欄位名稱不能包含破折號。有關欄位名稱語法的更多資訊,請參閱訊息和欄位名稱。
- 對重複欄位使用複數名稱。
為欄位分配名稱後,您分配一個欄位編號。欄位編號不能重新利用或重用。如果您刪除一個欄位,您應該保留其欄位編號以防止有人意外重用該編號。
額外的資料型別支援
Protocol buffers 支援多種標量值型別,包括使用可變長度編碼和固定大小的整數。您還可以透過定義自身是資料型別的訊息來建立自己的複合資料型別,您可以將其分配給欄位。除了簡單和複合值型別之外,還發布了幾個常見型別。
歷史
要閱讀 protocol buffers 專案的歷史,請參閱Protocol Buffers 的歷史。
Protocol Buffers 開源理念
Protocol buffers 於 2008 年開源,旨在為 Google 之外的開發者提供我們內部所獲得的相同好處。我們透過對語言的定期更新來支援開源社群,因為我們對語言進行了這些更改以支援我們的內部需求。雖然我們接受外部開發者的一些拉取請求,但我們不能總是優先考慮不符合 Google 特定需求的功能請求和錯誤修復。
開發者社群
要了解 Protocol Buffers 的即將更改並與 protobuf 開發者和使用者聯絡,請加入 Google Group。