本教程使用proto3版本的protocol buffer語言,提供了一個基本的在Go程序中使用protocol buffer的介紹。經過建立一個簡單的示例應用程序,向你展現如何git
.proto
文件中定義消息格式。它不是一個全面的在Go中使用protocol buffer的指南,更詳細的參考信息請查看前面的兩個教程。github
Protobuf語言指南golang
Protobuf生成Go代碼指南segmentfault
咱們將要使用的示例是一個很是簡單的「地址簿」應用程序,能夠在文件中讀取和寫入人員的聯繫人詳細信息。地址簿中的每一個人都有姓名,ID,電子郵件地址和聯繫電話號碼。數組
如何序列化和檢索這樣的結構化數據?有幾種方法能夠解決這個問題:數據結構
protocol buffer是靈活,高效,自動化的解決方案,能夠解決這個問題。使用protocol buffer,您能夠編寫要存儲的數據結構的.proto
描述。由此,protocol buffer編譯器會建立一個類,該類使用有效的二進制格式實現協議緩衝區數據的自動編碼和解析。生成的類會爲構成protocol buffer的字段提供getter和setter,並負責將protocol buffer做爲一個單元讀取和寫入的細節。重要的是,protocol buffer格式支持隨着時間的推移擴展格式的想法,使得代碼仍然能夠讀取使用舊格式編碼的數據。函數
示例是一組用於管理地址簿數據文件的命令行應用程序,使用protocol buffer進行編碼。命令add_person_go
向數據文件添加新條目。命令list_people_go
解析數據文件並將數據打印到控制檯。性能
下載這些文件到你的項目目錄中:優化
.proto
文件 addressbook.proto 要建立地址簿應用程序,您須要從.proto
文件開始。 .proto文件中的定義很簡單:爲要序列化的每一個數據結構定義消息,而後爲消息中的每一個字段指定名稱和類型。在咱們的示例中,定義消息的.proto文件是addressbook.proto。ui
.proto文件以包聲明開頭,這有助於防止不一樣項目之間的命名衝突。
syntax = "proto3"; package tutorial; import "google/protobuf/timestamp.proto";
在Go中,protocol buffer的包名稱用做Go包,除非您指定了go_package。即便你確實提供了go_package,你仍然應該在.proto
文件中定義一個包名,以免在Protocol Buffers命名空間和非Go語言中發生名稱衝突。
接下來,是消息定義。消息只是包含一組類型字段的聚合。許多標準的簡單數據類型均可用做字段類型,包括bool,int32,float,double和string。您還可使用其餘消息類型做爲字段類型,爲消息添加更多結構。
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; }
在上面的示例中,Person
消息包含PhoneNumber
消息,而AddressBook
消息包含Person
消息。您甚至能夠定義嵌套在其餘消息中的消息類型 - 如您所見,PhoneNumber
類型在Person
中定義。若是您但願其中一個字段值的取值範圍是預約義的值列表中的值,還能夠定義枚舉類型 - 此處你要指定電話號碼能夠是MOBILE
,HOME
或WORK
之一。
每一個元素上的「= 1」,「= 2」標記標識該字段在二進制編碼中使用的惟一「標記」。標籤號1-15編碼時比更大編號少須要一個字節,所以做爲優化,您能夠決定將這些標籤用於經常使用或重複的元素,將標籤16和更高標籤留給不太經常使用的可選元素。重複字段中的每一個元素都須要從新編碼標記號,所以重複字段特別適合此優化。
若是未設置字段值,則使用默認值:數字類型爲零,字符串爲空字符串,bools爲false。對於嵌入式消息,默認值始終是消息的「默認實例」或「原型」,其中沒有設置其字段。調用訪問器以獲取還沒有顯式設置的字段的值始終返回該字段的默認值。
若是一個字段是可重複的,該字段能夠重複任意次數(包括零)。重複值的順序將保留在protocol buffer中。將可重複字段視爲變長數組。
您將在Protobuf語言指南中找到編寫.proto文件的完整指南 - 包括全部可能的字段類型。不要去尋找類繼承相似的東西,protocol buffer不支持這些。
有了.proto
後,你須要作的下一件事是生成你須要讀取和寫入AddressBook(以及Person和PhoneNumber)消息所需的類(Go中是結構體和結構體方法)。爲此,你須要在.proto上運行protocol buffer譯器protoc:
protoc
protoc須要安裝插件才能編譯生成Go代碼,能夠運行以下命令安裝插件
go get -u github.com/golang/protobuf/protoc-gen-go
如今運行編譯器,指定源目錄(應用程序的源代碼所在的位置 - 若是不提供值,則使用當前目錄),目標目錄(您但願生成的代碼在哪裏;一般與$相同) SRC_DIR),以及.proto的路徑。在這種狀況下,你...:
protoc -I=$SRC_DIR --go_out=$DST_DIR $SRC_DIR/addressbook.proto
咱們使用的示例go代碼中導入編譯後的pb.go
文件的路徑是 pb "github.com/protocolbuffers/protobuf/examples/tutorial"
因此用protoc編譯時使用的目標路徑應該是
protoc --go_out=$GOPATH/src/github.com/protocolbuffers/protobuf/examples/tutorial ./addressbook.proto
$GOPATH/src/github.com/protocolbuffers/protobuf/examples/tutorial
目錄須要提早建立好。
生成addressbook.pb.go提供如下有用類型:
能夠閱讀更多有關「生成代碼」指南中生成的內容的詳細信息,但在大多數狀況下,您能夠將這些視爲徹底普通的Go類型。
行動勝千言,下載教程中提供的代碼,運行上面的編譯命令,去看看生成的addressbook.pb.go
中的代碼吧。
下面是如何建立Person實例的示例:
p := pb.Person{ Id: 1234, Name: "John Doe", Email: "jdoe@example.com", Phones: []*pb.Person_PhoneNumber{ {Number: "555-4321", Type: pb.Person_HOME}, }, }
使用protocl buffer目的是序列化你的結構化數據,以即可以在其餘地方解析它。在Go中,使用proto
庫的Marshal
函數來序列化protocol buffer數據。指向消息的結構體的指針實現了proto.Message
接口。調用proto.Marshal
會返回以其有線格式編碼的protocol buffer。例如,咱們在add_person命令中使用此函數:
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
函數。調用它將buf中的數據解析爲protocol buffer,並將結果放在結構體中。所以,要在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) }
go build add_person.go
和 go build list_people.go
會生成兩個二進制文件add_person
和list_people
。./add_person ADDRESS_BOOK
程序會在命令行中提示輸入,用命令行的輸入構建地址簿數據而後將數據序列化爲protocol buffer存儲到文件ADDRESS_BOOK
中。./list_people
程序會從文件ADDRESS_BOOK
讀取protocol buffer數據,解析到結構體中而後打印出結構體中的Person
數據。