Protobuf
2022-09-12
enum Gender {
UNKNOWN = 0;
MALE = 1;
FEMALE = 2;
}
转换成 Golang 之后是这个样子:
type Person_Gender int32
const (
Person_UNKNOWN Person_Gender = 0
Person_MALE Person_Gender = 1
Person_FEMALE Person_Gender = 2
)
// Enum value maps for Person_Gender.
var (
Person_Gender_name = map[int32]string{
0: "UNKNOWN",
1: "MALE",
2: "FEMALE",
}
Person_Gender_value = map[string]int32{
"UNKNOWN": 0,
"MALE": 1,
"FEMALE": 2,
}
)
func (x Person_Gender) Enum() *Person_Gender {
p := new(Person_Gender)
*p = x
return p
}
func (x Person_Gender) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (Person_Gender) Descriptor() protoreflect.EnumDescriptor {
return file_example_proto_enumTypes[0].Descriptor()
}
func (Person_Gender) Type() protoreflect.EnumType {
return &file_example_proto_enumTypes[0]
}
func (x Person_Gender) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use Person_Gender.Descriptor instead.
func (Person_Gender) EnumDescriptor() ([]byte, []int) {
return file_example_proto_rawDescGZIP(), []int{0, 0}
}
Protobuf
2022-09-12
相较于文本编码的 JSON
- 速度快(序列化和反序列化)
- 数据小(二进制数据流更加紧凑)
- 数据类型丰富(enum,map...)
- 可读性差
数据类型
Protobuf 类型 |
描述 |
Go 对应类型 |
int32 |
有符号 32 位整数,采用可变长度编码,适合较小的整数 |
int32 |
int64 |
有符号 64 位整数,采用可变长度编码 |
int64 |
uint32 |
无符号 32 位整数,采用可变长度编码 |
uint32 |
uint64 |
无符号 64 位整数,采用可变长度编码 |
uint64 |
sint32 |
有符号 32 位整数,采用 ZigZag 编码,适合负数较多的情况 |
int32 |
sint64 |
有符号 64 位整数,采用 ZigZag 编码 |
int64 |
fixed32 |
无符号 32 位整数,固定 4 字节编码,对于大整数比 uint32 更高效 |
uint32 |
fixed64 |
无符号 64 位整数,固定 8 字节编码 |
uint64 |
sfixed32 |
有符号 32 位整数,固定 4 字节编码 |
int32 |
sfixed64 |
有符号 64 位整数,固定 8 字节编码 |
int64 |
float |
单精度 32 位浮点数 |
float32 |
double |
双精度 64 位浮点数 |
float64 |
bool |
布尔值,取值为 true 或 false |
bool |
string |
包含 UTF - 8 编码或 7 位 ASCII 文本的字符串,长度不能超过 2^32 |
string |
bytes |
任意的字节序列,长度不能超过 2^32 |
[]byte |
message |
自定义的数据结构,由多个字段组成,可以嵌套定义 |
struct |
enum |
用于定义一组命名的常量值 |
Go 枚举实现 |
repeated |
在字段前加上 repeated 关键字表示该字段是一个数组 |
Go 数组 |
map<KeyType, ValueType> |
从 proto3 开始支持的映射类型,用于表示键值对 |
map |
-
枚举类型,有另一篇文章可供参考:Protobuf 枚举类型的实现
-
数组(Repeated)示例:
message Person {
repeated string hobbies = 5;
}
-
映射(Map)示例:
message Person {
map<string, string> contacts = 6;
}
这里 contacts
是一个键为字符串、值也为字符串的映射。
基本流程
- 定义数据结构(.proto 文件)
- 使用编译器生成目标编程语言代码
-
在程序中引入生成的代码
-
Protobuf 编译器: protoc,作用是生成目标语言的数据定义代码
生成的代码
需要到 GitHub 下载。
-
Protobuf 运行时,作用是序列化和反序列化的基础库
Lang |
Repo |
C++ |
https://github.com/protocolbuffers/protobuf/blob/main/src |
Java |
https://github.com/protocolbuffers/protobuf/blob/main/java |
Python |
https://github.com/protocolbuffers/protobuf/blob/main/python |
Golang |
https://github.com/protocolbuffers/protobuf-go |
示例
-
proto 文件
syntax = "proto3";
package example;
message Person {
enum Gender {
UNKNOWN = 0;
MALE = 1;
FEMALE = 2;
}
string name = 1;
int32 age = 2;
Gender gender = 3;
}
-
编译:
$ protoc --go_out=. --go_opt=Mexample.proto=./generated example.proto
$ find .
.
./example.proto
./generated
./generated/example.pb.go
-
调用 proto 生成的 Go 代码
package main
import (
"fmt"
"log"
pb "example/generated" // 导入生成的包
"google.golang.org/protobuf/proto"
)
func main() {
// 创建一个 Person 实例
person := &pb.Person{
Name: "Alice",
Age: 30,
Gender: pb.Person_FEMALE,
}
// 序列化
data, err := proto.Marshal(person)
if err != nil {
log.Fatalf("Failed to marshal: %v", err)
}
// 反序列化
newPerson := &pb.Person{}
err = proto.Unmarshal(data, newPerson)
if err != nil {
log.Fatalf("Failed to unmarshal: %v", err)
}
fmt.Printf("Deserialized person: %+v\n", newPerson)
}
$ go mod init example
$ go mod tidy
$ go build -o main.exe .
$ ./main.exe
Deserialized person: name:"Alice" age:30 gender:FEMALE