定义
Protocol buffers是一个灵活、高效、自动化地序列化结构化数据的方案。类似于XML,但是更快、更小和更灵活。你只需要定义一次数据的结构,就可以使用多种语言从多种数据流中读取结构化数据,并进行操作。而且可以很方便的变更数据的结构,而且不影响基于变更前的数据结构的程序。
在golang中,protocol buffers可以基于不同的传输协议进行传输,如HTTP/2和AMQP(Advanced Message Queuing Protocol)。
Protocol buffers是一个类似JSON的传输格式,但是是强类型的、只能被服务器端和客户端理解,人是无法阅读的。即以二进制的方式进行传输,
优势
Protocal buffers和JSON或XML在序列化结构化数据方面进行对比,具有如下的优势:
- 它是强类型的;
- 它的体积更小;
- 在序列化和反序列化时,速度更快;
- 由于具有类型和序号,它更加清晰,不容易混淆;
- 它会产生数据访问的类,对编程更加友好;
使用
编译
protoc编译器会自动的基于.proto文件生成Go的结构体。这些结构体后期就会被导入到项目中进行业务处理,参与到序列化、反序列化的过程中。
安装编译器
安装编译器的方式根据操作系统的不同而不同,具体可参考:
github网址
具体到mac上,只要使用如下命令即可:
brew install protobuf
注意
在我们安装完成后,实际上是无法从.proto文件生成golang文件的,它会显示如下错误信息:
这个错误实际上是在提示我们我们还没有安装能产生golang文件文件的插件,因为在安装protobuf,它只是安装如下的语言插件:
下面我们就来解决这个问题,即安装golang的插件:
go get -u github.com/golang/protobuf/protoc-gen-go
编译器的插件protoc-gen-go会安装到 GOBIN环境变量。 GOBIN必须在$PATH里,否则编译器protoc是无法找到它的。
编写proto文件
具体的内容如下:
syntax = "proto3";
package protofiles;
message Person {
string name = 1;
int32 id = 2;
string email = 3;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
string number = 1;
PhoneType type = 2;
}
repeated PhoneNumber phones = 4;
}
message AddressBook {
repeated Person persons = 1;
}
编译文件
在我们编写好proto文件后,我们可以通过如下命令进行文件的编译:
protoc --go_out=. *.proto
这个命令会发现当前目录下的所有的proto文件,并转化为golang文件。golang的文件名与proto文件名相同。当运行完此命令后,在当前目录下会生成一个新的golang文件:person.pb.go。如果我们打开这个文件,就会看到自动生成的文件内容:
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_4c9e10cf24b1156d, []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:"-"`
}
使用
在生成完数据的golang文件后,我们就可以基于结构体进行使用了。
序列化和反序列化
package main
import (
pb "awesomeProject/protobufs"
"github.com/golang/protobuf/proto"
"log"
)
func main() {
p := &pb.Person{
Id:1234,
Name: "Roger F",
Email: "[email protected]",
Phones: []*pb.Person_PhoneNumber{
{Number:"555-4321", Type: pb.Person_HOME},
},
}
log.Println(p)
p1 := &pb.Person{}
body, _ := proto.Marshal(p)
_ = proto.Unmarshal(body, p1)
log.Printf("%v", p1)
}
在执行上述程序后会显示如下信息:
Protocol buffers和JSON
在golang中使用protocol buffers的另一个好处是,基于编译proto文件生成的结构体,我们可以很容易的生成JSON格式的字符串,具体代码如下:
func main() {
p := &pb.Person{
Id:1234,
Name: "Roger F",
Email: "[email protected]",
Phones: []*pb.Person_PhoneNumber{
{Number:"555-4321", Type: pb.Person_HOME},
},
}
log.Println(p)
body, _ := json.Marshal(p)
log.Println(string(body))
}
在执行上述程序后会显示如下信息:
为什么使用protocol buffers
- 首先protocol buffers是为了后台系统间以强类型、小负载的方式通信而存在的。因为二进制文件普遍比文本文件小,所以protocal buffers生成的二进制文件要比JSON的文本文件小很多;
- 结合gPRC会显著的提高数据传输效率;