golang使用protocol-buffers简单示例

在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二进制程序

下载地址 : protobuf/releases

在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设置语法类型,有proto2proto3两种语法。

  • package addressbook可以设置生成的golang代码的包名。

  • message对应于golang中的struct,可以看到文件中一共定义了PersonPhoneNumberAddressBook3个message,其中PhoneNumberPerson的嵌套类型。

  • message中有字段,可以是intstring,枚举或者其他消息类型。

  • 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: "[email protected]",
		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倍)、更为简单
  • 扩展性、兼容性好。可以更新数据结构,而不影响和破坏原有的旧程序

why-not-xml

示例:

func main() {
	encodeAndserialize()
	FileSize()
}

func MakePerson()  pb_persion.Person {
	person := pb_persion.Person{
		Id:    1,
		Name:  "zy",
		Email: "[email protected]",
		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

参考资料:

proto3官方文档

golang使用protocol-buffers

golang-protocol-buffers-api

你可能感兴趣的:(golang,go,protobuf)