資料交換利器 Protobuf 技術淺析

oliver_lv發表於2016-11-04

由於最近公司採用protocol buffer(以下簡稱protobuf)來作為不同應用之間的資料交換,故最近一段時間研究了protobuf相關技術。在這裡分享下。

protobuf是什麼?

protobuf是google旗下的一款平臺無關,語言無關,可擴充套件的序列化結構資料格式。所以很適合用做資料儲存和作為不同應用,不同語言之間相互通訊的資料交換格式,只要實現相同的協議格式即同一proto檔案被編譯成不同的語言版本,加入到各自的工程中去。這樣不同語言就可以解析其他語言通過protobuf序列化的資料。目前官網提供了C++,Python,JAVA,GO等語言的支援。

protobuf 語法定義

要想使用protobuf必須得先定義proto檔案。所以得先熟悉protobuf的訊息定義的相關語法。下面就來介紹

首先我們先定義一個proto檔案,結構如下:

上面我們主要定義了一個訊息,這個訊息包括文章ID,文章摘要,文章圖片。下面給出訊息定義的相關說明

message是訊息定義的關鍵字

required 表示這個欄位必須的,必須在序列化的時候被賦值。

optional 代表這個欄位是可選的,可以為0個或1個但不能大於1個。

repeated 則代表此欄位可以被重複任意多次包括0次。

int32和string是欄位的型別。後面是我們定義的欄位名。

最後的1,2,3則是代表每個欄位的一個唯一的編號標籤,在同一個訊息裡不可以重複。這些編號標籤用與在訊息二進位制格式中標識你的欄位,並且訊息一旦定義就不能更改。需要說明的是標籤在1到15範圍的採用一個位元組進行編碼。所以通常將標籤1到15用於頻繁發生的訊息欄位。編號標籤大小的範圍是1到229 – 1。此外不能使用protobuf系統預留的編號標籤(19000 -19999)。

當然protobuf支援更多的型別,比如bool,double,float,列舉,也可以是其他定義過的訊息型別譬如前面的訊息Article。支援的基本型別如下:

基本資料型別

下面讓我們定義一個資料比較多的article.proto檔案來再次說明下proto語法的相關內容,起碼通過列子可以更直觀的感受。

上面proto檔案,我們定義了enum列舉型別,巢狀的訊息。甚至對原有的訊息進行了擴充套件,也可以對欄位設定預設值。新增註釋等

此外reserved關鍵字主要用於保留相關編號標籤,主要是防止在更新proto檔案刪除了某些欄位,而未來的使用者定義新的欄位時重新使用了該編號標籤。這會引起一些問題在獲取老版本的訊息時,譬如資料衝突,隱藏的一些bug等。所以一定要用reserved標記這些編號標籤以保證不會被使用

當我們需要對訊息進行擴充套件的時候,我們可以用extensions關鍵字來定義一些編號標籤供第三方擴充套件。這樣的好處是不需要修改原來的訊息格式。就像上面proto檔案,我們用extend關鍵字來擴充套件。只要擴充套件的欄位編號標籤在extensions定義的範圍裡。

對於基本數值型別,由於歷史原因,不能被protobuf更有效的encode。所以在新的程式碼中使用packed=true可以更加有效率的encode。注意packed只能用於repeated 數值型別的欄位。不能用於string型別的欄位。

在訊息Other中我們看到定義了一個oneof關鍵字。這個關鍵字作用比較有意思。當你設定了oneof裡某個成員值時,它會自動清除掉oneof裡的其他成員,也就是說同一時刻oneof裡只有一個成員有效。這常用於你有許多optional欄位時但同一時刻只能使用其中一個,就可以用oneof來加強這種效果。但需要注意的是oneof裡的欄位不能用required,optional,repeted關鍵字

一般在我們的專案中肯定會有很多訊息型別。我們總不能都定義在一個檔案中。當一個proto檔案需要另一個proto檔案的時候,我們可以通過import匯入,就像下面這樣:

protobuf也提供了包的定義,只要在檔案開頭定義package關鍵字即可。主要是為了防止命名衝突,不過對於Python語言在編譯的時候會忽略包名。

很多時候我們會修改更新我們定義的proto檔案,如果不遵守一定規則的話,修改的後proto檔案可能會引發許多異常。在官網上對更新proto有以下幾點要求

1.不能改變已有的任何編號標籤。

2.只能新增optional和repeated的欄位。這樣舊程式碼能夠解析新的訊息,只是那些新新增的欄位會被忽略。但是序列化的時候還是會包含哪些新欄位。而新程式碼無論是舊訊息還是新訊息都可以解析。

3.非required的欄位可以被刪除,但是編號標籤不可以再次被使用,應該把它標記到reserved中去

4.非required可以被轉換為擴充套件欄位,只要欄位型別和編號標籤保持一致

5.相互相容的型別,可以從一個型別修改為另一個型別,譬如int32的欄位可以修改為int64

ptotobuf語法相對比較簡單,一般都能很快熟悉上手。這裡只是粗淺的介紹下,更多詳細內容可以參考https://developers.google.com/protocol-buffers/docs/proto

proto檔案編譯

現在我們有了proto檔案,需要把它編譯成我們需要的語言,這裡以python為例。通過以下命令生成我們需要的python程式碼,你會發現目錄多了一個article_pb2.py的檔案。

-I 指定搜尋proto檔案的目錄,這裡指定為當前目錄。-I 也可以寫成 –proto_path

–python_out 會將生成的python程式碼檔案放到等號後面指定的目錄,這裡也指定當前目錄。如果需要生成其他語言的程式碼譬如java換成–java_out即可。這裡提供一個官網提供的模版,如下

最後指定我們要編譯的proto檔案。

現在我們有了編譯後的article_pb2.py,加入到我們的專案中去該怎麼用呢?這個時候就需要用到google提供的protobuf python API。 下面我們通過例子來簡單介紹下API的使用

protobuf python api的使用

直接貼程式碼來看,詳細的說明都在註釋裡。主要的SerializeToString和ParseFromString2個方法。一個序列化,一個反序列化。

以上主要是通過python來操作protobuf序列化的資料,我們也可以將序列化後的資料通過網路發給其他應用。通過protobuf序列化的資料體量更小,傳遞效率相比於XML,JSON效率會更高。其他應用也不需要是python,可以是java,c++。只要實現了相同的proto協議,就可以解析傳送過來的序列化資料。

以上就是本人對protobuf的理解,有不當之處還請指出,謝謝!

打賞支援我寫出更多好文章,謝謝!

打賞作者

打賞支援我寫出更多好文章,謝謝!

任選一種支付方式

資料交換利器 Protobuf 技術淺析 資料交換利器 Protobuf 技術淺析

相關文章