为速度而设计:无模式事件接入的理由

SchemaBridge Team · 2025-12-08 · Event Ingestion, JSON, DX

在事件驱动系统中,速度与严格类型的权衡。为什么我们选择原生 JSON 而非严格类型定义。

模式悖论:朋友还是敌人?

在传统企业软件中,严格的模式(SQL、Protobuf、WSDL)是可靠性的基石。契约很简单:我定义数据的形状,你遵守它,编译器保证我们不会崩溃。在我们掌控管道两端的受控环境中,这种方法几十年来一直行之有效。它提供了编译期安全性、高效的二进制序列化,以及一个清晰的、供开发者依赖的“事实来源”。

但在一个现代的、高速集成的环境中——SaaS 供应商每周都在改变他们的负载格式,遗留 ERP 系统发出“灵活”的 CSV,内部微服务每月都在新生与退役——僵化的模式反而变成了一件紧身衣。它们以惯性为代价换取安全性。当你周围的世界处于流动状态时,僵化的契约不是基石,而是一个故障点。模式悖论正在于此:你越是试图用严格类型来保护你的系统,当外部世界发生变化时,你的系统就会变得越脆弱。

历史演变:从 COBOL 到 JSON

要理解对无模式系统的需求,我们必须回顾数据交换的演变历程。在大型机计算的早期,数据以定长记录(COBOL COPYBOOK)的形式存储。如果你想添加一个字段,就必须重新编译每一个读取该记录的程序。这是终极的僵化模式。这是“单机思维”的时代,数据存储的成本高昂到每一个字节都必须被安排在固定的位置。没有层级结构的空间,没有可选性的空间,当然也没有演化的空间。记录中的每一个字符都是宝贵的资源,任何变更都是一场需要数周规划和测试的地震级事件。

在 20 世纪 80、90 年代,我们转向了关系型数据库和 SQL。这是巨大的进步,因为它引入了结构化关系的概念。但它同时也带来了数据库迁移。添加一列意味着停机、锁表,以及 DBA 与开发者之间的精心协调。模式仍然是一堵墙,开发者每次想要创新都必须翻越它。即使随着 Hibernate 等 ORM 的兴起,表结构底层的僵化性仍然是决定可能性的最终仲裁者。

接着是 90 年代末出现的 XMLSOAP。这引入了“标签”的概念,带来了一定的灵活性。你可以添加一个 XML 标签而不必然破坏解析器。然而,行业很快又加入了 XSD(XML 模式定义)WSDL,重新带回了僵化性。我们花费数年时间与命名空间以及复杂的企业服务总线作斗争,只要有一个字符位置不对,消息就会被拒绝。这就是“XML 的黑暗时代”,模式本身的开销甚至超过了实际数据。

如今,我们有了 JSONREST。JSON 天生就是灵活的。它只是一个键值映射。然而,我们的工程本能仍然驱使我们用严格类型(TypeScript 接口、Java DTO、Avro 模式)去包裹这种灵活性。我们在试图把 20 世纪 70 年代大型机的僵化性强加到 2020 年代的云事件上。为什么?因为我们害怕未知。我们害怕一个缺失的字段会导致我们的服务崩溃。但正如我们将看到的,这种恐惧正被用错误的工具来应对。

财务分析:集成维护的“隐性税”

让我们量化一下模式僵化的成本。在一个典型的中大型工程组织中,集成维护是一场“静默危机”。它不会作为一个明细项出现在资产负债表上,但它对生产力造成了巨大的拖累。

维护的数学

假设一个组织拥有 100 个外部 SaaS 集成。

按薪资计算,这每年要花费超过 15 万美元,仅仅用于“修补管道”。但真正的代价是机会成本。当你的资深工程师正在为 Stripe 的 v2025 更新更新 DTO 时,他们没有在构建能为公司节省数百万美元的新的自动化欺诈检测功能。经过 5 年的积累,这项税负会导致速度的累积性损失,让一家公司在竞争中落后于那些更加敏捷的对手数年之久。

数据流动性的哲学:读时模式(Schema-on-Read)

在 SchemaBridge,我们提倡一种根本性的思维转变:读时模式

我们不是在数据进入的那一刻就进行验证(写时模式),而是首先接入事件原始的、层级化的真相。我们在持久化存储中保留 JSON 负载的每一个字节。只有当特定业务流程真正需要这些数据时,我们才会应用一个模式——或者更准确地说,一次转换

为什么数据流动性能取胜

1. 零接触接入:你可以在几秒钟内开始接收来自新提供商的事件。将 webhook 指向一个 SchemaBridge 网关,数据就会立即开始流入持久化存储。你可以稍后再弄清楚这些数据的含义。

2. 应对未知的保险:如果某个提供商今天新增了一个你暂时不需要的字段,它仍然会被捕获在原始 JSON 中。如果你在六个月后意识到确实需要这个字段,历史数据早已就绪。你不必回头再向提供商索要旧数据。

3. 解耦的演化:你的接入层和你的转换层可以以不同的速度演化。你可以一天更新 10 次业务逻辑,而完全不必触碰你的接入网关。

延迟绑定的数学论证

