“这不可能有用”:我在Temporal.io工作坊中学到的东西
**当开发者们第一次听说 Temporal 时,往往会经历一系列可预见的情绪变化。首先是怀疑——声称你的应用能在执行中途崩溃,然后从断点处恢复,变量完好无损,这听起来简直荒谬。接着是恼怒,觉得有人居然敢说这种事情是可能的。然后他们尝试了,发现它真的能工作,于是变得非常兴奋。最后他们问出唯一重要的问题:如何做到?
[LOADING...]
这条情绪弧线贯穿了昨天在柏林举办的 We Are Developers World Congress Europe 上、人气爆棚的实践研讨会 “开发防崩溃Java应用” ,该研讨会由 首席开发者倡导者 Tom Wheeler 主持——他坦言自己也曾经历过完全相同的阶段。在加入该公司之前,他花了数天时间试图“破坏”这个平台,但没能成功。
问题:我们所有人都成了分布式系统开发者
二十五年以前,大多数应用都在单台机器上运行。函数调用要么成功,要么失败,生活很简单。然后互联网出现了。应用需要跨机器水平扩展、与云服务和第三方API集成,并拆分为微服务。原本只是本地函数调用的组件变成了网络跳转——而每一跳都是一次潜在的失败机会。
研讨会的经典案例聚焦于在两个银行之间转账。代码看起来很简单:从账户A取款,存入账户B。四五行代码。但如果在两个步骤之间进程崩溃会发生什么?取款发生了,存款却没有。钱就这么消失了。天真地重启程序,它又会从头开始——再取一次款。每个变量、每一点已经完成的工作记录,都随着进程消失了。
传统上,你会用检查点代码、消息队列、cron作业、幂等键和大量的错误处理逻辑来应对这种情况。所有这些基础设施的存在,都源于我们从第一个“Hello World”起就内化了一个假设:进程会死,因此执行时间要短,永远不要相信内存状态。
持久执行:防崩溃,而非防 Crash
Temporal 的答案是持久执行。研讨会上用的比喻是防水手表:它不会阻止你掉进湖里,但如果你真的掉进去了,手表依然能工作。持久执行不防止崩溃——它让崩溃变得无关紧要。
其机制是一个横跨多个物理进程的逻辑执行。你的程序在一个进程中启动;如果该进程死亡,执行会在另一个进程——可能完全不同的机器上——恢复。设定 x = 5,崩溃服务器,当执行在其他地方恢复时,x 仍然是 5。无需数据库写入,无需手动持久化状态。你的笔记本着火了,工作流也会在别处继续运行。
实际工作原理:历史重放,而非快照
几乎每个人首先都会猜测 Temporal 一定是在对进程内存做快照。但事实并非如此——每秒将数 GB 的 RAM 写入磁盘会慢得难以想象,而且重启后内存地址也无法保留。
相反,Temporal 使用一种叫做历史重放的技术。它只记录足够在需要时重建应用状态的信息。这之所以可行,是因为严格的架构拆分:
-
工作流是确定性编排层——业务逻辑中的步骤序列。对购物车进行总计是确定性的:相同输入、相同输出,无论在哪台机器、哪一天运行。工作流代码获得完整的持久执行能力,但作为交换,它必须在每次运行时行为完全一致。
-
活动是所有非确定性、易失败工作所在的地方:调用 API、查询数据库、发送电子邮件、调用 LLM。活动没有持久状态,但可以获得带可配置超时的自动重试。服务宕机?Temporal 会一直重试,直到成功或你取消任务。
-
工作者负责执行重放。每当工作流调用一个活动时,Temporal 都会将该调用、其参数及其结果记录在由 Temporal Service 维护的事件历史中。如果发生崩溃,工作者会将该历史回放在你的确定性工作流代码上,重建出精确的状态——通常只需不到一秒。没有丢失任何东西,关键是,已经完成的工作不会被重复执行。不会重复取款。
组成部分
该架构有三个主要组件。你的工作流和活动定义只是普通代码——在研讨会中是 Java,尽管 Temporal 也为 Go、TypeScript、Python、PHP、.NET 等提供了 SDK,并且你可以在一个系统中自由混用语言。工作者由 SDK 提供,运行在你自己的基础设施上,轮询任务队列并执行你的代码。
Temporal Service 独立运行,管理任务队列和事件历史——它从不执行你的代码,也从不主动发起连接,这使得防火墙规则保持简单。
眼见为实
Temporal 的 Web UI 被特别指出“光凭这一点就值回票价”。每个工作流执行——无论是正在运行还是早已结束——都可检查:输入、输出、时间线、每个活动的详细情况,甚至可以精确到在哪台机器、哪个进程上执行了每一步。
[LOADING...]
在实时演示中,Tom 在订单处理中途杀死了库存服务。UI 显示“保留库存”活动失败并重试——第六次、第七次、第八次——而工作流的其余部分只是等待。
将服务恢复上线,订单就像什么都没发生过一样完成。没有警报,没有人工干预,没有凌晨三点的呼叫。
为什么这会改变你编写软件的方式
研讨会的更深层次要点不在于可靠性——而在于简洁性。如果你的程序能在任何崩溃后存活,既不丢失状态也不重复工作,那么它实际上可以永远运行下去。而如果它能永远运行,那么整个类别的基础设施就变得多余了。需要在30天后做某事?你不需要 cron 作业;你只需要在代码里 sleep 30 天。需要跨服务协调?你可能不需要那条消息队列。
Tom 解释说,人们是因为可靠性来到 Temporal,但最终是因为生产力而留下。代码不再被重试循环、检查点逻辑和故障处理所充斥。剩下的只有快乐路径——你的程序实际上想要做的事情——这意味着初级团队成员也能阅读、维护它,从而把你解放出来去做下一件事。
你可能仍然觉得这像魔法。但正如柏林满屋子第一次接触 Temporal 的用户所发现的那样,它真的能工作。