在linux下使用Protobuf
下载
git clone https://github.com/protocolbuffers/protobuf.git
解压
unzip protobuf.zip
安装依赖
sudo apt-get install autoconf automake libtool curl make g++ unzip libffi-dev -y
安装
$ cd protobuf/
$ ./autogen.sh
$ ./configure
$ make
$ sudo make install
$ sudo ldconfig // 刷新共享库,很重要的一步
测试是否安装成功
protoc -h
获取 proto包(Go语言的proto API接口)
$ go get -v -u github.com/golang/protobuf/proto
进入GOPATH/src/github.com/golang/protobuf/protoc-gen-go
目录下,打开终端,输入:
$ go build
将build生成的protoc-gen-go
可执行文件复制到/bin目录下
$ sudo cp protoc-gen-go /bin/
在goland中使用protobuf
下载protoc二进制程序
在windows上,选择protoc-3.7.0-rc-2-win64.zip 进行下载。
压缩包中有两个文件夹:
protoc-3.11.4-win64
- bin
- include
将bin目录下的protoc.exe
拷贝到GOPATH/bin
目录下,将include/
目录下的google
文件夹拷贝GOPATH/src
目录下。
安装protobuf库文件
go get github.com/golang/protobuf/proto
安装插件 goprotobuf
go get github.com/golang/protobuf/protoc-gen-go
安装成功后,会在GOPATH/bin
下生成protoc-gen-go.exe
程序。
示例:
目录结构为:
protobuf
pb_test
pb.pb.go
pb.proto
main.go
编写pb.proto
syntax = "proto3";
package pb_persion;
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;
}
-
syntax
设置语法类型,有proto2
和proto3
两种语法。 -
package addressbook
可以设置生成的golang
代码的包名。 -
message
对应于golang
中的struct
,可以看到文件中一共定义了Person
,PhoneNumber
,AddressBook
3个message
,其中PhoneNumber
是Person
的嵌套类型。 -
message
中有字段,可以是int
,string
,枚举或者其他消息类型。 -
repeated
表示该字段可以不止一个,类似于golang
中的slice
。 -
标签(在此示例中为 1 到 4)是唯一的整数标识符,用于确定字段序列化的顺序。
-
reserved
限定符确保用于实现这三个符号名的数值不能重复使用。
编译
执行protoc --go_out=. pb.proto
,会在该目录下生成pb.proto
文件。
标志 --go_out
指示编译器生成 Go 源代码。其他语言也有类似的标志。
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: pb.proto
package pb_persion
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type Person_PhoneType int32
const (
Person_MOBILE Person_PhoneType = 0
Person_HOME Person_PhoneType = 1
Person_WORK Person_PhoneType = 2
)
var Person_PhoneType_name = map[int32]string{
0: "MOBILE",
1: "HOME",
2: "WORK",
}
var Person_PhoneType_value = map[string]int32{
"MOBILE": 0,
"HOME": 1,
"WORK": 2,
}
func (x Person_PhoneType) String() string {
return proto.EnumName(Person_PhoneType_name, int32(x))
}
func (Person_PhoneType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_f80abaa17e25ccc8, []int{0, 0}
}
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:"-"`
}
func (m *Person) Reset() { *m = Person{} }
func (m *Person) String() string { return proto.CompactTextString(m) }
func (*Person) ProtoMessage() {}
func (*Person) Descriptor() ([]byte, []int) {
return fileDescriptor_f80abaa17e25ccc8, []int{0}
}
func (m *Person) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Person.Unmarshal(m, b)
}
func (m *Person) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Person.Marshal(b, m, deterministic)
}
func (m *Person) XXX_Merge(src proto.Message) {
xxx_messageInfo_Person.Merge(m, src)
}
func (m *Person) XXX_Size() int {
return xxx_messageInfo_Person.Size(m)
}
func (m *Person) XXX_DiscardUnknown() {
xxx_messageInfo_Person.DiscardUnknown(m)
}
var xxx_messageInfo_Person proto.InternalMessageInfo
func (m *Person) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *Person) GetId() int32 {
if m != nil {
return m.Id
}
return 0
}
func (m *Person) GetEmail() string {
if m != nil {
return m.Email
}
return ""
}
func (m *Person) GetPhones() []*Person_PhoneNumber {
if m != nil {
return m.Phones
}
return nil
}
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=pb_persion.Person_PhoneType" json:"type,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Person_PhoneNumber) Reset() { *m = Person_PhoneNumber{} }
func (m *Person_PhoneNumber) String() string { return proto.CompactTextString(m) }
func (*Person_PhoneNumber) ProtoMessage() {}
func (*Person_PhoneNumber) Descriptor() ([]byte, []int) {
return fileDescriptor_f80abaa17e25ccc8, []int{0, 0}
}
func (m *Person_PhoneNumber) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Person_PhoneNumber.Unmarshal(m, b)
}
func (m *Person_PhoneNumber) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Person_PhoneNumber.Marshal(b, m, deterministic)
}
func (m *Person_PhoneNumber) XXX_Merge(src proto.Message) {
xxx_messageInfo_Person_PhoneNumber.Merge(m, src)
}
func (m *Person_PhoneNumber) XXX_Size() int {
return xxx_messageInfo_Person_PhoneNumber.Size(m)
}
func (m *Person_PhoneNumber) XXX_DiscardUnknown() {
xxx_messageInfo_Person_PhoneNumber.DiscardUnknown(m)
}
var xxx_messageInfo_Person_PhoneNumber proto.InternalMessageInfo
func (m *Person_PhoneNumber) GetNumber() string {
if m != nil {
return m.Number
}
return ""
}
func (m *Person_PhoneNumber) GetType() Person_PhoneType {
if m != nil {
return m.Type
}
return Person_MOBILE
}
// 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:"-"`
}
func (m *AddressBook) Reset() { *m = AddressBook{} }
func (m *AddressBook) String() string { return proto.CompactTextString(m) }
func (*AddressBook) ProtoMessage() {}
func (*AddressBook) Descriptor() ([]byte, []int) {
return fileDescriptor_f80abaa17e25ccc8, []int{1}
}
func (m *AddressBook) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_AddressBook.Unmarshal(m, b)
}
func (m *AddressBook) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_AddressBook.Marshal(b, m, deterministic)
}
func (m *AddressBook) XXX_Merge(src proto.Message) {
xxx_messageInfo_AddressBook.Merge(m, src)
}
func (m *AddressBook) XXX_Size() int {
return xxx_messageInfo_AddressBook.Size(m)
}
func (m *AddressBook) XXX_DiscardUnknown() {
xxx_messageInfo_AddressBook.DiscardUnknown(m)
}
var xxx_messageInfo_AddressBook proto.InternalMessageInfo
func (m *AddressBook) GetPeople() []*Person {
if m != nil {
return m.People
}
return nil
}
func init() {
proto.RegisterEnum("pb_persion.Person_PhoneType", Person_PhoneType_name, Person_PhoneType_value)
proto.RegisterType((*Person)(nil), "pb_persion.Person")
proto.RegisterType((*Person_PhoneNumber)(nil), "pb_persion.Person.PhoneNumber")
proto.RegisterType((*AddressBook)(nil), "pb_persion.AddressBook")
}
func init() {
proto.RegisterFile("pb.proto", fileDescriptor_f80abaa17e25ccc8)
}
var fileDescriptor_f80abaa17e25ccc8 = []byte{
// 252 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x90, 0x41, 0x4b, 0xc3, 0x40,
0x10, 0x85, 0xcd, 0x36, 0x5d, 0xda, 0x09, 0x94, 0x30, 0x88, 0x04, 0x11, 0x09, 0x39, 0x05, 0x85,
0x20, 0x15, 0x04, 0x8f, 0x16, 0x0a, 0x8a, 0xd6, 0x94, 0x45, 0xe8, 0x51, 0x1a, 0x32, 0x60, 0xb0,
0xd9, 0x1d, 0x76, 0xeb, 0xa1, 0x37, 0x7f, 0xba, 0x64, 0x13, 0x54, 0x10, 0x6f, 0x6f, 0x86, 0x8f,
0x79, 0x6f, 0x1e, 0x4c, 0xb8, 0x2a, 0xd8, 0x9a, 0xbd, 0x41, 0xe0, 0xea, 0x95, 0xc9, 0xba, 0xc6,
0xe8, 0xec, 0x53, 0x80, 0x5c, 0x93, 0x75, 0x46, 0x23, 0x42, 0xa8, 0xb7, 0x2d, 0x25, 0x41, 0x1a,
0xe4, 0x53, 0xe5, 0x35, 0xce, 0x40, 0x34, 0x75, 0x22, 0xd2, 0x20, 0x1f, 0x2b, 0xd1, 0xd4, 0x78,
0x0c, 0x63, 0x6a, 0xb7, 0xcd, 0x2e, 0x19, 0x79, 0xa8, 0x1f, 0xf0, 0x06, 0x24, 0xbf, 0x19, 0x4d,
0x2e, 0x09, 0xd3, 0x51, 0x1e, 0xcd, 0xcf, 0x8b, 0x1f, 0x87, 0xa2, 0xbf, 0x5e, 0xac, 0x3b, 0xe0,
0xf9, 0xa3, 0xad, 0xc8, 0xaa, 0x81, 0x3e, 0xdd, 0x40, 0xf4, 0x6b, 0x8d, 0x27, 0x20, 0xb5, 0x57,
0x43, 0x84, 0x61, 0xc2, 0x2b, 0x08, 0xf7, 0x07, 0x26, 0x1f, 0x63, 0x36, 0x3f, 0xfb, 0xef, 0xf8,
0xcb, 0x81, 0x49, 0x79, 0x32, 0xbb, 0x84, 0xe9, 0xf7, 0x0a, 0x01, 0xe4, 0xaa, 0x5c, 0x3c, 0x3c,
0x2d, 0xe3, 0x23, 0x9c, 0x40, 0x78, 0x5f, 0xae, 0x96, 0x71, 0xd0, 0xa9, 0x4d, 0xa9, 0x1e, 0x63,
0x91, 0xdd, 0x42, 0x74, 0x57, 0xd7, 0x96, 0x9c, 0x5b, 0x18, 0xf3, 0x8e, 0x17, 0x20, 0x99, 0x0c,
0xef, 0xba, 0x22, 0xba, 0x67, 0xf0, 0xaf, 0x9f, 0x1a, 0x88, 0x4a, 0xfa, 0x42, 0xaf, 0xbf, 0x02,
0x00, 0x00, 0xff, 0xff, 0xd4, 0xa2, 0xc3, 0x15, 0x5c, 0x01, 0x00, 0x00,
}
编写测试
package main
import (
"fmt"
"github.com/golang/protobuf/proto"
pb_persion "go_demo/src/logAgent/protobuf/pb_test"
"io/ioutil"
"os"
)
func main() {
MarshalProto()
UnMarshalProto()
}
func MarshalProto() {
person := &pb_persion.Person{
Id: 1,
Name: "zy",
Email: "794182189@qq.com",
Phones: []*pb_persion.Person_PhoneNumber{
&pb_persion.Person_PhoneNumber{
Number: "111",
Type: pb_persion.Person_MOBILE,
},
&pb_persion.Person_PhoneNumber{
Number: "222",
Type: pb_persion.Person_HOME,
},
},
}
//序列化
pData, err := proto.Marshal(person)
if err != nil {
panic(err)
}
//把数据写入文件
ioutil.WriteFile("./test.txt", pData, os.ModePerm);
}
func UnMarshalProto() {
in, _ := ReadFile("./test.txt")
person := &pb_persion.Person{}
// 反序列化
if err := proto.Unmarshal(in, person); err != nil {
fmt.Println("Failed to parse person:", err)
}
fmt.Println(person.Name)
fmt.Println(person.Email)
for _, v := range person.Phones {
fmt.Println(v.Number, v.Type);
}
}
func ReadFile(path string) (bytes []byte, err error) {
bytes, err = ioutil.ReadFile(path)
if err != nil {
fmt.Println("error : %s", err)
return
}
return
}
何为 ProtoBuf
官方文档的定义:
协议缓冲区是一种用于序列化结构化数据的灵活,高效,自动化的机制–以XML为例,但更小,更快,更简单。可以定义如何一次构造数据,然后可以使用生成的特殊源代码轻松地使用各种语言在各种数据流中写入和读取结构化数据。甚至可以更新数据结构,而不会破坏已按照“旧”格式编译的已部署程序。
- 语言无关、平台无关。即 ProtoBuf 支持 Java、C++、Python 等多种语言,支持多个平台
- 高效。即比 XML 更小(3 ~ 10倍)、更快(20 ~ 100倍)、更为简单
- 扩展性、兼容性好。可以更新数据结构,而不影响和破坏原有的旧程序
示例:
func main() {
encodeAndserialize()
FileSize()
}
func MakePerson() pb_persion.Person {
person := pb_persion.Person{
Id: 1,
Name: "zy",
Email: "794182189@qq.com",
Phones: []*pb_persion.Person_PhoneNumber{
&pb_persion.Person_PhoneNumber{
Number: "111",
Type: pb_persion.Person_MOBILE,
},
&pb_persion.Person_PhoneNumber{
Number: "222",
Type: pb_persion.Person_HOME,
},
},
}
return person
}
func encodeAndserialize() {
person := MakePerson()
// Xml to person.xml
bytes, _ := xml.MarshalIndent(&person, "", " ")
ioutil.WriteFile("./XmlFile.txt", bytes, 0644)
// Json to person.json
bytes, _ = json.MarshalIndent(&person, "", " ")
ioutil.WriteFile("./JsonFile.txt", bytes, 0644)
// Protobuf to person.pbuf
bytes, _ = proto.Marshal(&person)
ioutil.WriteFile("./pb_test.txt", bytes, 0644)
}
func FileSize() {
XmlFileInfo, err := os.Stat("./XmlFile.txt")
if err != nil {
fmt.Println("error : %s", err)
return
}
fmt.Println("XmlFileInfo: ",XmlFileInfo.Size())
JsonFileInfo, err := os.Stat("./JsonFile.txt")
if err != nil {
fmt.Println("error : %s", err)
return
}
fmt.Println("JsonFileInfo: ",JsonFileInfo.Size())
PbInfo, err := os.Stat("./pb_test.txt")
if err != nil {
fmt.Println("error : %s", err)
return
}
fmt.Println("PbInfo: ",PbInfo.Size())
}
运行结果:
XmlFileInfo: 568
JsonFileInfo: 143
PbInfo: 40