1. 程式人生 > >Protocol Buffers 開發者指南

Protocol Buffers 開發者指南

歡迎來到 protocol buffers 的開發者指南。protocol buffers 是一個語言中立,平臺中立針對通訊協議,資料儲存和其他領域中對結構化資料進行序列化的擴充套件方法。

本文件主要針對的是 Java,C++ 或 Python 的開發人員希望在開發的應用程式中使用 Protocol Buffers。這個有關 Protocol Buffers 摘要性的介紹將會告訴你如何開始使用 Protocol Buffers。如果你希望更加深入的瞭解有關 Protocol Buffers 的內容,你可以進入 tutorials 或者 

protocol buffer encoding 頁面來詳細瞭解。

有關 API 的參考文件,請參考頁面:reference documentation 這裡提供了所有這 3 種語言的參考,同時也針對 .proto language 和 style 提供相關的指南。

什麼是 Protocol Buffers?

Protocol buffers 是對結構化資料序列化的一個靈活,高效,自動化工具 —— 你可以將 Protocol buffers 想象成 XML,但是體積更小,更快也更加簡單。

你可以自己定義你的結構化資料,然後你可以使用特定的程式碼生成工具來非常容易對你的結構化資料進行讀取和寫入。這些資料的讀取和寫入可以是一系列的資料流和使用不同的計算機程式語言。你甚至可以在不對已經部署的程式進行破壞的情況下更新你的資料結構。

Protocol Buffers 是如何進行工作的

你需要制定你希望如何將你的資料進行序列化。你是通過 proto 檔案來定義你的訊息結構化資料的。

每一 protocol buffer message 是一個小的資訊記錄邏輯,這個訊息中包含有一系列的名字,變數對照序列。下面是一些基本的.proto 檔案,這些檔案中定義了一個訊息,這個訊息包含有一個 person 資訊:

message Person {

  required string name = 1;

  required int32 id = 

2;

  optional string email = 3;

 

  enum PhoneType {

    MOBILE = 0;

    HOME = 1;

    WORK = 2;

  }

 

  message PhoneNumber {

    required string number = 1;

    optional PhoneType type = 2 [default = HOME];

  }

 

  repeated PhoneNumber phone = 4;

}

通過上面你可以看到這個訊息的格式非常簡單—— 每一個訊息型別都有一個或者多個唯一進行編號的欄位,每一個欄位包含有一個名字和變數型別。

變數可以為數字(整形或者浮點型)(numbers),布林型別(booleans),字串(strings),原生二進位制(raw bytes)甚至其他的 protocol buffer 訊息型別,能夠允許你分級的結構化你的資料。

你可以將欄位指定為可選欄位(optional fields),必須欄位(required fields)和重複欄位(repeated fields)。你可以從下面的 Protocol Buffer Language Guide 頁面中找到更多有關 .proto 的定義。

一旦你成功定義了你的訊息,你可以針對你使用的語言使用你定義的 .proto 來執行 protocol buffer 編譯器(protocol buffer compiler)來生成資料訪問類。

針對每一個欄位,在資料訪問類中提供了簡單的訪問方法(例如 name() 和 set_name())和序列化到原生 2 進位制資料和從原生 2 進位制資料反序列化的方法。

針對上面的定義,如果你現在使用的是  C++ 語言的話,當你把訊息定義進行編譯後,你將會得到一個稱為 Person 的類。 你可以使用這個類在你的應用程式中進行填充資料,對資料進行序列化和從序列化的資料中(protocol buffer 訊息)重新獲得 Person 資料。

然後你可以寫一些類似 Person person; 的程式碼。

Person person;

person.set_name("John Doe");

person.set_id(1234);

person.set_email("[email protected]");

fstream output("myfile", ios::out | ios::binary);

person.SerializeToOstream(&output);

隨後,你可以對訊息進行讀取:

fstream input("myfile", ios::in | ios::binary);

Person person;

person.ParseFromIstream(&input);

cout << "Name: " << person.name() << endl;

cout << "E-mail: " << person.email() << endl;

你可以向你的訊息中新增新的欄位而不會損壞老的訊息。這是因為在老的訊息處理中,針對新的欄位是完全忽略掉的。因此,如果你在你的通訊協議中使用 protocol buffers 為資料結構的話,你可以對你的協議和訊息進行擴充套件而不需要擔心老的程式碼沒有辦法編譯通過,或者損壞老的程式碼。

你可以訪問 API Reference section 頁面中的內容來了解完整 protocol buffer 程式碼的生成和使用。

你也可以在 Protocol Buffer Encoding 頁面中瞭解更多protocol buffer 訊息是如何進行編碼的。

為什麼不使用 XML

針對 XML 來說 Protocol Buffers 具有更多的優勢來對序列化結構資料。

  • 更加簡單
  • 小於 XML  3 到 10 倍
  • 快於 XML 20 到 100 倍
  • 鬆耦合
  • 使用程式工具來建立資料訪問類,使數訪問類更加簡單

假設,你需要講 person 這個資料進行定義,在 XML 你需要使用:

<person>

  <name>John Doe</name>

  <email>jdoe@example.com</email>

</person>

來進行定義。

在 Protocol Buffers 中針對上面的訊息文字化(text format)後顯示為:

# Textual representation of a protocol buffer.

# This is *not* the binary format used on the wire.

person {

  name: "John Doe"

  email: "[email protected]"

}

