Protobuf 和 protoc-gen-go 详解

Protobuf 是什么?

Protocol Buffers(Protobuf) 是 Google 开发的一种语言中立、平台中立的数据序列化格式,类似于 XML 和 JSON,但更小、更快、更简单。

主要特点:

  • 高效:二进制格式,体积小,解析速度快
  • 跨语言:支持 C++, Java, Python, Go, C# 等多种语言
  • 强类型:通过 .proto 文件定义数据结构
  • 向后兼容:可以在不破坏现有代码的情况下更新数据结构
  • 自动生成代码:根据定义自动生成各语言的数据访问类

protoc-gen-go 是什么?

protoc-gen-go 是 Protobuf 编译器(protoc)的 Go 语言插件,用于将 .proto 文件编译生成 Go 语言代码。

工作流程:

复制代码
.proto 文件 → protoc (编译器) → protoc-gen-go (插件) → .pb.go 文件

案例演示

案例 1: 基础消息定义

1. 创建 person.proto 文件:

复制代码
syntax = "proto3";

package example;
option go_package = "example.com/myapp/pb";

message Person {
  string name = 1;
  int32 age = 2;
  string email = 3;
  repeated string phone_numbers = 4;
}

2. 安装工具:

复制代码
# 安装 protoc 编译器
# macOS: brew install protobuf
# Ubuntu: apt-get install protobuf-compiler

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

3. 生成 Go 代码:

复制代码
protoc --go_out=. --go_opt=paths=source_relative person.proto

4. 使用生成的代码:

复制代码
复制代码
package main

import (
    "fmt"
    "google.golang.org/protobuf/proto"
    pb "example.com/myapp/pb"
)

func main() {
    // 创建对象
    person := &pb.Person{
        Name:  "张三",
        Age:   30,
        Email: "zhangsan@example.com",
        PhoneNumbers: []string{"123-4567", "890-1234"},
    }
    
    // 序列化
    data, err := proto.Marshal(person)
    if err != nil {
        panic(err)
    }
    fmt.Printf("序列化后大小: %d 字节\n", len(data))
    
    // 反序列化
    newPerson := &pb.Person{}
    err = proto.Unmarshal(data, newPerson)
    if err != nil {
        panic(err)
    }
    fmt.Printf("姓名: %s, 年龄: %d\n", newPerson.Name, newPerson.Age)
}

案例 2: gRPC 服务定义

1. 创建 user_service.proto:

复制代码
syntax = "proto3";

package userservice;
option go_package = "example.com/myapp/userservice";

// 请求消息
message GetUserRequest {
  int64 user_id = 1;
}

// 响应消息
message GetUserResponse {
  int64 user_id = 1;
  string username = 2;
  string email = 3;
}

// 服务定义
service UserService {
  rpc GetUser(GetUserRequest) returns (GetUserResponse);
}

2. 生成代码(包含 gRPC):

复制代码
复制代码
# 需要额外安装 grpc 插件
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

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

3. 实现服务端:

复制代码
package main

import (
    "context"
    "net"
    "google.golang.org/grpc"
    pb "example.com/myapp/userservice"
)

type server struct {
    pb.UnimplementedUserServiceServer
}

func (s *server) GetUser(ctx context.Context, req *pb.GetUserRequest) (*pb.GetUserResponse, error) {
    return &pb.GetUserResponse{
        UserId:   req.UserId,
        Username: "张三",
        Email:    "zhangsan@example.com",
    }, nil
}

func main() {
    lis, _ := net.Listen("tcp", ":50051")
    s := grpc.NewServer()
    pb.RegisterUserServiceServer(s, &server{})
    s.Serve(lis)
}

案例 3: 复杂数据结构

order.proto:

复制代码
syntax = "proto3";

package shop;
option go_package = "example.com/shop/pb";

import "google/protobuf/timestamp.proto";

enum OrderStatus {
  PENDING = 0;
  PAID = 1;
  SHIPPED = 2;
  COMPLETED = 3;
}

message Product {
  string product_id = 1;
  string name = 2;
  double price = 3;
  int32 quantity = 4;
}

message Order {
  string order_id = 1;
  OrderStatus status = 2;
  repeated Product products = 3;
  google.protobuf.Timestamp created_at = 4;
  
  // 嵌套消息
  message Address {
    string street = 1;
    string city = 2;
    string country = 3;
  }
  Address shipping_address = 5;
}

使用示例:

复制代码
package main

import (
    "fmt"
    "time"
    "google.golang.org/protobuf/types/known/timestamppb"
    pb "example.com/shop/pb"
)

func main() {
    order := &pb.Order{
        OrderId: "ORD-12345",
        Status:  pb.OrderStatus_PAID,
        Products: []*pb.Product{
            {ProductId: "P1", Name: "笔记本电脑", Price: 5999.99, Quantity: 1},
            {ProductId: "P2", Name: "鼠标", Price: 99.99, Quantity: 2},
        },
        CreatedAt: timestamppb.New(time.Now()),
        ShippingAddress: &pb.Order_Address{
            Street:  "中关村大街1号",
            City:    "北京",
            Country: "中国",
        },
    }
    
    fmt.Printf("订单号: %s\n", order.OrderId)
    fmt.Printf("状态: %s\n", order.Status)
    fmt.Printf("商品数量: %d\n", len(order.Products))
}

总结

  • Protobuf:高效的数据序列化格式
  • protoc-gen-go:将 .proto 转换为 Go 代码的工具
  • 适用场景:微服务通信、数据存储、配置文件等
  • 优势:性能高、跨语言、类型安全
相关推荐
Alice-YUE5 分钟前
【js高频八股】防抖与节流
开发语言·前端·javascript·笔记·学习·ecmascript
云泽8089 分钟前
C++11 核心特性全解:列表初始化、右值引用与移动语义实战
开发语言·c++
froginwe1127 分钟前
DOM 加载函数
开发语言
Hello eveybody43 分钟前
介绍一下背包DP(Python)
开发语言·python·动态规划·dp·背包dp
AI进化营-智能译站1 小时前
ROS2 C++开发系列12-用多态与虚函数构建可扩展的ROS2机器人行为模块
开发语言·c++·ai·机器人
iCxhust1 小时前
微机原理实践教程(C语言篇)---A002流水灯
c语言·开发语言·单片机·嵌入式硬件·51单片机·课程设计·微机原理
莎士比亚的文学花园1 小时前
Linux驱动开发(3)——设备树
开发语言·javascript·ecmascript
图码2 小时前
如何用多种方法判断字符串是否为回文?
开发语言·数据结构·c++·算法·阿里云·线性回归·数字雕刻
U盘失踪了2 小时前
python curl转python脚本
开发语言·chrome·python
charlie1145141912 小时前
Linux 字符设备驱动:cdev、设备号与设备模型
linux·开发语言·驱动开发·c