3. Bundle序列化与反序列化设计
3.1 边界与目标
序列化与反序列化的核心边界在于:将内存中的Bundle对象图转换为可存储或传输的字节流(序列化),以及从字节流反向构建出语义等价的对象图(反序列化)。本模块的目标是:
- 策略灵活:支持多种序列化格式,按场景切换(高性能/低体积/可读性)
- 版本透明:读写双方Bundle结构可不同步,向前/向后兼容
- 平台无关:字节序、内存对齐、基本类型表示在异构系统间正确转换
3.2 分层架构与组件职责
序列化层采用三层分解,下层屏蔽硬件差异,中层管理格式切换,上层处理业务语义。
| 层级 | 组件 | 职责 |
|---|---|---|
| 表示层 | Schema Registry | 管理Bundle结构ID、版本号、字段变更日志 |
| 格式层 | Codec 路由 | 根据配置选择序列化插件(Protobuf/FlatBuffers/JSON/CBOR) |
| 传输层 | Wire Format Adapter | 处理端序转换、对齐填充、类型映射,输出平台无关字节流 |
3.3 序列化策略选型
主流序列化方案对比(基于Bundle典型场景:session恢复、状态快照、跨进程通信):
| 策略 | 优点 | 缺点 | 适合场景 |
|---|---|---|---|
| Protobuf | 强Schema、向前/向后兼容、跨语言 | 元数据膨胀、反序列化性能中等 | 版本频繁变更、多语言互操作 |
| FlatBuffers | 零拷贝反序列化、内存友好 | Schema变更后需要重新编译 | 高频写入/低延迟恢复 |
| JSON | 人类可读、调试方便 | 体积大、类型不严格 | 开发期、单线程场景 |
| CBOR | 紧凑二进制、流式解析 | 工具链不如Protobuf成熟 | IoT/嵌入式Bundle存储 |
选型权衡:优先Protobuf作为默认策略,仅在延迟敏感路径启用FlatBuffers;JSON仅用于调试日志与测试用例。
3.4 版本兼容策略
Bundle结构演化遵循向后兼容(老版本也可读取新数据)和向前兼容(新版本可读取老数据)准则。
- 字段添加:新字段必须可选的(optional/repeated),带默认值
- 字段删除:保留字段编号(reserved),避免复用导致数据错乱
- 字段类型变更:仅允许扩容(如int32→int64),禁止缩小
- 结构嵌套:嵌套类型同样遵循上述规则,通过Schema版本号前缀标识
// Bundle 结构定义示例(Protobuf IDL)
message SessionBundle {
reserved 1, 2, 5; // 已删除的字段编号
optional string session_id = 3;
optional int64 last_active_ts = 4;
optional UserContext user = 6; // 新增字段,optional
repeated int32 flags = 7;
}
3.5 跨平台表示
异构平台(x86/ARM、大小端、不同对齐要求)下,序列化层需要主动转换:
- 端序转换:网络字节序(大端)作为交换格式,写入时CPU端序→大端,读取时大端→CPU端序
- 对齐填充:Protobuf/FB等自动处理;JSON/CBOR无对齐要求
- 浮点/整型:IEEE 754浮点统一,整型变长编码(Varint)减少大小端问题
- 字符串编码:全部使用UTF-8
使用固定字节序标记(magic number + 版本头)让反序列化器能自动检测并转换,避免硬编码假设。
3.6 数据流 / 控制流 / 信任链路
序列化/反序列化属于无信任边界操作:输入字节流可能来自不可信来源(网络恢复、外部存储),必须做校验和长度检查。
数据流:
- 调用方提供Bundle对象 → 表示层获取当前Schema版本 → 格式层Codec编码
- 传输层将中间表示转为字节流(大小端/对齐/填充)
- 反方向:字节流 → 传输层校验 → 格式层Codec解码 → 表示层版本映射 → 目标Bundle对象
信任链路:序列化输出前需签名(HMAC)或校验和(CRC32C),反序列化入口先校验再解码,防止恶意构造触发内存越界。
3.7 选型权衡与风险点
| 风险项 | 影响 | 缓解措施 |
|---|---|---|
| Schema演化导致数据丢失 | 老版本无法识别新字段 | 启用未知字段保留(PreserveUnknown) |
| 跨平台对齐异常 | ARM读取x86序列化数据时Segfault | 强制使用对齐友好的格式(Protobuf/FB) |
| 序列化性能瓶颈 | 高频写场景拖慢主线程 | 异步序列化 + 对象池复用Buffer |
| 版本头冲突 | 不同分支使用相同ID导致解析错误 | 集中管理Schema Registry,每次提交自动分配ID |
3.8 落地步骤
- 定义Schema:编写Protobuf IDL,为每个Bundle类型分配唯一ID和初始版本号
- 搭建Schema Registry:支持版本查询、差异对比、自动校验规则
- 实现Codec插件:基于策略模式,实现Protobuf/FB/JSON/CBOR插件,注册到Codec路由
- 集成Wire Format Adapter:统一字节序、校验和、长度前缀
- 单元测试 & 模糊测试:测试随机结构演化、边界值、恶意输入
- 性能基准:对比不同策略的吞吐、延迟、内存占用,调整默认策略
3.9 验收标准
- √ 所有Bundle类型均可序列化为字节流并完整恢复,语义等价
- √ 向前/向后兼容测试:旧版reader可读取新版数据的字段子集,新版reader可读取旧版数据并填充默认值
- √ 跨平台测试:在x86 Linux、ARM macOS、小端/大端模拟器上互操作通过
- √ 反序列化校验:非法CRC、过短长度、未知字段ID被拒绝或安全忽略
- √ 性能指标:Protobuf序列化延迟 ≤ 5μs(100字段对象),FlatBuffers零拷贝路径延迟 ≤ 1μs