Protocol Buffer Basics: Go

為什么使用 protocol buffer?

我們將要使用的示例是一個非常簡單的“地址簿”應用程序喜庞,它可以在文件中讀取和寫入人們的聯(lián)系方式诀浪。
地址簿中的每個人都有一個姓名、一個 ID延都、一個電子郵件地址雷猪、一個聯(lián)系電話號碼。

你如何序列化和檢索這樣的結構化數據晰房?
有幾種方法可以解決這個問題:

  • 使用 gobs 序列化 Go 數據結構
    這在特定于 Go 的環(huán)境中是一個很好的解決方案求摇,但是如果您需要與為其他平臺編寫的應用程序共享數據,它就不能很好地工作殊者。

  • 您可以發(fā)明一種特殊方式將數據項編碼為單個字符串
    例如將 4 個整數編碼為“12:3:-23:67”与境。 這是一種簡單而靈活的方法,盡管它確實需要編寫一次性的編碼和解析代碼猖吴,并且解析會產生很小的運行時成本摔刁。 這最適合編碼非常簡單的數據。

  • 將數據序列化為 XML
    這種方法可能非常有吸引力海蔽,因為 XML(某種程度)是人類可讀的共屈,并且有許多語言的綁定庫。 如果您想與其他應用程序/項目共享數據党窜,這可能是一個不錯的選擇趁俊。 然而,眾所周知刑然,XML 是空間密集型的寺擂,對它進行編碼/解碼會對應用程序造成巨大的性能損失。

protocol buffer 是解決這個問題的靈活泼掠、高效怔软、自動化的解決方案。
使用 protocol buffer择镇,您可以編寫要存儲的數據結構的 .proto 描述挡逼。
由此,protocol buffer 編譯器創(chuàng)建了一個類腻豌,該類以高效的二進制格式實現 protocol buffer 數據的自動編碼和解析家坎。
生成的類為組成 protocol buffer 的字段提供 getter 和 setter嘱能,并將讀取和寫入 protocol buffer 的細節(jié)作為一個單元處理。
重要的是虱疏,protocol buffer格式支持隨著時間的推移擴展格式的想法惹骂,這樣代碼仍然可以讀取用舊格式編碼的數據。

示例

https://github.com/protocolbuffers/protobuf/tree/main/examples

定義proto

要創(chuàng)建地址簿應用程序做瞪,您需要從 .proto 文件開始对粪。
.proto 文件中的定義很簡單:為每個要序列化的數據結構添加一條消息,然后為消息中的每個字段指定名稱和類型装蓬。
在示例中著拭,定義消息的 .proto 文件是 addressbook.proto。

.proto 文件以包聲明開頭牍帚,這有助于防止不同項目之間的命名沖突儡遮。

syntax = "proto3";
package tutorial;

import "google/protobuf/timestamp.proto";

go_package 選項定義包的導入路徑,該路徑將包含此文件的所有生成代碼暗赶。
Go 包名稱將是導入路徑的最后一個路徑組件鄙币。
例如,示例將使用包名“tutorialpb”忆首。

option go_package = "github.com/protocolbuffers/protobuf/examples/go/tutorialpb";

接下來爱榔,定義 message。 message 是包含一組類型字段的聚合糙及。
許多標準的簡單數據類型可用作字段類型详幽,包括 bool、int32浸锨、float唇聘、double 和 string。還可以通過使用其它 message 類型作為字段類型柱搜,來為 message 添加更多結構迟郎。

message Person {
  string name = 1;
  int32 id = 2;  // Unique ID number for this person.
  string email = 3;

  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }

  message PhoneNumber {
    string number = 1;
    PhoneType type = 2;
  }

  repeated PhoneNumber phones = 4;

  google.protobuf.Timestamp last_updated = 5;
}

// Our address book file is just one of these.
message AddressBook {
  repeated Person people = 1;
}

編譯 protocol buffers

現在您已經有了一個 .proto,接下來您需要生成讀取和寫入 AddressBook(以及由此產生的 Person 和 PhoneNumber)消息所需的類聪蘸。 為此宪肖,您需要在 .proto 上運行protocol buffers 編譯器 protoc:

  1. 如果您尚未安裝編譯器,請下載軟件包并按照 README 中的說明進行操作健爬。
    https://developers.google.cn/protocol-buffers/docs/downloads

  2. 運行以下命令安裝 Go protocol buffers 插件

go install google.golang.org/protobuf/cmd/protoc-gen-go@latest

編譯器插件 protoc-gen-go 將安裝在 $GOBIN 中控乾,默認為 $GOPATH/bin。 它必須在您的 $PATH 中娜遵,編譯器 protoc 才能找到它蜕衡。

  1. 現在運行編譯器,指定源目錄(應用程序的源代碼所在的位置设拟。如果不提供值慨仿,則使用當前目錄)久脯、目標目錄(您希望生成的代碼所在的位置;通常與 $SRC_DIR 相同)镰吆,以及 .proto 的路徑帘撰。
