找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

3668

积分

0

好友

478

主题
发表于 2 小时前 | 查看: 4| 回复: 0

前两天有个读者问我:

“项目里序列化用 JSON 就好了吧,为什么大厂非要搞 Protobuf?”

这个问题问得很好。很多人写代码,序列化这件事从来没认真想过,能跑就行。

但当你的服务每天处理几亿次请求,序列化格式的选择,直接决定了你的带宽成本、CPU 消耗、接口延迟。

今天就把 JSON、Protobuf、MessagePack 这三个放在一起,彻底讲清楚。

先搞懂:序列化是在做什么?

程序里的数据——一个结构体、一个对象——是活在内存里的。你想把它存到磁盘,或者通过网络发给另一台机器,就必须把它“压扁”成一串字节。

这个过程就是序列化,反过来还原叫反序列化

三个选手,一句话介绍

JSON:人能直接看懂,键值对文本格式,到处都支持。

Protobuf:Google 出品,二进制格式,体积小、速度快,需要提前定义 .proto 文件。

MessagePack:也是二进制格式,可以理解成“二进制版 JSON”,不需要提前定义 schema。

同一份数据,三种格式长什么样?

假设我们有这样一条用户数据:

用户ID: 1001
用户名: "xiaokang"
年龄: 28
是否VIP: true

JSON 序列化后:

{
  "user_id": 1001,
  "username": "xiaokang",
  "age": 28,
  "is_vip": true
}

可读性很强,但你注意到了吗?字段名 user_idusername 这些也被完整传输了,纯属浪费。

Protobuf 序列化后(二进制,示意):

08 E9 07 12 08 78 69 61 6F 6B 61 6E 67 18 1C 20 01

没有字段名,只有字段编号 + 值,体积缩到极致。

MessagePack 序列化后(二进制,示意):

84 A7 75 73 65 72 5F 69 64 CD 03 E9 A8 75 73 65 72...

比 JSON 小,但因为还是保留了 key,比 Protobuf 大一些。

图解:三种格式编码方式

JSON、MessagePack与Protobuf编码方式对比:展示字段名、字段值与字段编号的差异,直观体现Protobuf体积最小

对比同样的 4 个字段,JSON 传了 60 字节,Protobuf 只用了 17 字节——差了将近 4 倍

这不是 benchmark 数字游戏,而是实实在在的带宽和性能差距。当你的服务每秒处理 10 万条消息的时候,这个差距就会直接变成账单上的数字。

为什么 Protobuf 这么小?

核心原因只有一个:它不传字段名

JSON 每次都要老老实实把 "user_id""username" 这些字符串打包进去,接收方才知道每个值是什么意思。

Protobuf 在 .proto 文件里提前定好字段编号,发送方只传编号+值,接收方拿到编号 1,就知道这是 user_id

// user.proto
message User {
  int32  user_id  = 1;
  string username = 2;
  int32  age      = 3;
  bool   is_vip   = 4;
}

提前约定好,运行时就省掉了所有字段名的开销。这不仅是序列化格式的选择,更是一种典型的后端架构设计权衡——用前期的 Schema 定义成本,换取运行时的极致效率。

性能实测数据

光说体积还不够,实际的序列化速度呢?

用同一份 C++ 数据做 10 万次序列化 + 反序列化,结果如下:

JSON、MessagePack与Protobuf在10万次序列化与反序列化操作的耗时对比柱状图

Protobuf 序列化只用了 65ms,JSON 用了 320ms,差距将近 5 倍

这还只是 10 万次。你可以想象,在一个每天处理亿级请求的微服务里,这个差距意味着什么。

那什么时候用哪个?

三种格式都有自己的场景,不存在“谁更好”,只有“谁更合适”。

用 JSON 的场景:

  • 对外暴露的 REST API(前端要能直接读)
  • 调试、日志记录(人要能直接看)
  • 对性能要求不高的内部接口

用 MessagePack 的场景:

  • 想要 JSON 的灵活性(无需提前定 schema),但又在意体积
  • 跨语言项目,不想引入 Protobuf 的编译流程
  • 游戏、实时通信这类对体积敏感但接口变动频繁的场景

用 Protobuf 的场景:

  • 高并发微服务之间的通信(RPC 首选)
  • 对性能和带宽极度敏感的场景
  • 接口相对稳定,愿意维护 .proto 文件

用代码感受一下差距

下面用 C++ 分别序列化同一份数据,感受三者的代码风格:

JSON(用 nlohmann/json):

nlohmann::json j;
j["user_id"]  = 1001;
j["username"] = "xiaokang";
j["age"]      = 28;
j["is_vip"]   = true;
std::string data = j.dump(); // 直接得到可读字符串

Protobuf:

User user;
user.set_user_id(1001);
user.set_username("xiaokang");
user.set_age(28);
user.set_is_vip(true);
std::string data;
user.SerializeToString(&data); // 二进制,人看不懂,但很快

MessagePack(用 msgpack-c):

msgpack::sbuffer buf;
msgpack::packer<msgpack::sbuffer> pk(&buf);
pk.pack_map(4);
pk.pack(std::string("user_id"));  pk.pack(1001);
pk.pack(std::string("username")); pk.pack(std::string("xiaokang"));
pk.pack(std::string("age"));      pk.pack(28);
pk.pack(std::string("is_vip"));   pk.pack(true);

可以看出:JSON 最直觉,Protobuf 需要预先生成代码,MessagePack 灵活但手写稍繁琐。

最后一张图总结

JSON、MessagePack与Protobuf多维度性能与特性对比表格,包括数据体积、速度、可读性、跨语言支持等

小结

选序列化格式这件事,没有银弹。

写对外 API——用 JSON,别让前端骂你。

写高并发内部服务、RPC——上 Protobuf,性能差距肉眼可见。

想要折中——MessagePack 是个不错的过渡方案。

大多数人在职业生涯早期会一直用 JSON,直到系统遇到性能瓶颈才开始考虑 Protobuf。理解背后的原因,你就能在架构评审里说得清楚为什么换、换了能带来多大提升——这才是真正的工程判断力。





上一篇:Modern C++工程实战:28个特性从C++11到C++20全解析
下一篇:前端加密逆向实战:AES解密与水平越权漏洞挖掘全流程
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-6-5 03:51 , Processed in 1.032601 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表