← gRPC | Sonic →

Protobuf - Go 数据序列化

Protocol Buffers (Protobuf) 是 Google 开发的二进制序列化格式,广泛用于 gRPC 和数据存储。掌握 Protobuf 是开发高性能分布式系统的基础。

定义消息

📝 .proto 文件

// user.proto
syntax = "proto3";

package user;

option go_package = "github.com/example/proto";

// 枚举类型
enum UserRole {
    USER_ROLE_UNSPECIFIED = 0;
    USER_ROLE_ADMIN = 1;
    USER_ROLE_MEMBER = 2;
    USER_ROLE_GUEST = 3;
}

// 消息定义
message User {
    int64 id = 1;
    string name = 2;
    string email = 3;
    int32 age = 4;
    UserRole role = 5;
    bool active = 6;
    repeated string tags = 7;  // 列表
    Profile profile = 8;         // 嵌套消息
}

message Profile {
    string bio = 1;
    string avatar_url = 2;
    map<string, string> social_links = 3;  // Map
}

// Oneof: 互斥字段
message Contact {
    oneof contact_info {
        string email = 1;
        string phone = 2;
    }
}

生成代码

📝 编译 Proto 文件

# 安装 protoc 编译器
# macOS
brew install protobuf

# Ubuntu/Debian
apt-get install protobuf-compiler

# 安装 Go 插件
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

# 生成 Go 代码
protoc --go_out=. --go_opt=paths=source_relative \
       --go-grpc_out=. --go-grpc_opt=paths=source_relative \
       user.proto

# 生成文件:
# - user.pb.go (消息定义)
# - user_grpc.pb.go (gRPC 服务)

使用消息

📝 序列化和反序列化

package main

import (
    "fmt"
    "google.golang.org/protobuf/proto"
    pb "path/to/proto"
)

func main() {
    // 创建消息
    user := &pb.User{
        Id:    1,
        Name:  "Alice",
        Email: "alice@example.com",
        Age:   30,
        Role:  pb.UserRole_USER_ROLE_MEMBER,
        Active: true,
        Tags:   []string{"go", "protobuf"},
        Profile: &pb.Profile{
            Bio: "Go Developer",
            SocialLinks: map[string]string{
                "github": "alice",
                "twitter": "@alice",
            },
        },
    }
    
    // 序列化
    data, err := proto.Marshal(user)
    if err != nil {
        panic(err)
    }
    fmt.Printf("Serialized size: %d bytes\n", len(data))
    
    // 反序列化
    var user2 pb.User
    if err := proto.Unmarshal(data, &user2); err != nil {
        panic(err)
    }
    
    fmt.Printf("Deserialized: %+v\n", &user2)
}

JSON 互操作

📝 JSON 转换

import (
    "google.golang.org/protobuf/encoding/protojson"
    pb "path/to/proto"
)

func main() {
    user := &pb.User{
        Id: 1,
        Name: "Alice",
    }
    
    // Protobuf → JSON
    jsonData, err := protojson.Marshal(user)
    if err != nil {
        panic(err)
    }
    fmt.Println(string(jsonData))
    // {"id":"1","name":"Alice"}
    
    // JSON → Protobuf
    var user2 pb.User
    if err := protojson.Unmarshal(jsonData, &user2); err != nil {
        panic(err)
    }
}

📖 延伸阅读