为了提升接入数据效率,大家绞尽脑汁想出轻便高效的结构化数据存储格式,本文重点讲述protobuf二进制格式以及和常见json对比性能。按照protobuf官网介绍,protobuf原因是:通过protobuf提供的机制,服务端与服务端之间只需要关注接口方法名(service)和参数(message)即可通信,而不需关注繁琐的链路协议和字段解析,极大降低了服务端的设计开发成本。但是 Protobuf 真的有吹的那么牛么?
1、protobuf前期准备
1.1、安装protobuf编译器
wget https://github.com/protocolbuffers/protobuf/releases/download/v3.11.2/protoc-3.11.2-linux-x86_64.zip
# 解压到 /usr/local 目录下
unzip protoc-3.11.2-linux-x86_64.zip -o/usr/local
#验证成功
[root@lin bin]# protoc --version
libprotoc 3.11.2
1.2、安装protobuf-go插件
protobuf有多种语言转换插件,其中protobuf-go是针对go语言转换工具。
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
#进入gopath/bin下查看是否已正确下载
[root@lin bin]# go env | grep GOPATH
GOPATH="/root/go"
[root@lin bin]# ls -sl | grep proto
8828 -rwxr-xr-x 1 root root 9036308 9月 6 02:47 protoc-gen-go
2、创建protobuf项目
2.1、go mod init创建项目
mkdir -p protobuf
cd protobuf
go mod init protobuf
2.2、编写proto文件,如person.proto
项目下创建person目录,然后创建person.proto文件
syntax = "proto3"; //表示使用proto3
package person;
option go_package="."; //指定protoc-gen-go生成的go包的路径,注意:一定要写,不然编译报错
message Person
{
uint32 doc_id = 1;//1、2...代表序列号,唯一对应的
string position = 2;
string company = 3;
string city = 4;
int32 school_level = 5;
bool vip = 6;
bool chat = 7;
int32 active = 8;
int32 work_age=9;
}
2.3、根据person.proto生成person.pb.go
进入person目录下,执行如下指令:
protoc --go_out=. --plugin=/root/go/bin/protoc-gen-go *.proto
protoc:为使用protoc编译器;
–go_out=…/proto_go:表示要将生成的go文件输出到哪里;
–plugin指定protoc-gen-go位置
*.proto:要进行编译的文件;
编译完毕之后,会在proto_go中出现person.pb.go文件。
如果person.pb.go文件中package为__,改成person即可。
[root@lin protobuffer]# tree
.
├── go.mod
├── go.sum
├── main.go
├── person
│ ├── person.pb.go
│ └── person.proto
3、与json对比
3.1、建一个对比json的结构体user,里面字段和person一样
mkdir -p user
cd user
touch user.go
[root@lin user]# cat user.go
package user
type User struct {
DocId uint32
Position string
Company string
City string
SchoolLevel int32
Vip bool
Chat bool
Active int32
WorkAge int32
}
3.2、测试代码
benchmark:
[root@lin test]# cat protobuffer_test.go
package test
import (
"encoding/json"
"fmt"
"github.com/golang/protobuf/proto"
"protobuffer/person"
"protobuffer/user"
"testing"
)
var p = person.Person{DocId: 123, Position: "搜索工程师", Company: "百度", City: "北京", SchoolLevel: 2, Vip: false, Chat: true, Active: 1, WorkAge: 3}
var u = user.User{DocId: 123, Position: "搜索工程师", Company: "百度", City: "北京", SchoolLevel: 2, Vip: false, Chat: true, Active: 1, WorkAge: 3}
func BenchmarkJsonEncode(c *testing.B) {
for i := 0; i < c.N; i++ {
json.Marshal(&u)
}
}
func BenchmarkJsonDecode(b *testing.B) {
bs, _ := json.Marshal(u)
var inst user.User
b.ResetTimer()
for i := 0; i < b.N; i++ {
json.Unmarshal(bs, &inst)
}
}
func BenchmarkPbEncode(b *testing.B) {
for i := 0; i < b.N; i++ {
proto.Marshal(&p)
}
}
func BenchmarkPbDecode(b *testing.B) {
bs, _ := proto.Marshal(&p)
var inst person.Person
b.ResetTimer()
for i := 0; i < b.N; i++ {
proto.Unmarshal(bs, &inst)
}
}
非benchmark
package main
import (
"encoding/json"
"github.com/golang/protobuf/proto"
"log"
"protobuffer/person"
"protobuffer/user"
"time"
)
func main() {
//创建一个 Person 对象
p := &person.Person{DocId: 123, Position: "搜索工程师", Company: "百度", City: "北京", SchoolLevel: 2, Vip: false, Chat: true, Active: 1, WorkAge: 3}
u := &user.User{DocId: 123, Position: "搜索工程师", Company: "百度", City: "北京", SchoolLevel: 2, Vip: false, Chat: true, Active: 1, WorkAge: 3}
tii(p, u)
}
func tii(p *person.Person, u *user.User) {
// 使用 Protocol Buffers 进行序列化和反序列化
startTime := time.Now()
serializedPB, err := proto.Marshal(p)
if err != nil {
log.Fatal("Protocol Buffers serialization error: ", err)
}
// 反序列化
newPB := &person.Person{}
err = proto.Unmarshal(serializedPB, newPB)
if err != nil {
log.Fatal("Protocol Buffers deserialization error: ", err)
}
endTime := time.Now()
pbDuration := endTime.Sub(startTime)
log.Println("Protocol Buffers serialization/deserialization time:", pbDuration)
// 使用 JSON 进行序列化和反序列化
startTime = time.Now()
serializedJSON, err := json.Marshal(u)
if err != nil {
log.Fatal("JSON serialization error: ", err)
}
// 反序列化
newJSON := &user.User{}
err = json.Unmarshal(serializedJSON, newJSON)
if err != nil {
log.Fatal("JSON deserialization error: ", err)
}
endTime = time.Now()
jsonDuration := endTime.Sub(startTime)
log.Println("JSON serialization/deserialization time:", jsonDuration)
}
3.3、测试结果
不采用benchmark时,json比protobuf更好一点点
[root@lin protobuffer]# go run main.go
2023/09/09 22:49:12 Protocol Buffers serialization/deserialization time: 318.195µs
2023/09/09 22:49:12 JSON serialization/deserialization time: 216.034µs
采用benchmark,protobuf更佳
[root@lin test]# go test -bench=.
json encode byte length 140
json decode position 搜索工程师
pb encode byte length 43
pb decode position 搜索工程师
goos: linux
goarch: amd64
pkg: protobuffer/test
cpu: Intel(R) Core(TM) i7-10510U CPU @ 1.80GHz
BenchmarkJsonEncode-4 693571 1693 ns/op
BenchmarkJsonDecode-4 139368 8567 ns/op
BenchmarkPbEncode-4 1321004 949.8 ns/op
BenchmarkPbDecode-4 1223168 844.9 ns/op
PASS
ok protobuffer/test 7.622s
4、个人认为
根据上面测试对比数据来看,并不是我们项目一定要使用protobuf,它更适合大量数据接入且数据结构类型是整型更有优势,一般情况下,我们使用好理解json或者fastjson即可。
原文始发于微信公众号(云原生内经):go语言序列化protobuf认知
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/167911.html