當上面的訊息被編碼為 Protocol Buffer 二進位制格式(binary format)上面的文字可能小於 28 bytes,並且可能需要 100-200 納秒(nanoseconds)來進行處理。

我們將上面轉換為可以人為讀取的目的主要是為進行除錯和編輯。

如果你使用 XML 的話,上面的資訊至少需要 69 bytes (你需要刪除所有的空格),同時你需要 5,000-10,000 納秒(nanoseconds)來進行處理。

同時,對 protocol buffer 進行操作也是非常容易的:

cout << "Name: " << person.name() << endl;

cout << "E-mail: " << person.email() << endl;

如果使用的是 XML 的話,你需要進行下面的操作:

cout << "Name: "

     << person.getElementsByTagName("name")->item(0)->innerText()

     << endl;

cout << "E-mail: "

     << person.getElementsByTagName("email")->item(0)->innerText()

     << endl;

但是,protocol buffers 並不是任何時候都會比 XML 好。例如,針對基於文字的標記語言(例如,XML),protocol buffers 就不是一個很好的選項,因為你不能使用 protocol buffer 更好的在文件中進行交換。更主要的是 HTML 是人類可以閱讀和編輯的。protocol buffer 也不是不可以人為的讀取,但是針對原生的 protocol buffer 格式是沒有辦法人為進行讀取和編輯的。

XML 與  HTML 一樣,在某種程度上是一種自我描述資料。protocol buffer 只針對你在 .proto 檔案中描述的內容進行表達。

看起來像一個解決方案,我應該如何開始呢?

Download the package – 這包中含有針對 Java, Python, 和 C++ protocol buffer 編譯器原始碼,和你需要進行 I/O 和測試的類。希望對你的編譯器進行編譯和構建,請參考程式碼中的 README 檔案。

一旦你完成了所有的設定,請參考 tutorial 頁面中的內容來選擇你需要的語言——這個能夠幫助你使用 protocol buffer 建立一個簡單的應用程式。

介紹 proto3

在我們最新的 version 3 發行版 中推出了新的語言版本 —— Protocol Buffers language version 3(另稱 proto3),在這個版本中針對我們已經存在的語言版本(proto2)使用了一些新的特性。

Proto3 簡化了 protocol buffer 語言,使其更加容易使用並且能夠支援更多的語言:我們當前發行的 proto3 能夠讓你建立 Java, C++, Python, Java Lite, Ruby, JavaScript, Objective-C, and C#。

另外你也可以通過使用 Go protoc 外掛來用 proto3 建立 Go 程式碼,這個外掛你可以到 golang/protobuf Github 中下載到。更多的語言還在逐步進行支援中。

請注意,這 2 個版本的 API 並不是完全相容的。為了照顧還在使用老版本的使用者,我們將會在新的 protocol buffers 發行中同時支援老的版本。

你可以在下面的發行日志(release notes)檢視 2 個版本的主要不同。有關 proto3 的句法,請參考 Proto3 Language Guide 中的內容,針對 proto3 的完整文件還沒有編寫完成,將會隨後推出。

看起來 proto2 和 proto3 可能會產生一些混淆,這是因為原始的開源  protocol buffers 實際上是 Google 內部語言的第二個版本,同時我們的開源版本也是從 v2.0.0 開始的。簡單來說就是 proto 最開始的版本是 Google 內部使用的,在 proto 第二個版本的時候,Google 決定進行開源了,所以開源的 proto 是從 proto2 開始的。

一個簡短的歷史

Protocol buffers 最開始是在 Google 內部進行開發的,用於處理在索引伺服器上請求/響應(request/response)的協議。

在 Protocol buffers 之前,針對請求和響應,使用的是 marshalling/unmarshalling,這個能夠支援一系列的協議。但是結果看起來卻是非常的難看,例如:

if (version == 3) {

  ...

else if (version > 4) {

  if (version == 5) {

    ...

  }

  ...

}

明確格式化的的協議也使新版本的協議更加難以推出,這是因為開發者必須能夠了解老協議在伺服器之間是如何進行處理的,同時也需要了解新的協議。只有對新老協議都有所瞭解後才能逐步使用新的協議替換老的協議。

Protocol buffers 被用來設計解決上面的很多問題:

  • 新的欄位比較能夠容易的進行定義,中級伺服器不需要對資料進行檢查,直接對資料進行處理,同時也可以直接傳輸資料而不需要了解資料是如何進行定義的。
  • 格式使用自描述,能夠更加容易支援更多的語言(C++,Java 等)。

但是,使用者還是需要手動書寫他們自己的處理diam。

作為系統的進化來說,它獲得了許多其他的特性和用途:

  • 自動生成序列化和反序列化程式碼而避免手動書寫這些程式碼。
  • 除了開始使用短期RPC(遠端過程呼叫)請求,人們開始使用 protocol buffers 作為高效的自描述結構化資料格式(主要針對資料短期存在,例如在 Bigtable)。
  • 伺服器RPC介面開始被宣告為協議檔案的一部分,協議編譯器生成根類,使用者可以通過伺服器介面的實現和過載它們。

Protocol buffers 在 Google 中成為針對資料的通用語言—— 隨著時間的流逝,在 Google 內部已經有超過 348,952 .proto 檔案被定義。這些被用在 RPC 系統和儲存系統中儲存資料。

https://www.cwiki.us/display/ProtocolBuffer