Protobuf简介与安装
2026/1/15大约 4 分钟ProtobufProtocol Buffers序列化
Protobuf 简介与安装
一、Protobuf 简介
1.1 什么是 Protobuf
Protocol Buffers(简称 Protobuf)是 Google 开发的一种数据序列化协议,用于结构化数据的序列化和反序列化。
核心特点:
- 语言无关、平台无关
- 高效的二进制编码
- 向后兼容、向前兼容
- 自动生成代码
- 支持多种编程语言
1.2 为什么使用 Protobuf
1.3 Protobuf vs JSON
示例对比:
JSON 格式(55 字节):
{"name":"张三","age":25,"email":"test@example.com"}Protobuf 格式(约 20 字节):
二进制数据,体积更小性能对比:
| 指标 | Protobuf | JSON |
|---|---|---|
| 序列化速度 | 快 3-5 倍 | 基准 |
| 反序列化速度 | 快 5-10 倍 | 基准 |
| 数据体积 | 小 30-50% | 基准 |
| 可读性 | 差(二进制) | 好(文本) |
二、安装 Protobuf
2.1 安装 protoc 编译器
macOS:
brew install protobuf
# 验证安装
protoc --versionLinux:
# 下载预编译包
wget https://github.com/protocolbuffers/protobuf/releases/download/v25.1/protoc-25.1-linux-x86_64.zip
# 解压
unzip protoc-25.1-linux-x86_64.zip -d protoc
# 移动到系统路径
sudo mv protoc/bin/protoc /usr/local/bin/
sudo mv protoc/include/* /usr/local/include/
# 验证
protoc --versionWindows:
# 下载 protoc-25.1-win64.zip
# 解压并添加到 PATH 环境变量2.2 安装 Go 插件
# 安装 protoc-gen-go(生成 Go 代码)
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
# 安装 protoc-gen-go-grpc(生成 gRPC 代码)
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
# 确保 $GOPATH/bin 在 PATH 中
export PATH="$PATH:$(go env GOPATH)/bin"2.3 验证安装
# 检查 protoc
protoc --version
# libprotoc 25.1
# 检查 Go 插件
which protoc-gen-go
which protoc-gen-go-grpc三、快速开始
3.1 创建 .proto 文件
// user.proto
syntax = "proto3";
package user;
option go_package = "example.com/user";
message User {
int32 id = 1;
string name = 2;
string email = 3;
int32 age = 4;
}3.2 编译生成 Go 代码
protoc --go_out=. --go_opt=paths=source_relative user.proto生成的文件:user.pb.go
3.3 使用生成的代码
package main
import (
"fmt"
"log"
"google.golang.org/protobuf/proto"
pb "example.com/user"
)
func main() {
// 创建消息
user := &pb.User{
Id: 1,
Name: "张三",
Email: "zhangsan@example.com",
Age: 25,
}
// 序列化
data, err := proto.Marshal(user)
if err != nil {
log.Fatal("序列化失败:", err)
}
fmt.Printf("序列化后大小: %d 字节\n", len(data))
// 反序列化
newUser := &pb.User{}
err = proto.Unmarshal(data, newUser)
if err != nil {
log.Fatal("反序列化失败:", err)
}
fmt.Printf("用户: %s, 年龄: %d\n", newUser.Name, newUser.Age)
}四、项目结构
4.1 推荐目录结构
myproject/
├── proto/
│ ├── user.proto
│ ├── order.proto
│ └── common.proto
├── pb/
│ ├── user.pb.go
│ ├── order.pb.go
│ └── common.pb.go
├── main.go
└── go.mod4.2 Makefile 自动化
# Makefile
.PHONY: proto clean
proto:
protoc --go_out=pb --go_opt=paths=source_relative \
proto/*.proto
clean:
rm -f pb/*.pb.go使用:
make proto # 编译所有 proto 文件
make clean # 清理生成的文件五、基本语法
5.1 消息定义
syntax = "proto3";
message Person {
string name = 1; // 字段编号 1
int32 age = 2; // 字段编号 2
string email = 3; // 字段编号 3
}关键点:
syntax = "proto3":指定 proto3 语法- 字段编号:唯一标识字段,不能修改
- 字段类型:string、int32、bool 等
5.2 字段编号规则
message Example {
// 1-15:单字节编码,用于常用字段
string name = 1;
int32 age = 2;
// 16-2047:双字节编码
string description = 16;
// 19000-19999:保留字段,不能使用
// reserved 19000 to 19999;
}5.3 注释
// 单行注释
/*
* 多行注释
* 用于详细说明
*/
message User {
string name = 1; // 行尾注释
}六、常用数据类型
6.1 标量类型
| Proto 类型 | Go 类型 | 说明 |
|---|---|---|
| double | float64 | 双精度浮点数 |
| float | float32 | 单精度浮点数 |
| int32 | int32 | 整数 |
| int64 | int64 | 长整数 |
| uint32 | uint32 | 无符号整数 |
| uint64 | uint64 | 无符号长整数 |
| bool | bool | 布尔值 |
| string | string | 字符串 |
| bytes | []byte | 字节数组 |
6.2 示例
message DataTypes {
double price = 1;
float score = 2;
int32 age = 3;
int64 timestamp = 4;
uint32 count = 5;
bool active = 6;
string name = 7;
bytes data = 8;
}七、编译选项
7.1 常用选项
# 指定输出目录
protoc --go_out=./pb user.proto
# 保持源文件路径
protoc --go_out=. --go_opt=paths=source_relative user.proto
# 指定 Go 包名
protoc --go_out=. --go_opt=module=example.com user.proto
# 同时生成多种语言
protoc --go_out=. --java_out=. --python_out=. user.proto7.2 go_package 选项
syntax = "proto3";
// 指定生成的 Go 包路径
option go_package = "example.com/myapp/pb";
message User {
string name = 1;
}八、完整示例
8.1 定义消息
// user.proto
syntax = "proto3";
package user;
option go_package = "example.com/myapp/pb/user";
message User {
int32 id = 1;
string username = 2;
string email = 3;
int32 age = 4;
bool active = 5;
int64 created_at = 6;
}
message UserList {
repeated User users = 1;
int32 total = 2;
}8.2 编译
protoc --go_out=. --go_opt=paths=source_relative user.proto8.3 使用
package main
import (
"fmt"
"time"
"google.golang.org/protobuf/proto"
pb "example.com/myapp/pb/user"
)
func main() {
// 创建用户列表
userList := &pb.UserList{
Users: []*pb.User{
{
Id: 1,
Username: "张三",
Email: "zhangsan@example.com",
Age: 25,
Active: true,
CreatedAt: time.Now().Unix(),
},
{
Id: 2,
Username: "李四",
Email: "lisi@example.com",
Age: 30,
Active: true,
CreatedAt: time.Now().Unix(),
},
},
Total: 2,
}
// 序列化
data, _ := proto.Marshal(userList)
fmt.Printf("序列化后大小: %d 字节\n", len(data))
// 反序列化
newList := &pb.UserList{}
proto.Unmarshal(data, newList)
fmt.Printf("用户总数: %d\n", newList.Total)
for _, user := range newList.Users {
fmt.Printf("- %s (%s)\n", user.Username, user.Email)
}
}