TOC

Protobuf 二进制编码

相较于文本编码的 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 布尔值,取值为 truefalse 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
  1. 枚举类型,有另一篇文章可供参考:Protobuf 枚举类型的实现

  2. 数组(Repeated)示例:

    message Person {
        repeated string hobbies = 5;
    }
    
  3. 映射(Map)示例:

    message Person {
        map<string, string> contacts = 6;
    }
    

    这里 contacts 是一个键为字符串、值也为字符串的映射。

基本流程

  1. 定义数据结构(.proto 文件)
  2. 使用编译器生成目标编程语言代码
  3. 在程序中引入生成的代码

  4. Protobuf 编译器: protoc,作用是生成目标语言的数据定义代码
    生成的代码
    需要到 GitHub 下载。

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

示例

  1. proto 文件

    syntax = "proto3";
    
    package example;
    
    message Person {
        enum Gender {
            UNKNOWN = 0;
            MALE = 1;
            FEMALE = 2;
        }
        string name = 1;
        int32 age = 2;
        Gender gender = 3;
    }
    
  2. 编译:

    $ protoc --go_out=. --go_opt=Mexample.proto=./generated example.proto
    $ find .
    .
    ./example.proto
    ./generated
    ./generated/example.pb.go
    
  3. 调用 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
    
如果你有魔法,你可以看到一个评论框~