Skip to content

Restate 架构拆解

Restate 的官方架构可以概括为:客户端请求先进入 Ingress,再按 key 路由到分区;分区处理器把所有动作先写入 Durable Log,然后再物化到本地状态并驱动服务执行。服务本身保持普通业务代码形态,可靠性由 Restate Server 和 SDK 协作完成。

总览

这里最重要的不是组件名,而是写路径:

业务动作先成为日志里的事实,再被处理器读取、物化、确认给 handler。

组件职责

组件职责教学版对应物
Ingress接收 HTTP/Kafka/内部调用,解析目标服务与 keyFastAPI /api/orders
Keyed Routing根据 workflow id、object key、idempotency key 选择分区教学版单分区,保留 idempotency key
Durable Log复制并排序所有 invocation、step、timer、state 事件MySQL journal_entries
Partition Processor读取日志、驱动 handler、维护物化状态后台 worker_loop()
Partition Store快速读取 Journal、状态、计时器索引MySQL 查询 + 简化状态表
Control Plane节点、分区、epoch、segment 元数据教学版不实现,只解释
Service SDK给业务代码提供 ctx.runctx.sleep 等动作DurableContext

Invocation 生命周期

如果服务在 ctx.run("charge") 后崩溃,重试时处理器会把已有 Journal 交给新的 attempt。业务代码重新进入同一个函数,但 charge 的结果由 Journal 返回。

为什么要分区

生产系统不能把所有 invocation 放到一个全局锁后面。Restate 使用 key 做分区:

分区带来两个关键收益:

  1. 同一个 key 的状态和编排在同一分区内完成,热路径不需要跨节点事务。
  2. 不同 key 可以并发扩展,吞吐量随分区和 worker 扩展。

Journal 与物化状态

Restate 的文档强调 replicated log 是事实来源,RocksDB 里的 partition store 是可重建的物化视图。这个区分非常关键:

数据性质
Durable Log事实来源,决定动作顺序和提交点
Partition Store为了快速读取 Journal、timer、state 的缓存/物化视图
Snapshot限制恢复时需要重放的日志长度

教学版使用 MySQL 做两件事:既存 Journal,也查询 Journal。这简化了实现,但也模糊了“事实日志”和“物化视图”的边界。课程中会反复提醒这一点。

失败处理的关键:epoch fencing

分布式恢复有一个危险场景:旧 leader 以为自己还活着,新 leader 已经接管。Restate 用 leader epoch 给 attempt 和处理器加边界,低 epoch 的迟到事件会被拒绝。这就是 fencing。

教学版不实现 epoch,但会保留一个工程提示:

教学版限制

如果你把 examples/durable-mini 部署成多个 worker 并发处理同一个 invocation,当前代码不能保证 exactly-once。生产化必须引入锁、租约、epoch 或数据库条件更新。

与传统方案的区别

方案优点局限
Cron + 状态表简单,适合定时扫描业务步骤仍需自己处理幂等和恢复
队列 + consumer retry解耦吞吐好retry 会重新进入 handler,步骤级恢复要自写
Workflow 引擎编排能力强有些系统要求 DSL 或活动函数拆分较重
Durable Execution runtime保留普通代码形态,步骤级恢复运行时复杂,对日志和协议要求高

小结

Restate 的架构可以用三句话记住:

  1. Ingress 负责把调用变成可路由的 invocation。
  2. Partition Processor 负责把执行过程变成 log-first 的事实流。
  3. SDK Context 让普通业务代码以 run/sleep/call 的方式参与持久化协议。

Teaching project inspired by Restate's public architecture and documentation.