3. 数据序列化与反序列化:Protobuf vs JSON vs MessagePack
做市商系统里,数据怎么在进程间、机器间高效传输?
这个问题我琢磨了很久。说白了,就是序列化与反序列化的选择。你想想看,每秒几万笔订单,每笔订单包含价格、数量、时间戳、订单ID……如果序列化效率低,整个管道的吞吐量就上不去。
我个人习惯把序列化方案分成三类:文本型(JSON)、二进制紧凑型(MessagePack)、二进制强类型型(Protobuf)。今天咱们就掰开揉碎聊聊这三兄弟。
3.1 三种方案的底层逻辑
JSON:人类可读,调试方便。但它的Schema是隐式的,解析时得动态构建对象树。我在项目中遇到过,一个嵌套5层的JSON结构,解析耗时占了整个请求处理时间的40%。
MessagePack:本质上是JSON的二进制变体。它用更少的字节表示相同的数据。比如数字123,JSON要存成字符串"123"占3字节,MessagePack只占1字节(0x7B)。但它的Schema依然是隐式的,这点和JSON一样。
Protobuf:Google出品,强类型。你得先定义.proto文件,然后生成代码。它的编码方式很聪明——用字段编号代替字段名,用Varint压缩整数。我做过测试,同样的订单数据,Protobuf比JSON小60%左右。
核心结论:JSON适合配置和调试,MessagePack适合对延迟不敏感但带宽有限的场景,Protobuf适合高性能、高吞吐的做市商核心链路。
3.2 Schema管理——别让数据变成一团乱麻
做市商系统里,数据格式会变。今天加个字段,明天改个类型。如果没有Schema管理,你会陷入噩梦。
我曾经接手过一个遗留系统,订单消息用JSON,但不同版本之间字段名大小写不统一。有的地方用"orderId",有的用"order_id"。解析时各种if-else,维护成本极高。
我的建议是:
- Protobuf:天然支持Schema。.proto文件就是契约。用
optional和repeated处理可选字段和重复字段。版本升级时,遵循向后兼容原则——只加字段,不改已有字段编号。 - JSON/MessagePack:需要手动维护Schema文档。我习惯用JSON Schema规范,配合代码生成工具(如quicktype)生成强类型类。但说实话,这不如Protobuf原生支持来得自然。
避坑指南:我曾经在.proto文件中使用required关键字,结果后来要删除这个字段时,发现所有历史数据都解析失败了。现在我只用optional和repeated,绝不使用required。
3.3 编解码性能基准测试
光说不练假把式。咱们直接看数据。
测试环境:Intel Xeon Gold 6248 @ 2.5GHz,32GB RAM,JDK 11。测试数据:100万条订单消息,每条包含10个字段(int64, string, double, int32, bool等)。
| 指标 | JSON (Jackson) | MessagePack (msgpack-java) | Protobuf (protobuf-java) |
|---|---|---|---|
| 序列化耗时 (ms) | 1,245 | 987 | 412 |
| 反序列化耗时 (ms) | 1,532 | 1,102 | 489 |
| 数据大小 (MB) | 45.6 | 32.1 | 18.3 |
| GC压力 (Minor GC次数) | 23 | 17 | 6 |
看到没?Protobuf在序列化速度、数据大小、GC压力上全面领先。MessagePack比JSON好一些,但差距不大。
为什么会这样?
JSON解析时,Jackson需要先构建JsonNode树,再映射到Java对象。这个过程会产生大量临时对象。Protobuf直接操作字节流,用预生成的代码解析,几乎没有临时对象。
我印象很深,有一次优化做市商系统的行情推送管道。原来用JSON,GC暂停时间平均8ms,偶尔飙到30ms。换成Protobuf后,GC暂停降到2ms以下,再也没有超过5ms。嗯,这就是差距。
3.4 实战中的选择策略
做市商系统里,我一般这样选:
- 行情数据推送:Protobuf。延迟敏感,数据量大,Schema稳定。
- 订单日志:MessagePack。需要持久化,带宽有限,但Schema偶尔变化。
- 配置文件和REST API:JSON。人类可读,调试方便,性能不是瓶颈。
- 进程间通信(IPC):Protobuf。共享内存或Unix Domain Socket场景下,零拷贝优势明显。
注意:不要盲目追求极致性能。如果你们的系统每秒只有几百笔订单,JSON完全够用。引入Protobuf会增加编译步骤和Schema维护成本。我见过一个团队,为了用Protobuf而用Protobuf,结果.proto文件版本混乱,反而降低了开发效率。
3.5 知识体系图
下面这张图,是我梳理的数据序列化选型决策流程。你可以把它贴在工位上。
这张图的核心逻辑:先问延迟敏感不敏感。敏感,直接上Protobuf。不敏感,再看Schema稳定性。Schema稳定,用MessagePack省带宽;Schema不稳定,用JSON保灵活。
好了,关于数据序列化,我就讲这么多。记住一句话:没有银弹,只有最适合你场景的方案。
公众号:蓝海资料掘金营,微信deep3321