在计算机科学中,延迟绑定(Late-Binding)是指将身份或类型的解析推迟到执行时刻的做法。这正是 Ruby 或 Python 这类动态语言在某些任务上如此强大的原因。

读时模式就是将延迟绑定应用于你的数据基础设施。通过推迟映射,你从一个僵化的图(每次变更都需要完全重建)转向了一条灵活的路径(路径可以在移动过程中随地形而适应)。

从数学上讲,$N$ 个生产者与 $M$ 个消费者之间潜在映射的数量是 $N \times M$。如果每一个生产者和消费者都必须就一个严格的模式达成一致,你就面临一个巨大的协调问题。如果你使用一个具备延迟绑定的无模式桥梁,你就能将这个问题简化为 $N + M$ 个映射,其中每个映射都是局部且独立的。这就是实现真正横向工程规模的方式。

JSONata:函数式事件处理大师课

要让读时模式切实可行,你需要一门为“发现”而设计的语言。我们选择了 JSONata。JSONata 不仅仅是一门查询语言;它是一个直接作用于原始 JSON 层级结构之上的函数式转换引擎。

JSONata 表达式的解剖

设想一个来自遗留 ERP 系统的负载,其中返回一份订单列表。每个订单都有一个复杂的嵌套结构。你想提取所有金额超过 500 美元、且当前处于“SHIPPING”状态的订单中的所有零件编号。

传统代码(Javascript)

const parts = payload.orders
  .filter(o => o.total > 500 && o.status === 'SHIPPING')
  .flatMap(o => o.items)
  .map(i => i.partNumber);

这段代码很脆弱。如果 orders 为 null,或者某个订单缺少 items,它就会崩溃。

JSONata 的精妙之处

orders[total > 500][status = 'SHIPPING'].items.partNumber

这个表达式是空值安全的。如果 orders 缺失,结果就只是一个空数组。它从不抛出异常。它是在“发现”数据,而不是在“断言”数据。

高级模式:深度后代选择

JSONata 最强大的特性之一是 ** 操作符。它可以让你找到任意一个键,无论它位于层级结构中的哪个位置。

$**.tracking_number

如果你下游的所有提供商对物流单号使用了不同的嵌套方式,这一个表达式就能在每一种不同的负载版本中把它们全部找到。这就是结构韧性的定义。它把一次针对特定键的脆弱搜寻,变成了一次灵活的真相探寻。

高级模式:动态数据重构

JSONata 允许你在一次遍历中重建整个 JSON 对象。

orders.{ "order_id": ID, "summary": $join(items.name, ', ') }

在传统代码中,这需要对象映射、字符串拼接和数组遍历。在 JSONata 中,这是对你期望状态的一次声明式投影。当你需要向 Slack 通知或移动应用发送一个复杂事件的简化摘要时,这一点尤其强大。

让无模式数据可运维化:安全性与验证

批评者常常会问:“如果我们不使用模式,如何防止垃圾数据进入我们的系统?”

答案是:无模式不等于无验证。我们只是把验证转移到了顶点层面

案例研究:覆盖 50 个地区的数据接入网格

我们最近曾与一家全球物联网公司合作,他们正在接入来自 50 个不同地区的遥测数据,每个地区使用的传感器固件版本都略有不同。每个地区都有自己的 JSON“方言”。

挑战

他们曾尝试使用一套带有严格表模式的传统 SQL 接入系统。每当某个国家推出固件更新时,该国的整条接入管道就会崩溃,因为新固件添加了一个 battery_health_v2 字段,而数据库中没有对应的列。数据团队一直处于“救火”模式,在 50 个生产数据库上不断运行 ALTER TABLE 命令。他们在这些维护窗口期间损失了数以百万计的事件。

SchemaBridge 解决方案

他们转向了无模式策略。

1. 统一捕获:全部 50 个地区都将数据指向一个统一的 SchemaBridge 网关集群。网关不关心模式;它们只是持久化原始事件。

2. 读时映射:他们创建了 50 个不同的“归一化顶点”(每个固件版本一个)。工作流会从头部信息中识别固件版本,并将原始 JSON 路由到正确的顶点。

3. 无代码升级:当新固件版本发布时,他们无需更新数据库。他们只需复制现有顶点,更新 JSONata 映射以包含新字段,然后部署即可。

结果

结论:速度是一种设计选择

严格模式是一种选择。它是一种优先考虑静态安全性而非动态速度的选择。在一个封闭系统中,这是一个合理的选择。在一个互联的分布式生态系统中,这是一个通往失败的选择。

按照世界本来的样子——不可预测、不断演化、层级化——去设计你的系统。为速度而设计。与 SchemaBridge 一起设计。通过拥抱数据的流动性,你将解锁以一种速度构建、扩展和创新的能力,而那些困在僵化 DTO 和数据库迁移中的竞争对手,只能望尘莫及。

在第三部分,我们将从接入转向执行,探索“Spawner”顶点,以及如何在 10,000 个以上条目的规模下精通分布式扇出。加入我们,一起看看如何在不让服务器崩溃、不丢失任何一笔事务的情况下处理数百万个事件。在分布式语境下重新夺回循环的力量。

深入了解