4. 数据序列化:JSON、Protocol Buffers、MessagePack、CBOR对比与选型

数据序列化这事儿,说白了就是给数据「打包」。在边缘计算场景下,设备端和云端之间要传数据,怎么传、传多快、占多大空间,直接决定了你的系统能不能跑得稳。

我这些年做过的项目里,因为序列化选型翻车的案例真不少。有人图省事全用JSON,结果带宽吃紧;有人一上来就上Protobuf,结果调试时哭都哭不出来。今天咱们就把四种主流方案掰开揉碎聊清楚。

4.1 为什么序列化这么重要?

你想想看,边缘设备通常资源有限——CPU主频低、内存小、网络带宽窄。如果序列化方案选不对,轻则响应慢,重则设备死机。

我遇到过最夸张的一次,某智能水表项目,用JSON传数据,每次上报要传2KB。设备每天上报100次,一个月下来流量费比设备本身还贵。后来换成MessagePack,数据量砍掉一半,问题直接解决。

所以,选序列化方案,本质上是在做三件事的平衡:

  • 数据体积:越小越好,省带宽、省存储
  • 解析速度:越快越好,省CPU、省时间
  • 可读性:越直观越好,方便调试、方便对接

4.2 四种方案逐个看

4.2.1 JSON——最通用的「万金油」

JSON大家太熟了。文本格式,人类可读,几乎所有语言都支持。我刚开始做物联网那会儿,项目里清一色全是JSON。

但它的缺点也很明显:

  • 体积大:字段名重复出现,比如{"temperature": 25.3, "humidity": 80.1},每次都要写一遍字段名
  • 解析慢:文本解析比二进制慢一个数量级
  • 无类型约束:数字和字符串混用,容易出bug

适用场景

  • 设备与云端之间的配置下发(频率低、数据量小)
  • 调试阶段,方便人工查看
  • 与第三方系统对接,对方只支持JSON

4.2.2 Protocol Buffers——Google出品,性能怪兽

Protobuf是二进制格式,需要先定义.proto文件,然后生成代码。我第一次用的时候觉得好麻烦,但用上就回不去了。

它的优势:

  • 体积极小:比JSON小3-10倍
  • 解析极快:二进制直接映射到内存结构
  • 强类型:字段类型、范围都在.proto里定义好,不会出现「字符串当数字用」的尴尬

但要注意:

  • 需要预定义schema,改起来麻烦
  • 调试时得用工具解码,不能直接看
  • 库体积较大,对MCU不太友好

我的经验:在网关设备上,我习惯用Protobuf做数据上报。因为网关算力相对充足,而且数据量大,省带宽就是省钱。曾经有个项目,用JSON时每月流量费8000块,换成Protobuf后降到2000块,老板当场给我加了鸡腿。

4.2.3 MessagePack——JSON的二进制兄弟

MessagePack很有意思,它本质上就是「二进制的JSON」。同样的数据结构,MessagePack比JSON小30%-50%。

它的特点:

  • 兼容JSON:可以直接把JSON转成MessagePack,不需要定义schema
  • 体积适中:比JSON小,但比Protobuf大
  • 解析速度不错:比JSON快,但不如Protobuf

我推荐在什么场景用呢?

  • 设备端资源有限,跑不动Protobuf的库
  • 数据结构经常变,不想维护.proto文件
  • 需要保留一定的可读性(可以用工具转回JSON看)

避坑指南:我曾经在一个传感器节点上用了MessagePack,结果发现它的库在ARM Cortex-M0上编译后占用了8KB的Flash。对于只有64KB Flash的设备来说,这有点奢侈。后来我换成了CBOR,才把空间省出来。

4.2.4 CBOR——专为物联网设计的轻量级方案

CBOR(Concise Binary Object Representation)是IETF标准(RFC 7049),专门为受限设备设计的。

它的亮点:

  • 极简实现:核心编解码器可以做到几百行C代码
  • 无需schema:自描述格式,和JSON一样灵活
  • 体积控制好:比MessagePack还小一点
  • 流式处理:支持增量解析,适合内存极小的设备

我一般在什么情况下选CBOR?

  • MCU类设备,Flash小于128KB
  • 需要低延迟、低功耗的场景
  • 数据格式不固定,需要灵活扩展

4.3 横向对比:一张表说清楚

特性 JSON Protocol Buffers MessagePack CBOR
格式类型 文本 二进制 二进制 二进制
数据体积 大(基准) 极小(30%以下) 较小(50%-70%) 小(40%-60%)
解析速度 极快 较快
可读性 极好 差(需工具) 一般(可转JSON) 一般(可转JSON)
是否需要schema 是(.proto文件)
库体积 较大 中等 极小
适用设备 所有 网关/服务器 中高端MCU 低端MCU

4.4 选型决策树

说了这么多,到底怎么选?我一般按这个思路来:

  1. 设备资源够不够?
    • Flash > 256KB,RAM > 64KB → 可以上Protobuf
    • Flash 64-256KB,RAM 16-64KB → 考虑MessagePack或CBOR
    • Flash < 64KB,RAM < 16KB → 只能用CBOR或自定义二进制
  2. 数据量多大?
    • 每次上报 < 100字节 → JSON都行,别纠结
    • 每次上报 100-1000字节 → MessagePack或CBOR
    • 每次上报 > 1000字节 → 必须上Protobuf
  3. 调试频率高不高?
    • 经常要抓包看数据 → 留一个JSON接口做调试用
    • 几乎不调试 → 放心用二进制格式

我的推荐组合

  • 设备端上报:Protobuf(网关)或CBOR(MCU)
  • 云端下发配置:JSON(方便人工修改)
  • 设备间通信:MessagePack(平衡体积和灵活性)

4.5 代码示例:同样的数据,四种格式

假设我们要传一组传感器数据:

{
  "device_id": "sensor_01",
  "temperature": 25.3,
  "humidity": 80.1,
  "battery": 3.7,
  "timestamp": 1700000000
}

JSON版本:约120字节(含字段名)
MessagePack版本:约55字节
CBOR版本:约50字节
Protobuf版本:约30字节(定义好.proto后)

你看,同样的数据,体积差了4倍。在每天上报百万次的场景下,这差距就是真金白银。

4.6 总结

序列化选型没有银弹。我个人的习惯是:

  • 别一上来就追求极致性能,先看看你的设备能不能跑得动
  • 留一个JSON接口做调试,关键时刻能救命
  • 如果团队里有人对Protobuf不熟,先用MessagePack过渡

嗯,最后说一句:不管你选哪种,一定要在项目初期就定下来。中途换序列化方案,那酸爽,谁换谁知道。