关于FlatBuffers的理解

63 阅读3分钟

用现实生活中的一个比喻来解释 FlatBuffers 序列化的工作流程:

🏗️ 比喻:建造装配式房屋(预制房屋)

想象你是一家装配式房屋工厂的建造总监(Builder),客户(调用代码)需要一栋完整的预制房屋(最终二进制数据)。房屋由多个预制模块组成:


📦 1. 准备原材料(初始化 Builder)

const builder = new Builder();
  • 你租用了一个大型装配场地(内存缓冲区)
  • 场地初始大小是 1000㎡(默认缓冲区大小)
  • 场地可以随时扩建(缓冲区自动扩容)

🧩 2. 制造基础部件(创建底层对象)

客户订单:一栋有2个房间的房屋

  • 房间1:需要3面墙 + 1扇门
  • 房间2:需要4面墙 + 2扇窗
// 生产墙板
const wall1 = createWall(builder, 3, 4); // 尺寸3x4
const wall2 = createWall(builder, 3, 4);
// ...生产更多墙板

// 生产门窗
const door1 = createDoor(builder, 2, 1); // 尺寸2x1
const window1 = createWindow(builder, 1, 1);
  • 每块墙板/门窗生产后,你记录它们在场地中的位置编号(偏移量)
  • 这些部件散落在场地各处

🚚 3. 组装房间模块(创建嵌套对象)

// 组装房间1
const room1Walls = createWallCollection(builder, [wall1, wall2, wall3]);
const room1 = createRoom(builder, room1Walls, door1, null); // 房间1没有窗户

// 组装房间2
const room2Walls = createWallCollection(builder, [wall4, wall5, wall6, wall7]);
const room2 = createRoom(builder, room2Walls, null, [window1, window2]); 
  • 你让工人把指定墙板(通过位置编号)运到指定区域组装
  • 组装好的房间获得新的位置编号
  • 注意:房间2在场地中的位置比房间1更靠近入口(逆序构建)

🏠 4. 组装完整房屋(创建顶层对象)

const house = createHouse(builder, [room1, room2], roofType);
  • 用吊车把两个房间模块(通过位置编号)拼装在一起
  • 加上屋顶(路径信息)
  • 完整房屋获得最终位置编号

📦 5. 打包发货(生成二进制)

builder.finish(house); // 标记房屋完成
return builder.asUint8Array(); // 打包运输
  • 你只在卡车上装载从入口到房屋位置的实际使用空间
  • 废弃的边角料(未使用缓冲区)不装车
  • 最终得到一个紧凑的运输集装箱(Uint8Array)

🚛 客户收货使用(反序列化)

  • 客户收到集装箱后:

    • 直接走到入口处(根偏移量)
    • 看到指示牌:"主卧室在+20m处,客厅在+15m处"
    • 无需拆箱就能查看客厅窗户(直接内存访问)
    • 需要时快速添加扩展阳台(增量更新)

🔑 关键特点理解:

FlatBuffers 概念房屋建造类比优势
Builder建造总监+装配场地集中控制整个建造过程
偏移量(Offset)部件位置坐标通过坐标引用部件,无需移动实物
逆序构建先造内部部件,最后组装外壳避免反复拆装,效率高
向量(Vector)部件集合清单通过清单管理多个同类部件
零拷贝客户直接进入集装箱查看省去拆箱/重组时间
二进制紧凑只运输必要部件,紧密堆放节省90%运输空间

⚡ 为什么比传统JSON高效?

传统JSON方式相当于:

  1. 把整个房屋拆成零件清单(JSON字符串)
  2. 运输清单(文本传输)
  3. 客户收到后按清单重新购买材料再组装(解析+创建对象)
    → 耗时耗力!

而FlatBuffers:

  1. 在工厂完成组装(序列化)
  2. 运输整栋预制房屋(二进制)
  3. 客户直接入住(直接访问)
    → 省时省力!

这个工作流程特别适合快速加载的数据,FlatBuffers能瞬间获取复杂路径数据(预制),而不用"现场盖房子"(解析JSON)。