2. Protocol Buffers 入门:Protobuf 简介、安装 protoc 编译器、编写第一个 .proto 文件
好的,咱们正式开始聊 gRPC 的底层基石——Protocol Buffers(简称 Protobuf)。我在早期做分布式系统的时候,团队一直在 JSON 和 Protobuf 之间摇摆。后来我亲手把一套老接口从 JSON 迁移到 Protobuf,传输体积直接砍掉了一半以上。从那以后,只要涉及跨语言通信,我个人习惯第一选择就是 Protobuf。
2.1 Protocol Buffers 是什么?
说白了,Protobuf 就是一种与语言无关、与平台无关的序列化数据格式。你用它来定义数据结构(也就是消息),然后通过编译器自动生成各种语言(C++、Java、Python、Go 等)的代码。
举个例子:你要表示一个用户信息,用 JSON 的话是一串文本。而 Protobuf 会先写一个 .proto 文件描述结构,再用编译器转成各语言代码。数据在二进制流里压缩得更小,解析也更快。
核心思想:写一份定义,生成多语言代码,二进制传输。
2.2 为什么选 Protobuf?跟 JSON 比到底好在哪里?
我遇到过不少抵触的人,觉得 JSON 够用了。但这其实需要看场景。你想想看,如果只是几个字段、面向浏览器通信,JSON 确实方便。但一旦进入服务间高频调用、数据量大、对延迟敏感,Protobuf 的优势就非常明显。
| 对比维度 | Protobuf | JSON |
|---|---|---|
| 序列化体积 | 小(节省带宽) | 大(含字段名等元数据) |
| 解析性能 | 快(二进制直接映射) | 慢(文本解析) |
| 类型安全 | 强(编译期检查) | 弱(动态类型) |
| 跨语言支持 | 官方生成代码 | 各语言自行处理 |
| 可读性 | 差(二进制不可读) | 好(纯文本) |
所以一个比较务实的策略是:内部系统用 Protobuf,对外暴露必要时用 JSON 做协议转换。我在微服务项目里经常这样设计。
2.3 安装 protoc 编译器
工欲善其事,必先利其器。protoc 是用来把 .proto 文件编译成目标语言代码的工具。
- 前往 GitHub Releases 下载
地址:https://github.com/protocolbuffers/protobuf/releases
根据你的系统选择对应压缩包(protoc-<version>-<os>.zip)。 - 解压并加入 PATH
把解压后的bin目录添加到环境变量。我一般直接扔到/usr/local/bin或者C:\tools\里,省事。 - 验证安装
如果输出版本号(比如protoc --versionlibprotoc 3.21.12),就成功了。
小提示:Linux/macOS 下也可以用包管理器,比如 brew install protobuf 或 apt install protobuf-compiler。但官方包的版本往往更及时,我个人习惯直接下载二进制。
2.4 编写第一个 .proto 文件
咱们直接上手写一个。创建文件 person.proto:
syntax = "proto3";
message Person {
string name = 1;
int32 age = 2;
string email = 3;
}
解释一下:
syntax = "proto3":声明使用 proto3 语法(现在主流版本)。message Person:定义一个消息类型,类似于结构体或类。- 每个字段有类型、名称和编号(
=1、=2)。编号用于二进制编码,非常重要。
我曾经在这个编号上踩过坑:编号一旦发布使用就不能轻易改变,否则新旧版本数据会错乱。所以设计初期要预留够编号范围。
2.5 编译 .proto 文件
假设你现在要用 Python 或者 Go,那就执行:
# 生成 Python 代码
protoc --python_out=. person.proto
# 生成 Go 代码(需要额外安装 protoc-gen-go)
protoc --go_out=. person.proto
执行后会生成 person_pb2.py 或 person.pb.go。这些文件中包含了 Person 消息的序列化/反序列化方法,你直接 import 就能用。
避坑提示:Go 语言需要安装 protoc-gen-go 插件:go install google.golang.org/protobuf/cmd/protoc-gen-go@latest。否则 protoc 不会知道怎么生成 Go 代码。
2.6 一次完整的 Protobuf 工作流程
为了让流程更直观,我画了一张图:
2.7 小试牛刀:在 Python 中使用 Protobuf
生成 person_pb2.py 后,你可以这样操作:
import person_pb2
# 创建一个 Person 对象
person = person_pb2.Person()
person.name = "张三"
person.age = 28
person.email = "zhang@example.com"
# 序列化为字节串
data = person.SerializeToString()
print(f"序列化后大小: {len(data)} 字节")
# 反序列化
new_person = person_pb2.Person()
new_person.ParseFromString(data)
print(new_person.name)
输出大概是 序列化后大小: 22 字节。同样的数据如果用 JSON 会是多少?至少多一倍。这就是 Protobuf 的核心价值。
2.8 本章小结
这一节我们搞清楚了:
- Protobuf 是什么,为什么它比 JSON 更适用于服务间通信。
- 怎么安装 protoc 编译器。
- 怎么写一个简单的
.proto文件并且编译成 Python 代码。 - 走了一遍序列化-反序列化的完整流程。
你可能会觉得:“就这三五个字段,感觉有点简单?” 嗯,别急。下一个章节我们会进入 gRPC 的世界,到时候 .proto 文件里会有 service、rpc 方法定义,还要处理各种复杂类型。现在打好基础,后面你会越写越顺手。
额外提醒:如果你在 macOS 上遇到 protoc: command not found,八成是忘记加 PATH 了。我在首次配环境时就被这坑过一次。