原文在這里。
本教程為 Go 程序員提供了使用Protocol buffer的基本介紹。
本教程使用proto3
向 Go 程序員介紹如何使用 protobuf。通過創(chuàng)建一個簡單的示例應(yīng)用程序,它向你展示了如何:
- 在
.proto
中定義消息格式 - 使用protocol buffer編譯器
- 使用Go protocol buffer API讀寫消息
這并不是protocol buffer在Go中使用的完整指南。更多細節(jié),詳見Protocol Buffer Language Guide、Go API Reference、Go Generated Code Guide和Encoding Reference。
為什么使用Protocol Buffer
我們要使用的例子是一個非常簡單的“通訊錄”應(yīng)用程序,它可以從文件中讀寫聯(lián)系人的信息。通訊錄中每個人都有一個姓名、ID、郵箱和練習(xí)電話。
你如何序列化并取回這樣結(jié)構(gòu)化的數(shù)據(jù)呢?下面有幾條建議:
- 原始內(nèi)存中數(shù)據(jù)結(jié)構(gòu)可以發(fā)送/保存為二進制。這是一種隨時間推移而變得脆弱的方法,因為接收/讀寫的代碼必須編譯成相同的內(nèi)存布局,endianness等。另外,文件已原始格式積累數(shù)據(jù)和在網(wǎng)絡(luò)中到處傳輸副本,因此擴展這種格式十分困難。
- 你可以編寫已臨時的方法來講數(shù)據(jù)元素編碼到單個字符串中 --- 例如用“12:3:-23:67”來編碼4個int。這是一種簡單而靈活的方法,盡管它確實需要編寫一次性的編碼和解析代碼,并且解析會增加少量的運行時成本。這對于編碼非常簡單的數(shù)據(jù)最有效。
- 序列化為XML。這種方法非常有吸引力,因為XML(某種程度上)是人類可讀的,而且有許多語言的綁定庫。如果你希望與其他應(yīng)用程序/項目共享數(shù)據(jù),這可能是一個不錯的選擇。然而,XML是出了名的空間密集型,對它進行編碼/解碼會給應(yīng)用程序帶來巨大的性能損失。而且,在XML DOM樹中導(dǎo)航要比在類中導(dǎo)航簡單字段復(fù)雜得多。
Protocol buffers是解決這個問題的靈活、高效、自動化的解決方案。使用Protocol buffers,你編寫一個描述要存儲的數(shù)據(jù)結(jié)構(gòu)的.proto
文件。然后,Protocol buffer編譯器會創(chuàng)建一個類,該類實現(xiàn)了Protocol buffer數(shù)據(jù)的自動編碼和解析,使用高效的二進制格式。生成的類為構(gòu)成Protocol buffer的字段提供了獲取器和設(shè)置器,并處理了讀取和寫入Protocol buffer的細節(jié)。重要的是,Protocol buffer格式支持隨著時間的推移擴展格式的想法,以使代碼仍然能夠讀取使用舊格式編碼的數(shù)據(jù)。
從哪能找到示例代碼呢?
我們的示例是一組用Protocol buffer編碼的命令行應(yīng)用程序,用于管理地址簿數(shù)據(jù)文件。命令add_person_go
用于向數(shù)據(jù)文件添加新條目。命令list_people_go
解析數(shù)據(jù)文件并將數(shù)據(jù)打印到控制臺。
你可以從這里下載。
定義Protocol文件
通訊錄程序從定義.proto
文件開始。.proto
文件中的定義很簡單:為要序列化的每個數(shù)據(jù)結(jié)構(gòu)添加一個message,然后為消息中的每個字段指定名稱和類型。在我們的示例中,定義消息的.proto
文件是addressbook.proto
。
.proto
文件以一個包聲明開頭,這有助于防止不同項目之間的命名沖突。
syntax = "proto3";
package tutorial;
import "google/protobuf/timestamp.proto";
go_package
選項定義了包含此文件中所有生成代碼的包的導(dǎo)入路徑。 Go包名稱將是導(dǎo)入路徑的最后一個路徑組件。例如,我們的示例將使用“tutorialpb”作為包名稱。
option go_package = "github.com/protocolbuffers/protobuf/examples/go/tutorialpb";
接下來,需要定義message。消息只是一個包含一組類型化字段的聚合。許多標(biāo)準(zhǔn)簡單數(shù)據(jù)類型都可用作字段類型,包括bool
、int32
、float
、double
和string
。你也可以通過使用其他消息類型作為字段類型來為消息添加更多結(jié)構(gòu)。
message Person {
string name = 1;
int32 id = 2; // Unique ID number for this person.
string email = 3;
enum PhoneType {
PHONE_TYPE_UNSPECIFIED = 0;
PHONE_TYPE_MOBILE = 1;
PHONE_TYPE_HOME = 2;
PHONE_TYPE_WORK = 3;
}
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;
}
在上面例子中,Person
消息包含PhoneNumber
消息,同時Person
消息包含在AddressBook
消息中。你甚至可以定義消息類型嵌套在其它消息中 --- 就像上面PhoneNumber
定義在Person
中。你也可以定義enum
類型,如果你想讓你的字段只是用預(yù)定義列表中的一個值 --- 這里你想聲明的電話類型可以是MOBILE
、HOME
或WORK
其中之一。
“= 1”,“= 2”標(biāo)記每個字段在二進制編碼中的唯一的“tag”。序號1-15編碼的字節(jié)數(shù)比較高的數(shù)字少一位,因此,作為一種優(yōu)化,你可以決定對常用或重復(fù)的元素使用這些標(biāo)記,而對不常用的可選元素使用標(biāo)記16或更高。重復(fù)字段中的每個元素都需要重新編碼標(biāo)記號,因此重復(fù)字段是此優(yōu)化的特別好的候選項。
如果未設(shè)置字段值,則會使用默認(rèn)值:對于數(shù)字類型,使用零;對于字符串,使用空字符串;對于布爾值,使用false。對于嵌套的消息,默認(rèn)值始終是消息的“默認(rèn)實例”或“原型”,該實例沒有任何字段設(shè)置。調(diào)用訪問器以獲取未明確設(shè)置的字段的值始終返回該字段的默認(rèn)值。
如果字段是repeated
的,那么該字段可以重復(fù)任意次數(shù)(包括零次)。重復(fù)值的順序?qū)⒂蓀rotocol buffer處理??梢詫⒅貜?fù)字段視為動態(tài)大小的數(shù)組。
你可以在Protocol Buffer語言指南中找到撰寫.proto
文件的完整指南,包括所有可能的字段類型。但不要尋找類繼承類似的功能 - 因為protocol buffer不支持這一點。
編譯Protocol Buffers
現(xiàn)在你已經(jīng)有.proto
文件了,接下來你需要生成讀寫AddressBook
(包括Person
和PhoneNumber
)消息的類。現(xiàn)在,你需要運行protocol buffer編譯器protoc
:
- 如果你還沒安裝編譯器,可從這里下載并根據(jù)README編譯安裝。
- 使用如下命令按照Go protocol buffers插件:
$ go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
protoc-gen-go
編譯器插件將安裝在$GOBIN
中,默認(rèn)為$GOPATH/bin
。protocol buffer編譯器protoc
必須能夠在你的$PATH
中找到它。 - 現(xiàn)在運行編譯器,指明源目錄(應(yīng)用程序源文件目錄,不指定的話默認(rèn)使用當(dāng)前目錄),目標(biāo)路徑(你要存放生成的代碼的目錄,通常與
$SRC_DIR
一樣),.proto
文件路徑。這樣,你可以:
因為要生成Go代碼,所以使用$ protoc -I=$SRC_DIR --go_out=$DST_DIR $SRC_DIR/addressbook.proto
--go_out
選項。若要生成其它支持的語言,提供類似選項即可。
生成的github.com/protocolbuffers/protobuf/examples/go/tutorialpb/addressbook.pb.go
文件將保存在你指定的目錄下。
Protocol Buffer API
生成的addressbook.pb.go
為你提供了下面這些有用的類型:
- 包含
People
字段的AddressBook
結(jié)構(gòu)體 - 包含
Name
、Id
、Email
和Phones
字段的People
- 包含
Number
和Type
字段的Person_PhoneNumber
- 自定義枚舉類型的
Person.PhoneType
你可以在Go 生成的代碼指南中詳細了解生成的代碼的細節(jié),但在大多數(shù)情況下,你可以將這些代碼視為完全普通的 Go 類型。
以下是list_people
命令的單元測試示例,演示了如何創(chuàng)建一個Person
實例:
p := pb.Person{
Id: 1234,
Name: "John Doe",
Email: "jdoe@example.com",
Phones: []*pb.Person_PhoneNumber{
{Number: "555-4321", Type: pb.Person_PHONE_TYPE_HOME},
},
}
創(chuàng)建Message
使用protocol buffers的目的是將數(shù)據(jù)序列化,以便在其他地方進行解析。在 Go 中,你可以使用proto
庫的Marshal函數(shù)來序列化你的protocol buffers數(shù)據(jù)。protocol buffers消息的結(jié)構(gòu)體指針實現(xiàn)了proto.Message
接口。調(diào)用proto.Marshal
返回編碼后的protocol buffers數(shù)據(jù)。例如,我們在add_person
命令中使用了這個函數(shù):
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)
}
讀取Message
要解析已編碼的消息,可以使用proto
庫的Unmarshal函數(shù)。調(diào)用此函數(shù)將數(shù)據(jù)解析為protocol buffers,并將結(jié)果放book
中。因此,要在list_people
命令中解析文件,我們使用以下代碼:
// 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)
}
擴展
在發(fā)布protocol buffer生成的代碼后不久,你肯定會想提升
你的protocol buffer定義。如果你想新的buffer可以被后向兼容,并且舊的buffer可以被前向兼容,--- 你確實想這樣做 --- 那你需要遵守下面的規(guī)則。在新版的protocol buffer中:
- 你必須不能改變已有字段的序號。
- 你可以刪除repeated字段。
- 你可以新增repeated字段,但必須使用新的序號(序號在protocol buffer中沒被用過,也沒被刪除)。
還有一些其它的擴展要遵守,但很少會用到它們。
遵循這些規(guī)則,舊代碼將可以輕松地讀取新的消息,并且會忽略任何新字段。對于舊代碼來說,已刪除的單字段將只是它們的默認(rèn)值,而已刪除的重復(fù)字段將為空。新代碼也可以透明地讀取舊消息。
但請記住,舊消息中不會包含新字段,因此你需要合理地處理默認(rèn)值。使用類型特定的默認(rèn)值:對于字符串,默認(rèn)值是空字符串。對于布爾值,默認(rèn)值是false
。對于數(shù)值類型,默認(rèn)值是零。文章來源:http://www.zghlxwxcb.cn/news/detail-710664.html

聲明:本作品采用署名-非商業(yè)性使用-相同方式共享 4.0 國際 (CC BY-NC-SA 4.0)進行許可,使用時請注明出處。
Author: mengbin
blog: mengbin
Github: mengbin92
cnblogs: 戀水無意文章來源地址http://www.zghlxwxcb.cn/news/detail-710664.html
到了這里,關(guān)于Go with Protobuf的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!