1. 程式人生 > >Protocol Buffers程式設計指南與應用

Protocol Buffers程式設計指南與應用

Protocol Buffers
Protocol buffers,簡稱Protobuf,是一個獨立於程式語言,獨立於平臺,且可拓展的自動序列化結構資料的機制。
本文件服務於想要在程式中使用Protobuf的JAVA,C++,Python開發者。概述中簡單介紹了Protobuf,接下來將引導讀者一步步開發自己的Protobuf程式。——包括更深一步的protocol buffer encoding。API reference documentation提供三種程式語言語法支援以及在書寫.proto檔案時的程式設計風格推薦。

開發者指導

什麼是protocol buffers?

Protocol buffers提供了一種靈活、高效、自動序列化結構資料的機制,類比於 XML,但是它比 XML 更小、更快、更簡單。僅需要自定義一次你所需的資料格式,然後使用者就可以使用 Protobuf 編譯器自動生成各種語言的原始碼,方便的讀寫使用者自定義的格式化的資料。與語言無關,與平臺無關,還可以在不破壞原資料格式的基礎上,依據原有資料格式,更新現有的資料格式。

Protobuf如何工作

使用者在 .proto 檔案中定義 message 來指定所需要序列化的資料格式。每一個 message 都是一個小的資訊邏輯單元,包含一系列的 name-value 值對。下面舉例來說明一個簡單的 .proto 檔案,它定義了一條包含 Person 資訊的 message。

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

從以上程式碼來看,message 格式非常簡單。每種型別的 message 包含一個或者多個唯一編碼欄位,每個欄位由名稱和值型別組成,值型別可以是數字(整形或者浮點型)、布林值、字串、原始位元組,甚至是其他的 message(如上例所示)。Protobuf 允許 message 中包含 message,以達到分層巢狀。message 中可以定義 optional 欄位、required 欄位和 repeated 欄位。

定義好 message 後,執行 Protobuf 編譯器編譯 .proto 檔案,就可以生成存取資料的相關類。這些類包括簡單的設定及讀取欄位(比如 name() 和 set_name() )的方法,也包括整個資料結構的 message 和原始位元組之間的序列化/反序列化的轉換方法。舉個例子,如果你選擇的是 C++ 語言,執行 Protobuf 編譯器編譯以上 .proto 檔案生成 Person 類。你就能使用這個類去填充、序列化、檢索 Person message。如下程式碼:

Person person;
person.set_name("zebra");
person.set_id(123);
person.set_email("[email protected]");
fstream output("myfile", ios::out | ios::binary);
person.SerializeToOstream(&output);

接下來,用如下程式碼來讀取 message:

fstream input("myfile", ios::in | ios::binary);
Person person;
person.ParseFromIstream(&input);
cout << "Name: " << person.name() << endl;
cout << "E-mail" << person.email() << endl;

Protobuf 是易於擴充套件的,可以向後相容,我們可以在 message 中新增新欄位,在解析的時候,老版本的資料就會忽略新增加的欄位。因此,如果現有通訊協議使用 Protobuf 做為其資料格式,可以直接擴充套件該通訊協議,而不必擔心著將會破壞現有的程式碼。關於使用 .proto 檔案生成目的碼,後面將會介紹。

為什麼不直接使用XML

Protocol buffers在序列化結構資料上比XML具有更多的優勢:

  • 更簡單
  • 小3-10倍
  • 快20-100倍
  • 歧義更少
  • 生成的類更容易使用
    舉個栗子,如果你想儲存一個人的資訊,包括姓名和郵箱地址,下面是XML的寫法:
  <person>
    <name>John Doe</name>
    <email>[email protected]</email>
  </person>

而對應的protocol buffer message(使用protocol buffer 格式)如下:

# Textual representation of a protocol buffer.
# This is *not* the binary format used on the wire.
person {
  name: "John Doe"
  email: "[email protected]"
}

當這個message被編碼成protocol buffer二進位制格式時(以上程式碼只是為了方便閱讀和除錯),只有大概28個位元組長,100-200ns即可解析。而XML版本則最少需要69個位元組,並且需要5,000-10,000ns解析時長。

同樣的,操縱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 buffer也並非總是比XML好——比如說,protocol buffers並不適用於組建基於文字的文件標記,因為我們不能用簡單的方法在文字中插入結構。此外,XML是易讀性,和可編輯性更好的語言;protocol buffers的原始格式則不盡然。XML在某種程度上,是自解釋的,而protocol buffers必須有訊息定義(.proto檔案)才能看出來是什麼意思。

聽起來很不錯,哪怎麼開始呢?

Download the package-包含了適用於Java,Python,和C++語言的原始碼及編譯器,以及I/O類和測試範例。根據README檔案的指示,可以方便的安裝編譯器。
一旦上面的都做完了,請嘗試跟著你所選擇語言的入門指導完整的做一遍,將有助於你通過簡單的例子來了解如何使用protocol buffers。

proto3介紹

最新版本是版本3,它有全新的語言版本:Protocol Buffers 3,和一些已經存在於proto2中的新特性。Proto3簡化了protocol buffer語言,不僅易用性大大提升,而且支援更多程式語言:Java,C++,Python,Java Lite,Ruby,JavaScript,Objective-C,和C#。此外通過最新的Go protoc外掛,還能生成用於Go語言的proto3程式碼,戳此github倉庫golang/protobuf。更多的語言支援正在開發中。

目前我們只建議你在以下情況之一選擇proto3:

  • 你的專案所使用的語言是proto3剛剛支援的。
  • 希望嘗試使用我們最新的開源軟體gRPC-強烈建議使用proto3來避免相容性問題。

請注意,proto3和proto2的API並不是完全相容的。為了避免相容性問題,我們將在新的protocol buffers發行版本中支援先前的語言。

在當前的發行版本文件中可以瞭解proto3語法的主要變化,proto3的開發者文件即將出版!

(如果proto2和proto3的名字讓你感到不解,這是因為我們當初開源protobuf時,它已經是谷歌的第二個版本,也被稱為proto2。這也是為什麼我們的開原版本號從v2.0.0開始的原因。)

歷史漫談

Protobuf最初是谷歌開發用來解決索引伺服器的請求/響應協議的。在使用Protobuf之前,有一種格式用以處理請求&響應資料的編碼和解碼工作,並且支援多種版本的協議。這使得寫出來的程式碼非常醜陋,比如:

if (version==3) {
    ...
}else if (version>4) {
    if (version==5) {
        ...
    }
    ...
}

通訊協議因此變得越來越複雜,因為開發者必須確保請求者和接受者互相相容,並且在一方開始使用新協議時,另外一方也要使用新的協議。

Protobuf可用於解決下類問題:

  • 自動生成序列化與反序列化程式碼,避免人工處理編碼問題。
  • 除了用於短期RPC(遠端過程呼叫)請求之外,開發者還使用該協議作為一種便捷的自描述格式,用於資料持久化。
  • RPC伺服器介面可以作為 .proto 檔案來描述,而通過ProtocolBuffer的編譯器生成存根(stub)類供使用者實現伺服器介面。

Protobuf現在已經是Google的混合語言資料標準了,現在已經正在使用的有超過48,162種報文格式定義和超過12,183個 .proto 檔案。他們用於RPC系統和持續資料儲存系統。