本文最后更新于:2026年1月23日 上午

Protobuf是一种“数据交换的高效压缩包”。它用人类可读的文本定义数据结构(.proto文件),然后将其编译成各种语言的高效代码,最终在传输和存储时使用极简的二进制格式,本文介绍相关内容。

简介

Protobuf(Protocol Buffers)是Google开发的一种高效、跨平台的结构化数据序列化与通信协议工具。你可以把它理解为一种更高效、更紧凑的“数据格式”和“数据交换语言”。

它解决了在不同系统(如不同编程语言编写的服务)之间高效、可靠地传输结构化数据的核心问题。

如果项目涉及多语言环境、对性能或带宽有较高要求,那么Protobuf是一个非常理想的选择。反之,如果只是简单的、内部使用的、需要人类可读配置的场景,JSON或YAML可能更直接。

主要优势

  1. 性能极高序列化后的二进制数据体积通常比JSON/XML小3到10倍,序列化/反序列化的速度也快很多,对网络传输和性能敏感的场景(如微服务、移动App)至关重要。
  2. 跨语言支持:通过一份.proto文件,可以自动生成Java, Python, C++, Go, C#, JavaScript等几乎所有主流语言的代码,保证了多语言系统间数据接口的一致性。
  3. 清晰的接口契约.proto文件本身就是清晰、版本化的API接口文档,强制了前后端/服务间的约定,减少了歧义。
  4. 良好的兼容性:通过optionalrequired等字段规则和版本号管理,可以平滑地向前/向后兼容字段的添加和删除,利于长期演进。

Protobuf vs. JSON/XML

假设我们要描述一个人的信息,对比一下不同方式:

JSON格式

1
2
3
4
5
{
"name": "张三",
"id": 12345,
"email": "zhangsan@example.com"
}

Protobuf格式
你首先需要定义一个 .proto 文件(就像一份数据结构的“契约”或“蓝图”):

1
2
3
4
5
message Person {
required string name = 1;
optional int32 id = 2;
optional string email = 3;
}

实际传输的二进制数据,是基于这份“蓝图”编码的,类似这样:

1
0A 05 E5 BC A0 E4 B8 89 10 B9 60 1A 16 ...

性能对比演示

安装库:

1
2
3
4
5
# 系统安装库
sudo apt install -y protobuf-compiler

# Python安装protobuf
pip install protobuf

Python 脚本:

protobuf_test.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
import json
import xml.etree.ElementTree as ET
import time
import sys
from google.protobuf.json_format import MessageToJson, Parse
from person_pb2 import Person as ProtobufPerson

# 导入protobuf生成类(需先编译person.proto)
# 生成命令:protoc --python_out=. person.proto

# 定义相同的数据结构
sample_data = {
"name": "张三",
"id": 12345,
"email": "zhangsan@example.com",
"phones": [
{"number": "13800138000", "type": "MOBILE"},
{"number": "010-12345678", "type": "WORK"}
],
"address": {
"street": "科技园路123号",
"city": "北京",
"zip_code": "100000"
}
}

def json_serialization_demo():
"""JSON序列化演示"""
print("=== JSON演示 ===")

# 序列化
start = time.perf_counter_ns()
json_str = json.dumps(sample_data, ensure_ascii=False)
json_time = time.perf_counter_ns() - start
json_size = len(json_str.encode('utf-8'))

# 反序列化
start = time.perf_counter_ns()
decoded_data = json.loads(json_str)
json_decode_time = time.perf_counter_ns() - start

print(f"数据大小: {json_size} 字节")
print(f"序列化时间: {json_time} ns")
print(f"反序列化时间: {json_decode_time} ns")
print(f"序列化后数据预览: {json_str[:80]}...")
return json_size, json_time, json_decode_time, json_str

def xml_serialization_demo():
"""XML序列化演示"""
print("\n=== XML演示 ===")

# 创建XML结构
person = ET.Element("person")
ET.SubElement(person, "name").text = sample_data["name"]
ET.SubElement(person, "id").text = str(sample_data["id"])
ET.SubElement(person, "email").text = sample_data["email"]

phones = ET.SubElement(person, "phones")
for phone in sample_data["phones"]:
phone_elem = ET.SubElement(phones, "phone")
ET.SubElement(phone_elem, "number").text = phone["number"]
ET.SubElement(phone_elem, "type").text = phone["type"]

address = ET.SubElement(person, "address")
ET.SubElement(address, "street").text = sample_data["address"]["street"]
ET.SubElement(address, "city").text = sample_data["address"]["city"]
ET.SubElement(address, "zip_code").text = sample_data["address"]["zip_code"]

# 序列化
start = time.perf_counter_ns()
xml_str = ET.tostring(person, encoding='unicode', method='xml')
xml_time = time.perf_counter_ns() - start
xml_size = len(xml_str.encode('utf-8'))

