簡介
和http
中常用的json
協(xié)議一樣曹傀,protobuf
也是用來傳輸數(shù)據(jù)的含鳞,但是它使用二進(jìn)制格式,傳輸效率更高泼橘。
安裝
- 下載
protoc
二進(jìn)制程序
下載鏈接
在windows上秘噪,選擇protoc-3.7.0-rc-2-win64.zip 進(jìn)行下載魁索。
壓縮包中有兩個(gè)文件夾:
壓縮包中文件夾.png
將bin
目錄下的protoc.exe
拷貝到GOPATH/bin
目錄下融撞,將include/
目錄下的google
文件夾拷貝GOPATH/src
目錄下(只有使用protobuf的一些內(nèi)置結(jié)構(gòu)才需要用到該文件夾內(nèi)的文件,這次并不會(huì)用到這個(gè)文件夾)粗蔚。 - 安裝
protobuf
的編譯器插件protoc-gen-go
protoc
程序會(huì)調(diào)用protoc-gen-go
尝偎,將.proto
文件生成golang
代碼∨艨兀可以使用go get
命令安裝:
go get -u -v github.com/golang/protobuf/protoc-gen-go
安裝成功后致扯,會(huì)在GOPATH/bin
下生成protoc-gen-go.exe
程序。
例子
該demo
為官網(wǎng)tutorial
的簡化版本当辐。
在GOPATH/src/all-demo下抖僵,目錄結(jié)構(gòu)為:
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
設(shè)置語法類型缘揪,有proto2
和proto3
兩種語法耍群。 -
package addressbook
可以設(shè)置生成的golang
代碼的包名义桂。 -
message
對(duì)應(yīng)于golang
中的struct
,可以看到文件中一共定義了Person
世吨,PhoneNumber
澡刹,AddressBook
3個(gè)message
,其中PhoneNumber
是Person
的嵌套類型耘婚。 -
message
中有字段,可以是int
陆赋,string
沐祷,枚舉或者其他消息類型。 -
repeated
表示該字段可以不止一個(gè)攒岛,類似于golang
中的slice
赖临。
編譯
命令行進(jìn)入GOPATH/src/all-demo/protobuf-demo/demo1/addressbook
,
執(zhí)行protoc --go_out=. addressbook.proto
,會(huì)在該目錄下生成addressbook.pb.go
文件灾锯。
其中 --go_out
指定生成的golang
文件的目錄兢榨。
addressbook.pb.go
部分源碼如下:
// 可以看到包名為addressbook
package addressbook
// [START messages]
type Person struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Id int32 `protobuf:"varint,2,opt,name=id,proto3" json:"id,omitempty"`
Email string `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_sizecache int32 `json:"-"`
}
type Person_PhoneNumber struct {
Number string `protobuf:"bytes,1,opt,name=number,proto3" json:"number,omitempty"`
Type Person_PhoneType `protobuf:"varint,2,opt,name=type,proto3,enum=addressbook.Person_PhoneType" json:"type,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `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_sizecache int32 `json:"-"`
}
編寫main.go
代碼主要測(cè)試proto.Marshal
和proto.UnMarshal
的功能。先定義一個(gè)pb.AddressBook
結(jié)構(gòu)體顺饮,并將其初始化吵聪,用proto.Marshal
將其序列化成二進(jìn)制數(shù)據(jù),寫入文件兼雄,再將其從文件中讀取吟逝,使用proto.UnMarshal
反序列化成結(jié)構(gòu)體。
代碼如下:
package main
import (
"fmt"
pb "all-demo/protobuf-demo/demo1/addressbook"
"io/ioutil"
"log"
"github.com/golang/protobuf/proto"
)
func main() {
// 自定義AddressBook內(nèi)容
book := &pb.AddressBook{
People: []*pb.Person {
&pb.Person{
Id: 1,
Name: "zyq",
Email: "77@qq.com",
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進(jìn)行序列化
out, err := proto.Marshal(book)
if err != nil {
log.Fatalln("Failed to encode address book:", err)
}
// 將序列化的內(nèi)容寫入文件
if err := ioutil.WriteFile(fname, out, 0644); err != nil {
log.Fatalln("Failed to write address book:", err)
}
// 讀取寫入的二進(jìn)制數(shù)據(jù)
in, err := ioutil.ReadFile(fname)
if err != nil {
log.Fatalln("Error reading file:", err)
}
// 定義一個(gè)空的結(jié)構(gòu)體
book2 := &pb.AddressBook{}
// 將從文件中讀取的二進(jìn)制進(jìn)行反序列化
if err := proto.Unmarshal(in, book2); err != nil {
log.Fatalln("Failed to parse address book:", err)
}
fmt.Println("book2: ",book2)
}
執(zhí)行結(jié)果為:
book : people:<name:"zyq" id:1 email:"77@qq.com" phones:<number:"11111" > phones:<number:"22222" type:HOME > >
book2: people:<name:"zyq" id:1 email:"77@qq.com" phones:<number:"11111" > phones:<number:"22222" type:HOME > >
問題:
- 還有很多語法不了解赦肋。
-
proto.UnMarshal
函數(shù)的聲明為func Unmarshal(buf []byte, pb Message) error
块攒,該函數(shù)的第二個(gè)參數(shù)為proto.Message
接口,在網(wǎng)絡(luò)傳輸中佃乘,服務(wù)端怎么知道客戶端發(fā)過來的到底是哪一種message
囱井。