本文我们介绍如何在Golang应用中使用Protocol Buffers数据格式。包括Protocol Buffers的定义,与传统xml、json相比的优势,并通过几个示例进行实践。
Protocol Buffers数据格式
Protocol Buffers是Google推出的一种数据格式。我们知道json/xml可用在不同语言中存储数据,实现序列化和反序列化。它与json/xml格式相比,存储大小要小很多。
假设有person对象,我们用三种数据格式进行对比描述:
<person>
<name>Elliot</name>
<age>24</age>
</person>
用json描述会小点:
{
"name": "Elliot",
"age": 24
}
如果同样数据用protocol buffer表示:
[10 6 69 108 108 105 111 116 16 24]
仔细观察会发现,从数组第二个位置开始Elliot
被表示出来, E
=69, l
=108,依此下去,最后是年龄24.
编码格式比我们看到的要复杂得多,我也在尝试对它了解更多,如果需要建议查看谷歌文档。现在虽然我们的示例表示的大小与json差不多。下面我们考虑更复杂的场景,大小差异会更多。
简单对象序列化示例
首先我们需要安装protoc工具,下载地址:https://github.com/protocolbuffers/protobuf/releases ;
windows64对应文件为:protoc-3.19.4-win64.zip;下载后解压protoc.exe至go安装路径下bin目标,确保可以在任何位置执行该文件。
go get google.golang.org/protobuf
go get -u github.com/golang/protobuf/protoc-gen-go
安装完成后,在命令行中测试protoc命令是否可用。
下面我们定义 protobuf
数据格式,我们对上面person对象进行修改。首先指定使用的语法,这里使用proto3
格式。然后指定文件位置:option go_package ="../pb";
表示person.proto文件位于pb目录下,接着指定包,完整内容如下:
syntax="proto3";
option go_package ="../pb";
package pb;
message Person {
string name = 1;
int32 age = 2;
}
最后在pb目录下执行命令生成对应go文件:
protoc --go_out=. person.proto
正常生成不会出现错误,在pb目录中生成person.pb.go文件。下面我们定义Person对象并使用protobuf格式进行编码,然后使用fmt.Println(data)
输出 protobuf格式对象:
package main
import (
"fmt"
"github.com/dataz.cn/wordcount/pb"
"github.com/golang/protobuf/proto"
"log"
)
func main() {
elliot := &pb.Person{
Name: "Elliot",
Age: 24,
}
data, err := proto.Marshal(elliot)
if err != nil {
log.Fatal("marshaling error: ", err)
}
fmt.Println(data)
newElliot := &pb.Person{}
err = proto.Unmarshal(data, newElliot)
if err != nil {
log.Fatal("unmarshaling error: ", err)
}
fmt.Println(newElliot.GetAge())
fmt.Println(newElliot.GetName())
}
接着对编码后的对象进行反序列化生成新的对象,打印其属性进行验证。
运行上面代码,输出如下:
[10 6 69 108 108 105 111 116 16 24]
24
Elliot
嵌套类型序列化示例
上面示例稍显简单,实际应用会更复杂。下面我们看嵌套类型序列化情况,Employee对象中嵌套SocialFollowers对象:
syntax="proto3";
option go_package ="../pb";
package pb;
message SocialFollowers {
int32 weixin = 1;
int32 weibo = 2;
}
message Employee {
string name = 1;
int32 age = 2;
SocialFollowers socialFollowers = 3;
}
与上面示例的命令生成对应go文件。下面示例展示嵌套对象的序列化与反序列化过程:
package main
import (
"fmt"
"github.com/dataz.cn/wordcount/pb"
"github.com/golang/protobuf/proto"
"log"
)
func main() {
elliot := pb.Employee{
Name: "Elliot",
Age: 24,
SocialFollowers: &pb.SocialFollowers{
Weixin: 2500,
Weibo: 1400,
},
}
data, err := proto.Marshal(&elliot)
if err != nil {
log.Fatal("序列化错误: ", err)
}
newElliot := &pb.Employee{}
err = proto.Unmarshal(data, newElliot)
if err != nil {
log.Fatal("反序列错误: ", err)
}
fmt.Println(newElliot.GetName())
fmt.Println(newElliot.GetAge())
fmt.Println(newElliot.SocialFollowers.GetWeixin())
fmt.Println(newElliot.SocialFollowers.GetWeibo())
}
再次运行输出结果为:
Elliot
24
2500
1400
总结
本文我们介绍了Google的protocol buffer
数据格式,并通过示例展示利用该格式序列化、反序列化对象的过程。