# 反序列化
start = time.perf_counter_ns()
root = ET.fromstring(xml_str)
# 解析XML数据
xml_data = {
"name": root.find("name").text,
"id": int(root.find("id").text),
"email": root.find("email").text,
"phones": [],
"address": {}
}
for phone_elem in root.find("phones").findall("phone"):
xml_data["phones"].append({
"number": phone_elem.find("number").text,
"type": phone_elem.find("type").text
})
address_elem = root.find("address")
xml_data["address"]["street"] = address_elem.find("street").text
xml_data["address"]["city"] = address_elem.find("city").text
xml_data["address"]["zip_code"] = address_elem.find("zip_code").text

xml_decode_time = time.perf_counter_ns() - start

print(f"数据大小: {xml_size} 字节")
print(f"序列化时间: {xml_time} ns")
print(f"反序列化时间: {xml_decode_time} ns")
print(f"序列化后数据预览: {xml_str[:80]}...")
return xml_size, xml_time, xml_decode_time, xml_str

def protobuf_serialization_demo():
"""Protobuf序列化演示"""
print("\n=== Protobuf演示 ===")

# 创建Protobuf对象
person = ProtobufPerson()
person.name = sample_data["name"]
person.id = sample_data["id"]
person.email = sample_data["email"]

# 添加电话
for phone in sample_data["phones"]:
phone_entry = person.phones.add()
phone_entry.number = phone["number"]
if phone["type"] == "MOBILE":
phone_entry.type = ProtobufPerson.PhoneType.MOBILE
else:
phone_entry.type = ProtobufPerson.PhoneType.WORK

# 设置地址
person.address.street = sample_data["address"]["street"]
person.address.city = sample_data["address"]["city"]
person.address.zip_code = sample_data["address"]["zip_code"]

# 序列化
start = time.perf_counter_ns()
binary_data = person.SerializeToString()
pb_time = time.perf_counter_ns() - start
pb_size = len(binary_data)

# 反序列化
start = time.perf_counter_ns()
decoded_person = ProtobufPerson()
decoded_person.ParseFromString(binary_data)
pb_decode_time = time.perf_counter_ns() - start

# 转换为字典以便比较
pb_dict = {
"name": decoded_person.name,
"id": decoded_person.id,
"email": decoded_person.email,
"phones": [
{
"number": phone.number,
"type": ProtobufPerson.PhoneType.Name(phone.type)
}
for phone in decoded_person.phones
],
"address": {
"street": decoded_person.address.street,
"city": decoded_person.address.city,
"zip_code": decoded_person.address.zip_code
}
}

print(f"数据大小: {pb_size} 字节")
print(f"序列化时间: {pb_time} ns")
print(f"反序列化时间: {pb_decode_time} ns")
print(f"序列化后数据预览: 二进制数据,不可直接阅读")

# 将protobuf转换为JSON用于显示
json_from_pb = MessageToJson(decoded_person, preserving_proto_field_name=True)
print(f"转换为JSON后预览: {json_from_pb[:80]}...")

return pb_size, pb_time, pb_decode_time, binary_data

def performance_comparison():
"""性能对比总结"""
print("\n" + "="*60)
print("性能对比总结")
print("="*60)

# 运行所有演示
json_results = json_serialization_demo()
xml_results = xml_serialization_demo()
pb_results = protobuf_serialization_demo()

print("\n" + "="*60)
print("详细对比表")
print("="*60)
print(f"{'格式':<10} | {'大小(字节)':<12} | {'序列化时间(ns)':<16} | {'反序列化时间(ns)':<18} | {'压缩率'}")
print("-"*80)

formats = ["JSON", "XML", "Protobuf"]
results = [json_results, xml_results, pb_results]

for i in range(3):
size, ser_time, deser_time, _ = results[i]
compression_rate = f"{size/json_results[0]*100:.1f}%"
print(f"{formats[i]:<10} | {size:<12} | {ser_time:<16} | {deser_time:<18} | {compression_rate}")

print("\n" + "="*60)
print("Protobuf优势总结")
print("="*60)
print(f"1. 数据大小:比JSON小{json_results[0]/pb_results[0]:.1f}倍,比XML小{xml_results[0]/pb_results[0]:.1f}倍")
print(f"2. 序列化速度:比JSON快{json_results[1]/pb_results[1]:.1f}倍,比XML快{xml_results[1]/pb_results[1]:.1f}倍")
print(f"3. 反序列化速度:比JSON快{json_results[2]/pb_results[2]:.1f}倍,比XML快{xml_results[2]/pb_results[2]:.1f}倍")
print(f"4. 类型安全:有严格的类型检查和编译时验证")
print(f"5. 向后兼容:支持字段的添加和删除而不破坏旧版本")

