golang使用protobuf
簡介
和 http
中常用的 json
協議一樣, protobuf
也是用來傳輸資料的,但是它使用二進位制格式,傳輸效率更高。
安裝
- 下載
protoc
二進位制程式
下載連結
在windows上,選擇protoc-3.7.0-rc-2-win64.zip 進行下載。
壓縮包中有兩個資料夾:
壓縮包中資料夾.png
bin
目錄下的protoc.exe
拷貝到GOPATH/bin
目錄下,將include/
目錄下的google
資料夾拷貝GOPATH/src
目錄下(只有使用protobuf的一些內建結構才需要用到該資料夾內的檔案,這次並不會用到這個資料夾)。 - 安裝
protobuf
的編譯器外掛protoc-gen-go
protoc
程式會呼叫protoc-gen-go
,將.proto
檔案生成golang
程式碼。可以使用go get
命令安裝:
go get -u -v github.com/golang/protobuf/protoc-gen-go
安裝成功後,會在 GOPATH/bin
下生成 protoc-gen-go.exe
程式。
例子
該 demo
為官網 tutorial
的簡化版本。
在GOPATH/src/all-demo下,目錄結構為:
protobuf-demo demo1 addressbook addressbook.pb.go addressbook.proto main.go
編寫、編譯addressbook.proto檔案
其中 addressbook.proto
檔案為:
// [START declaration] syntax = "proto3"; package addressbook; // [END declaration] // [START messages] 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; } // Our address book file is just one of these. message AddressBook { repeated Person people = 1; } // [END messages]
其中:
-
syntax
設定語法型別,有proto2
和proto3
兩種語法。 -
package addressbook
可以設定生成的golang
程式碼的包名。 -
message
對應於golang
中的struct
,可以看到檔案中一共定義了Person
,PhoneNumber
,AddressBook
3個message
,其中PhoneNumber
是Person
的巢狀型別。 -
message
中有欄位,可以是int
,string
,列舉或者其他訊息型別。 -
repeated
表示該欄位可以不止一個,類似於golang
中的slice
。
編譯
命令列進入 GOPATH/src/all-demo/protobuf-demo/demo1/addressbook
,
執行 protoc --go_out=. addressbook.proto
,會在該目錄下生成 addressbook.pb.go
檔案。
其中 --go_out
指定生成的 golang
檔案的目錄。
addressbook.pb.go
部分原始碼如下:
// 可以看到包名為addressbook package addressbook // [START messages] type Person struct { Namestring`protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Idint32`protobuf:"varint,2,opt,name=id,proto3" json:"id,omitempty"` Emailstring`protobuf:"bytes,3,opt,name=email,proto3" json:"email,omitempty"` Phones[]*Person_PhoneNumber `protobuf:"bytes,4,rep,name=phones,proto3" json:"phones,omitempty"` XXX_NoUnkeyedLiteral struct{}`json:"-"` XXX_unrecognized[]byte`json:"-"` XXX_sizecacheint32`json:"-"` } type Person_PhoneNumber struct { Numberstring`protobuf:"bytes,1,opt,name=number,proto3" json:"number,omitempty"` TypePerson_PhoneType `protobuf:"varint,2,opt,name=type,proto3,enum=addressbook.Person_PhoneType" json:"type,omitempty"` XXX_NoUnkeyedLiteral struct{}`json:"-"` XXX_unrecognized[]byte`json:"-"` XXX_sizecacheint32`json:"-"` } // Our address book file is just one of these. type AddressBook struct { People[]*Person `protobuf:"bytes,1,rep,name=people,proto3" json:"people,omitempty"` XXX_NoUnkeyedLiteral struct{}`json:"-"` XXX_unrecognized[]byte`json:"-"` XXX_sizecacheint32`json:"-"` }
編寫main.go
程式碼主要測試 proto.Marshal
和 proto.UnMarshal
的功能。先定義一個 pb.AddressBook
結構體,並將其初始化,用 proto.Marshal
將其序列化成二進位制資料,寫入檔案,再將其從檔案中讀取,使用 proto.UnMarshal
反序列化成結構體。
程式碼如下:
package main import ( "fmt" pb "all-demo/protobuf-demo/demo1/addressbook" "io/ioutil" "log" "github.com/golang/protobuf/proto" ) func main() { // 自定義AddressBook內容 book := &pb.AddressBook{ People: []*pb.Person { &pb.Person{ Id: 1, Name: "zyq", Email: "[email protected]", Phones: []*pb.Person_PhoneNumber{ &pb.Person_PhoneNumber { Number: "11111", Type: pb.Person_MOBILE, }, &pb.Person_PhoneNumber { Number: "22222", Type: pb.Person_HOME, }, }, }, }, } fmt.Println("book : ",book) fname := "address.dat" // 將book進行序列化 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) } // 讀取寫入的二進位制資料 in, err := ioutil.ReadFile(fname) if err != nil { log.Fatalln("Error reading file:", err) } // 定義一個空的結構體 book2 := &pb.AddressBook{} // 將從檔案中讀取的二進位制進行反序列化 if err := proto.Unmarshal(in, book2); err != nil { log.Fatalln("Failed to parse address book:", err) } fmt.Println("book2: ",book2) }
執行結果為:
book :people:<name:"zyq" id:1 email:"[email protected]" phones:<number:"11111" > phones:<number:"22222" type:HOME > > book2:people:<name:"zyq" id:1 email:"[email protected]" phones:<number:"11111" > phones:<number:"22222" type:HOME > >
問題:
- 還有很多語法不瞭解。
-
proto.UnMarshal
函式的宣告為func Unmarshal(buf []byte, pb Message) error
,該函式的第二個引數為proto.Message
介面,在網路傳輸中,服務端怎麼知道客戶端發過來的到底是哪一種message
。