protobuf是一个跨语言的传输协议格式,其功能和json类似. protobuf和json不同的是proto使用二进制进行传输,速度上会比json效率高. 但是在可读性上比json差.
protobuf底层使用C++实现的.但现在已经支持很多的语言使用了.目前支持:C++,java,python,objective-c,c#, javascript, ruby, go, php, Dart. 在这里讲protobuf在go中的使用.我使用的环境是ubuntu 16.04
在使用之前需要安装proto的基础包.基础包是C++编写的,所以需要用到编译C++一些常规的环境,这里不具体介绍.
安装go的环境
再者获取go相关的插件包:go get -u github.com/golang/protobuf
获取之后进入相应的文件夹,使用go build
, go install
进行编译和安装.使用命令:proto --version
测试是否成功安装.
chenmei@bogon:~/go/src/test/protobuf$ protoc --version
libprotoc 2.6.1
下面我们就看一个使用的例子,先写一个proto的原文件,这里以学生以及和学生管理的考试成绩为例子:
syntax = "proto2";
package pb;
message Course {
required string name = 1;
required int32 score = 2;
}
message Student {
required int32 UUID = 1;
required string Username = 2;
repeated Course courses = 3;
}
message Studentlist {
repeated Student students = 1;
}
上面就定义了课程,学生,以及学生的列表这几个结构.
使用命令进行转换:
protoc --go_out=. Studentlist.proto
当转换完成会生成如下的文件:
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: Studentlist.proto
package pb
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 Course struct {
Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"`
Score *int32 `protobuf:"varint,2,req,name=score" json:"score,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Course) Reset() { *m = Course{} }
func (m *Course) String() string { return proto.CompactTextString(m) }
func (*Course) ProtoMessage() {}
func (*Course) Descriptor() ([]byte, []int) {
return fileDescriptor_14fc7c21f335a148, []int{0}
}
func (m *Course) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Course.Unmarshal(m, b)
}
func (m *Course) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Course.Marshal(b, m, deterministic)
}
func (m *Course) XXX_Merge(src proto.Message) {
xxx_messageInfo_Course.Merge(m, src)
}
func (m *Course) XXX_Size() int {
return xxx_messageInfo_Course.Size(m)
}
func (m *Course) XXX_DiscardUnknown() {
xxx_messageInfo_Course.DiscardUnknown(m)
}
var xxx_messageInfo_Course proto.InternalMessageInfo
func (m *Course) GetName() string {
if m != nil && m.Name != nil {
return *m.Name
}
return ""
}
func (m *Course) GetScore() int32 {
if m != nil && m.Score != nil {
return *m.Score
}
return 0
}
type Student struct {
UUID *int32 `protobuf:"varint,1,req,name=UUID" json:"UUID,omitempty"`
Username *string `protobuf:"bytes,2,req,name=Username" json:"Username,omitempty"`
Courses []*Course `protobuf:"bytes,3,rep,name=courses" json:"courses,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Student) Reset() { *m = Student{} }
func (m *Student) String() string { return proto.CompactTextString(m) }
func (*Student) ProtoMessage() {}
func (*Student) Descriptor() ([]byte, []int) {
return fileDescriptor_14fc7c21f335a148, []int{1}
}
func (m *Student) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Student.Unmarshal(m, b)
}
func (m *Student) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Student.Marshal(b, m, deterministic)
}
func (m *Student) XXX_Merge(src proto.Message) {
xxx_messageInfo_Student.Merge(m, src)
}
func (m *Student) XXX_Size() int {
return xxx_messageInfo_Student.Size(m)
}
func (m *Student) XXX_DiscardUnknown() {
xxx_messageInfo_Student.DiscardUnknown(m)
}
var xxx_messageInfo_Student proto.InternalMessageInfo
func (m *Student) GetUUID() int32 {
if m != nil && m.UUID != nil {
return *m.UUID
}
return 0
}
func (m *Student) GetUsername() string {
if m != nil && m.Username != nil {
return *m.Username
}
return ""
}
func (m *Student) GetCourses() []*Course {
if m != nil {
return m.Courses
}
return nil
}
type Studentlist struct {
Students []*Student `protobuf:"bytes,1,rep,name=students" json:"students,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Studentlist) Reset() { *m = Studentlist{} }
func (m *Studentlist) String() string { return proto.CompactTextString(m) }
func (*Studentlist) ProtoMessage() {}
func (*Studentlist) Descriptor() ([]byte, []int) {
return fileDescriptor_14fc7c21f335a148, []int{2}
}
func (m *Studentlist) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Studentlist.Unmarshal(m, b)
}
func (m *Studentlist) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Studentlist.Marshal(b, m, deterministic)
}
func (m *Studentlist) XXX_Merge(src proto.Message) {
xxx_messageInfo_Studentlist.Merge(m, src)
}
func (m *Studentlist) XXX_Size() int {
return xxx_messageInfo_Studentlist.Size(m)
}
func (m *Studentlist) XXX_DiscardUnknown() {
xxx_messageInfo_Studentlist.DiscardUnknown(m)
}
var xxx_messageInfo_Studentlist proto.InternalMessageInfo
func (m *Studentlist) GetStudents() []*Student {
if m != nil {
return m.Students
}
return nil
}
func init() {
proto.RegisterType((*Course)(nil), "pb.Course")
proto.RegisterType((*Student)(nil), "pb.Student")
proto.RegisterType((*Studentlist)(nil), "pb.Studentlist")
}
func init() { proto.RegisterFile("Studentlist.proto", fileDescriptor_14fc7c21f335a148) }
var fileDescriptor_14fc7c21f335a148 = []byte{
// 158 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x0c, 0x2e, 0x29, 0x4d,
0x49, 0xcd, 0x2b, 0xc9, 0xc9, 0x2c, 0x2e, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x2a,
0x48, 0x52, 0x52, 0xe5, 0x62, 0x73, 0xce, 0x2f, 0x2d, 0x2a, 0x4e, 0x15, 0xe2, 0xe1, 0x62, 0xc9,
0x4b, 0xcc, 0x4d, 0x95, 0x60, 0x54, 0x60, 0xd2, 0xe0, 0x14, 0xe2, 0xe5, 0x62, 0x2d, 0x4e, 0xce,
0x2f, 0x4a, 0x95, 0x60, 0x52, 0x60, 0xd2, 0x60, 0x55, 0x72, 0xe3, 0x62, 0x87, 0xea, 0x07, 0xa9,
0x0b, 0x0d, 0xf5, 0x74, 0x01, 0xab, 0x63, 0x15, 0x12, 0xe0, 0xe2, 0x08, 0x2d, 0x4e, 0x2d, 0x02,
0xeb, 0x64, 0x02, 0xeb, 0x94, 0xe6, 0x62, 0x4f, 0x06, 0x9b, 0x58, 0x2c, 0xc1, 0xac, 0xc0, 0xac,
0xc1, 0x6d, 0xc4, 0xa5, 0x57, 0x90, 0xa4, 0x07, 0xb1, 0x44, 0x49, 0x87, 0x8b, 0x1b, 0xc9, 0x1d,
0x42, 0xb2, 0x5c, 0x1c, 0xc5, 0x10, 0x6e, 0xb1, 0x04, 0x23, 0x58, 0x31, 0x37, 0x48, 0x31, 0x54,
0x09, 0x20, 0x00, 0x00, 0xff, 0xff, 0x66, 0x6d, 0x7d, 0x4c, 0xb4, 0x00, 0x00, 0x00,
}
下面看main.go的写法,在main里面先对数据进行序列化,然后再反序列化.
package main
import (
"fmt"
"test/protobuf/pb"
"github.com/golang/protobuf/proto"
)
func main() {
course11 := pb.Course{
Name: proto.String("math"),
Score: proto.Int32(99),
}
course12 := pb.Course {
Name: proto.String("english"),
Score: proto.Int32(90),
}
course21 := pb.Course{
Name: proto.String("math"),
Score: proto.Int32(94),
}
course22 := pb.Course {
Name: proto.String("english"),
Score: proto.Int32(95),
}
student1 := pb.Student {
UUID: proto.Int32(1),
Username: proto.String("mei.chen"),
Courses: []*pb.Course{&course11, &course12},
}
student2 := pb.Student {
UUID: proto.Int32(2),
Username: proto.String("he.he"),
Courses: []*pb.Course{&course21, &course22},
}
students := pb.Studentlist {
Students: []*pb.Student{&student1,&student2},
}
//序列化:
data, err := proto.Marshal(&students)
if err != nil {
fmt.Println("marshal error", err)
}
fmt.Println("marshal data:",data)
//反序列化
var list pb.Studentlist
err = proto.Unmarshal(data, &list)
if err != nil {
fmt.Println("unmarshal error:",err)
}
for _, student := range list.Students {
fmt.Println("------------------------------------------")
fmt.Println("sudent name: ",student.GetUsername())
for _, course := range student.Courses {
fmt.Println("course: ",course.GetName())
fmt.Println("score: ",course.GetScore())
}
}
}
输出结果如下:
marshal data: [10 35 8 1 18 8 109 101 105 46 99 104 101 110 26 8 10 4 109 97 116 104 16 99 26 11 10 7 101 110 103 108 105 115 104 16 90 10 32 8 2 18 5 104 101 46 104 101 26 8 10 4 109 97 116 104 16 94 26 11 10 7 101 110 103 108 105 115 104 16 95]
------------------------------------------
sudent name: mei.chen
course: math
score: 99
course: english
score: 90
------------------------------------------
sudent name: he.he
course: math
score: 94
course: english
score: 95
这里需要注意的地方:
1.在proto文件中定义的字段如果首字母是小写,那么在转换的时候会变成大写.
2.在proto中定义的类型其实都是指针,例如string,需要使用proto.String转为指针类型. 这个你可以在转换后的文件中可以看到.
作者:amei 链接:http://114.116.145.166:5897/blogs/amei/articles/2019/04/02/1554195518207