# 验证数据一致性
print("\n" + "="*60)
print("数据一致性验证")
print("="*60)

# 重新解析所有格式并比较
json_data = json.loads(json_results[3])
root = ET.fromstring(xml_results[3])

# 解析XML数据(简化版)
xml_data = {
"name": root.find("name").text,
"id": int(root.find("id").text),
"email": root.find("email").text
}

decoded_person = ProtobufPerson()
decoded_person.ParseFromString(pb_results[3])
pb_dict = {
"name": decoded_person.name,
"id": decoded_person.id,
"email": decoded_person.email
}

print("所有格式解析出的核心数据一致:")
print(f" JSON: name={json_data['name']}, id={json_data['id']}")
print(f" XML: name={xml_data['name']}, id={xml_data['id']}")
print(f" Proto: name={pb_dict['name']}, id={pb_dict['id']}")

if __name__ == "__main__":
# 首先需要定义并编译person.proto文件
# person.proto内容如下:
"""
syntax = "proto3";

package demo;

message Person {
string name = 1;
int32 id = 2;
string email = 3;

message PhoneNumber {
string number = 1;
PhoneType type = 2;
}

repeated PhoneNumber phones = 4;

message Address {
string street = 1;
string city = 2;
string zip_code = 3;
}

Address address = 5;
}

enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
"""

print("注意:运行此示例前,请确保:")
print("1. 已安装protobuf: pip install protobuf")
print("2. 已创建person.proto文件")
print("3. 已编译proto文件: protoc --python_out=. person.proto")
print("4. person_pb2.py文件已存在\n")

try:
performance_comparison()
except ImportError as e:
print(f"错误:{e}")
print("请确保已正确编译person.proto文件")
except Exception as e:
print(f"运行时错误:{e}")

创建 person.proto 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
syntax = "proto3";

package demo;

message Person {
string name = 1;
int32 id = 2;
string email = 3;

message PhoneNumber {
string number = 1;
PhoneType type = 2;
}

repeated PhoneNumber phones = 4;

message Address {
string street = 1;
string city = 2;
string zip_code = 3;
}

Address address = 5;
}

enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
  • 编译 proto 文件
1
protoc --python_out=. person.proto

该步骤会生成 person_pb2.py 文件

  • 执行 Python 脚本后输出:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
============================================================
性能对比总结
============================================================
=== JSON演示 ===
数据大小: 250 字节
序列化时间: 93036 ns
反序列化时间: 52183 ns
序列化后数据预览: {"name": "张三", "id": 12345, "email": "zhangsan@example.com", "phones": [{"number...

=== XML演示 ===
数据大小: 325 字节
序列化时间: 111002 ns
反序列化时间: 32268 ns
序列化后数据预览: <person><name>张三</name><id>12345</id><email>zhangsan@example.com</email><phones>...

=== Protobuf演示 ===
数据大小: 104 字节
序列化时间: 14297 ns
反序列化时间: 3972 ns
序列化后数据预览: 二进制数据,不可直接阅读
转换为JSON后预览: {
"name": "\u5f20\u4e09",
"id": 12345,
"email": "zhangsan@example.com",
...

============================================================
详细对比表
============================================================
格式 | 大小(字节) | 序列化时间(ns) | 反序列化时间(ns) | 压缩率
--------------------------------------------------------------------------------
JSON | 250 | 93036 | 52183 | 100.0%
XML | 325 | 111002 | 32268 | 130.0%
Protobuf | 104 | 14297 | 3972 | 41.6%

============================================================
Protobuf优势总结
============================================================
1. 数据大小:比JSON小2.4倍,比XML小3.1倍
2. 序列化速度:比JSON快6.5倍,比XML快7.8倍
3. 反序列化速度:比JSON快13.1倍,比XML快8.1倍
4. 类型安全:有严格的类型检查和编译时验证
5. 向后兼容:支持字段的添加和删除而不破坏旧版本

============================================================
数据一致性验证
============================================================
所有格式解析出的核心数据一致:
JSON: name=张三, id=12345
XML: name=张三, id=12345
Proto: name=张三, id=12345

展示了Protobuf在性能数据大小类型安全方面的显著优势,尤其适合高性能和高并发的生产环境。



文章链接:
https://www.zywvvd.com/notes/coding/python/protobuf/protobuf/


“觉得不错的话,给点打赏吧 ୧(๑•̀⌄•́๑)૭”

微信二维码

微信支付

支付宝二维码

支付宝支付

Protobuf 简介与测试
https://www.zywvvd.com/notes/coding/python/protobuf/protobuf/
作者
Yiwei Zhang
发布于
2026年1月17日
许可协议