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 的优势就非常明显。

对比维度ProtobufJSON
序列化体积小(节省带宽)大(含字段名等元数据)
解析性能快(二进制直接映射)慢(文本解析)
类型安全强(编译期检查)弱(动态类型)
跨语言支持官方生成代码各语言自行处理
可读性差(二进制不可读)好(纯文本)

所以一个比较务实的策略是:内部系统用 Protobuf,对外暴露必要时用 JSON 做协议转换。我在微服务项目里经常这样设计。

2.3 安装 protoc 编译器

工欲善其事,必先利其器。protoc 是用来把 .proto 文件编译成目标语言代码的工具。

  1. 前往 GitHub Releases 下载
    地址:https://github.com/protocolbuffers/protobuf/releases
    根据你的系统选择对应压缩包(protoc-<version>-<os>.zip)。
  2. 解压并加入 PATH
    把解压后的 bin 目录添加到环境变量。我一般直接扔到 /usr/local/bin 或者 C:\tools\ 里,省事。
  3. 验证安装
    protoc --version
    如果输出版本号(比如 libprotoc 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 工作流程

为了让流程更直观,我画了一张图:

Protobuf 工作流程 编写.proto protoc 编译 生成代码 序列化 网络传输 反序列化 说明:以上流程只在开发端进行。运行时数据经过序列化-网络-反序列化。 编译生成代码是一次性的,后续直接复用。

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 了。我在首次配环境时就被这坑过一次。