go语言序列化protobuf认知

为了提升接入数据效率,大家绞尽脑汁想出轻便高效的结构化数据存储格式,本文重点讲述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

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!