Golang Protocol Buffer

本文介绍如何在 Go 应用中使用 Protocol Buffer 数据。

Protocol Buffer 数据格式

Protocol Buffer 是 Google 开源的一套成熟的结构数据序列化机制(当然也可以使用其他数据格式如 JSON),和 JSON 或 XML 一样,用于结构化数据序列化或反序列化。该数据格式的优势是较 xml 或 json 更小。

gRPC 默认使用 protocol buffers。

假如我们有一个对象,我们用三种数据结构进行表示:

<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 ......]

如果您仔细观察上面编码可能会看到,从数组位置2开始名字 ellio t被拼出来,e = 69, l = 108等等。后面是年龄的字节表示,24岁。

Protocol buffer 优缺点

优点

性能好/效率高

时间维度:XML,JSON格式对数据进行序列化和反序列化时性能差。

空间维度:XML,JSON格式为了可读性,必然在进行存储数据时,也会消耗空间。

整体而言,Protobuf以高效的二进制方式存储,比XML小3到10倍,快20到100倍。

代码生成机制

代码生成机制能够极大解放开发者编写数据协议解析过程的时间,提高工作效率;于开发者维护和迭代。

支持“向后兼容”和“向前兼容”

支持多种编程语言

Protocol buffer 不仅是 Google 开源的一个数据协议,还有很多种语言的开源项目实现。

缺点

可读性较差

为了提高性能,Protobuf采用了二进制格式进行编码。二进制格式编码对于开发者来说,是没办法阅读的。在进行程序调试时,比较困难。对于可读性要求高的数据报文程序就不建议使用。

缺乏自描述

Protocol buffer 是通过二进制格式进行数据传输,开发者面对二进制格式的 Protocol buffer,没有办法知道所对应的真实的数据结构,因此在使用Protobuf协议传输时,必须配备对应的proto配置文件。

golang 使用 protobuf

protobuf 官网

https://protobuf.dev/

安装 protobuf 编译器

go install google.golang.org/protobuf/cmd/protoc-gen-go@latest   

编写 protobuf 文件

如 :/protos/person.proto

// 声明规则版本
syntax = "proto3";
// 指定生成的包名
option go_package = "./protos";
// 定义Person结构体
message Person {
  int32 id = 1;
  string name = 2;
  string email = 3;
}

生成 .go 文件

protoc --proto_path=./protos --go_out=. person.proto

测试序列化

编写 main.go 运行测试

package main

import (
	"fmt"
	"pb/protos"

	"google.golang.org/protobuf/proto"
)

func main() {
	p1 := &protos.Person{
		Id:    1,
		Name:  "John",
		Email: "john@gmail.com",
	}
	fmt.Println("原始数据:", p1)

	// 序列化
	dataMarshal, err := proto.Marshal(p1)
	if err != nil {
		fmt.Println("proto.Unmarshal.Err: ", err)
		return
	}
	fmt.Println("编码数据:", dataMarshal)

	// 反序列化
	entity := protos.Person{}
	err = proto.Unmarshal(dataMarshal, &entity)
	if err != nil {
		fmt.Println("proto.Unmarshal.Err: ", err)
		return
	}

	fmt.Printf("解码数据: ID:%d 姓名:%s 邮箱:%s ", entity.GetId(), entity.GetName(), entity.GetEmail())
}