protoc -I=$SRC_DIR --go_out=$DST_DIR $SRC_DIR/addressbook.proto

這會在您指定的目標目錄中生成 addressbook.pb.go。

The Protocol Buffer API

生成的 addressbook.pb.go 為您提供以下有用的類型:

  • AddressBook 結構體鼎姊,包含 People骡和;
  • Person 結構體相赁, 包含 Name, Id, Email, Phones相寇;
  • Person_PhoneNumber 結構體, 包含 Number, Type;
  • The type Person_PhoneType and a value defined for each value in the Person.PhoneType enum.
p := pb.Person{
        Id:    1234,
        Name:  "John Doe",
        Email: "jdoe@example.com",
        Phones: []*pb.Person_PhoneNumber{
                {Number: "555-4321", Type: pb.Person_HOME},
        },
}

序列化

使用 protocol buffer 的目的是序列化您的數據钮科,以便可以在其他地方對其進行解析唤衫。 在 Go 中,您使用 proto 庫的 Marshal 函數來序列化您的協(xié)議緩沖區(qū)數據绵脯。

book := &pb.AddressBook{}
// ...

// Write the new address book back to disk.
out, err := proto.Marshal(book)
if err != nil {
        log.Fatalln("Failed to encode address book:", err)
}
if err := ioutil.WriteFile(fname, out, 0644); err != nil {
        log.Fatalln("Failed to write address book:", err)
}

反序列化

要解析編碼消息佳励,請使用 proto 庫的 Unmarshal 函數。
調用它會將 in 中的數據解析為 protocol buffer 并將結果放入 book 中蛆挫。

// Read the existing address book.
in, err := ioutil.ReadFile(fname)
if err != nil {
        log.Fatalln("Error reading file:", err)
}
book := &pb.AddressBook{}
if err := proto.Unmarshal(in, book); err != nil {
        log.Fatalln("Failed to parse address book:", err)
}

擴展 protocol buffer

在你發(fā)布使用你的 protocol buffer 的代碼之后赃承,遲早會想要“改進” protocol buffer 的定義。 如果您希望新buffer 向后兼容悴侵,并且您的舊 buffer 向前兼容瞧剖。那么您需要遵循一些規(guī)則。 在新版本的協(xié)議緩沖區(qū)中:

  • 您不得更改任何現有字段的標簽號可免。
  • 您可以刪除字段抓于。
  • 您可以添加新字段,但必須使用新的標簽號(即從未在此 protocol buffer 中使用過的標簽號浇借,即使已刪除的字段也不使用)捉撮。

如果您遵循這些規(guī)則,舊代碼將愉快地閱讀新消息并忽略任何新字段妇垢。 對于舊代碼巾遭,已刪除的單個字段將僅具有其默認值,而刪除的重復字段將為空闯估。 新代碼也將透明地讀取舊消息灼舍。

但是,請記住睬愤,舊消息中不會出現新字段片仿,因此您需要對默認值做一些合理的事情。 使用特定類型的默認值:對于字符串尤辱,默認值為空字符串砂豌。 對于布爾值厢岂,默認值為 false。 對于數字類型阳距,默認值為零塔粒。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市筐摘,隨后出現的幾起案子卒茬,更是在濱河造成了極大的恐慌,老刑警劉巖咖熟,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件圃酵,死亡現場離奇詭異,居然都是意外死亡馍管,警方通過查閱死者的電腦和手機郭赐,發(fā)現死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來确沸,“玉大人捌锭,你說我怎么就攤上這事÷奚樱” “怎么了观谦?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長桨菜。 經常有香客問我豁状,道長,這世上最難降的妖魔是什么雷激? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任替蔬,我火速辦了婚禮,結果婚禮上屎暇,老公的妹妹穿的比我還像新娘承桥。我一直安慰自己,他們只是感情好根悼,可當我...
    茶點故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布凶异。 她就那樣靜靜地躺著,像睡著了一般挤巡。 火紅的嫁衣襯著肌膚如雪剩彬。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天矿卑,我揣著相機與錄音喉恋,去河邊找鬼。 笑死,一個胖子當著我的面吹牛轻黑,可吹牛的內容都是我干的糊肤。 我是一名探鬼主播氓鄙,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼馆揉,長吁一口氣:“原來是場噩夢啊……” “哼抖拦!你這毒婦竟也來了升酣?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤噩茄,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后向臀,有當地人在樹林里發(fā)現了一具尸體巢墅,經...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡诸狭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年券膀,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片芹彬。...
    茶點故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖叉庐,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情陡叠,我是刑警寧澤玩郊,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站枉阵,受9級特大地震影響译红,放射性物質發(fā)生泄漏。R本人自食惡果不足惜兴溜,卻給世界環(huán)境...
    茶點故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一侦厚、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧拙徽,春花似錦刨沦、人聲如沸膘怕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽来破。三九已至裁眯,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間穿稳,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工晌坤, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人骤菠。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像商乎,于是被迫代替她去往敵國和親央拖。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,601評論 2 353

推薦閱讀更多精彩內容