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 代码的工具
  • 适用场景:微服务通信、数据存储、配置文件等
  • 优势:性能高、跨语言、类型安全
相关推荐
永远前进不waiting2 小时前
C复习——1
c语言·开发语言
间彧2 小时前
Vert.x与Spring框架:开发效率与团队学习成本深度对比
后端
伯明翰java2 小时前
Java数据类型与变量
java·开发语言
间彧2 小时前
Vert.x与传统Spring框架在性能、并发处理方面有哪些差异
后端
一路往蓝-Anbo2 小时前
【第13期】中断机制详解 :从向量表到ISR
c语言·开发语言·stm32·单片机·嵌入式硬件
间彧2 小时前
Vert.x框架详解与项目实战:构建高性能异步应用
后端
间彧2 小时前
Spring Boot 与 Disruptor 高性能并发实战
后端
渣渣盟2 小时前
Linux邮件服务器快速搭建指南
linux·服务器·开发语言
想用offer打牌2 小时前
如何开启第一次开源贡献之路?
java·后端·面试·开源·github