38.3 Protocol Buffer数据格式

Protocol Buffer 简单称为protobuf(Pb),是Google开发出来的一个语言无关、平台无关的数据序列化工具,在rpc或tcp通信等很多场景都可以使用。在服务端定义一个数据结构,通过protobuf转化为字节流,再传送到客户端解码,就可以得到对应的数据结构。它的通信效率极高,同一条消息数据,用protobuf序列化后的大小是JSON的10分之一左右。

为了正常使用Protobuf,我们需要做一些准备工作。

1、下载Protobuf的编译器protoc,地址:https://github.com/google/protobuf/releases

window用户下载: protoc-3.6.1-win32.zip,然后解压,把protoc.exe文件复制到GOPATH/bin下。 Linux 用户下载:protoc-3.6.1-linux-x86_64.zip 或 protoc-3.6.1-linux-x86_32.zip,然后解压,把protoc文件复制到GOPATH/bin下。

2、获取Protobuf的编译器插件protoc-gen-go。

在命令行运行 go get -u github.com/golang/protobuf/protoc-gen-go 会在GOPATH/bin下生成protoc-gen-go.exe文件,如果没有请自行build。GOPATH/bin目录建议加入path,以便后续操作方便。

接下来我们可以正式开始尝试怎么使用Protobuf了。我们需要创建一个.proto 结尾的文件,这个文件需要按照一定规则编写。

具体请见官方说明:https://developers.google.com/protocol-buffers/docs/proto 也可参考:https://gowalker.org/github.com/golang/protobuf/proto

Protobuf的使用方法是将数据结构写入到.proto文件中,使用protoc编译器编译(通过调用protoc-gen-go)得到一个新的go包,里面包含go中可以使用的数据结构和一些辅助方法。

下面我们先创建一个msg.proto文件

syntax = "proto3";
package learn;
message UserInfo {
    int32 UserType = 1;     //必选字段
    string UserName = 2;    //必选字段
    string UserInfo = 3;    //必选字段
}

运行如下命令

> protoc --go_out=.  msg.proto

会生成一个msg.pb.go的文件,代码如下。

// Code generated by protoc-gen-go. DO NOT EDIT.
// source: msg.proto
package learn
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.ProtoPackageIsVersion2 // please upgrade the proto package
type UserInfo struct {
	UserType             int32    `protobuf:"varint,1,opt,name=UserType,proto3" json:"UserType,omitempty"`
	UserName             string   `protobuf:"bytes,2,opt,name=UserName,proto3" json:"UserName,omitempty"`
	UserInfo             string   `protobuf:"bytes,3,opt,name=UserInfo,proto3" json:"UserInfo,omitempty"`
	XXX_NoUnkeyedLiteral struct{} `json:"-"`
	XXX_unrecognized     []byte   `json:"-"`
	XXX_sizecache        int32    `json:"-"`
}
func (m *UserInfo) Reset()         { *m = UserInfo{} }
func (m *UserInfo) String() string { return proto.CompactTextString(m) }
func (*UserInfo) ProtoMessage()    {}
func (*UserInfo) Descriptor() ([]byte, []int) {
	return fileDescriptor_c06e4cca6c2cc899, []int{0}
}
func (m *UserInfo) XXX_Unmarshal(b []byte) error {
	return xxx_messageInfo_UserInfo.Unmarshal(m, b)
}
func (m *UserInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
	return xxx_messageInfo_UserInfo.Marshal(b, m, deterministic)
}
func (m *UserInfo) XXX_Merge(src proto.Message) {
	xxx_messageInfo_UserInfo.Merge(m, src)
}
func (m *UserInfo) XXX_Size() int {
	return xxx_messageInfo_UserInfo.Size(m)
}
func (m *UserInfo) XXX_DiscardUnknown() {
	xxx_messageInfo_UserInfo.DiscardUnknown(m)
}
var xxx_messageInfo_UserInfo proto.InternalMessageInfo
func (m *UserInfo) GetUserType() int32 {
	if m != nil {
		return m.UserType
	}
	return 0
}
func (m *UserInfo) GetUserName() string {
	if m != nil {
		return m.UserName
	}
	return ""
}
func (m *UserInfo) GetUserInfo() string {
	if m != nil {
		return m.UserInfo
	}
	return ""
}
func init() {
	proto.RegisterType((*UserInfo)(nil), "learn.UserInfo")
}
func init() { proto.RegisterFile("msg.proto", fileDescriptor_c06e4cca6c2cc899) }
var fileDescriptor_c06e4cca6c2cc899 = []byte{
	// 100 bytes of a gzipped FileDescriptorProto
	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0xcc, 0x2d, 0x4e, 0xd7,
	0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0xcd, 0x49, 0x4d, 0x2c, 0xca, 0x53, 0x8a, 0xe3, 0xe2,
	0x08, 0x2d, 0x4e, 0x2d, 0xf2, 0xcc, 0x4b, 0xcb, 0x17, 0x92, 0x82, 0xb0, 0x43, 0x2a, 0x0b, 0x52,
	0x25, 0x18, 0x15, 0x18, 0x35, 0x58, 0x83, 0xe0, 0x7c, 0x98, 0x9c, 0x5f, 0x62, 0x6e, 0xaa, 0x04,
	0x93, 0x02, 0xa3, 0x06, 0x67, 0x10, 0x9c, 0x0f, 0x93, 0x03, 0x99, 0x21, 0xc1, 0x8c, 0x90, 0x03,
	0xf1, 0x93, 0xd8, 0xc0, 0xb6, 0x19, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0x7e, 0x5d, 0xad, 0x78,
	0x7a, 0x00, 0x00, 0x00,
}

接下来,我们在Go语言程序中使用Protobuf。

package main
import (
	"github.com/golang/protobuf/proto"
	"fmt"
	"ind/pb"
)
func main() {
	//初始化message UserInfo
	usermsg := &pb.UserInfo{
		UserType: 1,
		UserName: "Jok",
		UserInfo: "I am a woker!",
	}
	//序列化
	userdata, err := proto.Marshal(usermsg)
	if err != nil {
		fmt.Println("Marshaling error: ", err)
	}
	//反序列化
	encodingmsg := &pb.UserInfo{}
	err = proto.Unmarshal(userdata, encodingmsg)
	if err != nil {
		fmt.Println("Unmarshaling error: ", err)
	}
	fmt.Printf("GetUserType: %d\n", encodingmsg.GetUserType())
	fmt.Printf("GetUserName: %s\n", encodingmsg.GetUserName())
	fmt.Printf("GetUserInfo: %s\n", encodingmsg.GetUserInfo())
}
程序输出:
GetUserType: 1
GetUserName: Jok
GetUserInfo: I am a woker!

通过上面的介绍,我们已经学会了怎么使用Protobuf来处理我们的数据。

下一节:Go 提供了database/sql包用于对关系型数据库的访问。