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 代码的工具
- 适用场景:微服务通信、数据存储、配置文件等
- 优势:性能高、跨语言、类型安全