Temporal 持久化工作流生产部署与故障恢复
Temporal 不是普通的定时任务平台,也不是把一串 HTTP 请求连起来的低代码编排器。它通过持久化工作流事件历史,让业务流程在进程重启、节点故障、网络超时甚至跨越数月之后仍能从确定的位置继续执行。订单履约、审批、支付补偿、数据处理、设备开通和 AI Agent 长任务都适合这一模型,但生产落地需要同时治理服务端、Worker、业务代码兼容性和事件历史增长。
一、先理解执行模型
Temporal 应用主要由 Workflow、Activity、Worker 和 Task Queue 构成。
- Workflow 描述业务状态机,必须保持确定性;
- Activity 执行数据库、HTTP、文件和第三方系统等有副作用的操作;
- Worker 轮询 Task Queue 并执行 Workflow Task 或 Activity Task;
- Temporal Service 保存事件历史、调度任务、处理超时和重试;
- Client 负责启动、查询、发信号和取消工作流。
Workflow 代码不是从某一行暂停后保存内存,而是根据事件历史重新播放并恢复状态。因此,Workflow 中不能直接读取当前时间、随机数、环境变量或执行网络请求,必须使用 SDK 提供的确定性 API,外部操作则放入 Activity。
业务系统 → Temporal Frontend → History / Matching / Worker
│
├─ 持久化数据库
└─ Visibility 检索存储
二、适用与不适用场景
适合:
- 流程持续时间长,可能跨越分钟、天或月;
- 需要可靠重试、超时、补偿和人工信号;
- 服务重启后不能丢失执行进度;
- 需要查询流程状态与完整审计轨迹;
- 流程由多个微服务共同完成。
不适合把所有短小同步请求都包装为 Workflow。高频、毫秒级、无状态计算更适合普通服务调用;大文件内容也不应塞入 Payload,应保存对象存储引用。Temporal 保证工作流编排可靠,不会自动让非幂等的外部系统变得幂等。
三、生产服务端架构
自建集群通常包含 Frontend、History、Matching 和 Worker Service。小规模可以合并部署,生产环境应按请求类型独立扩缩容,并分散在多个故障域。
1. 持久化数据库
数据库保存 Namespace、任务、事件历史和执行状态,是恢复能力的核心。选择官方支持的数据库类型和版本,采用高可用实例、独立账号、TLS、连接池监控、自动备份和时间点恢复。不要只备份 Temporal Pod 或虚拟机磁盘。
数据库容量应估算:
日增量 ≈ 日工作流数 × 平均事件数 × 平均单事件大小
保留容量 ≈ 日增量 × 历史保留天数 × 安全系数
事件数量比 Workflow 数量更能反映负载。大量短间隔 Timer、频繁 Signal、过细 Activity 和无限重试都会快速扩大历史。
2. Visibility
Visibility 用于按 Workflow Type、状态、时间和 Search Attribute 检索执行记录。它不应被当作业务报表数据库。高基数 Search Attribute 会增加索引成本,应只保存真正用于检索的维度,敏感信息不要写入可见字段。
3. Namespace
按环境、业务域或租户划分 Namespace,并明确历史保留期、访问权限、故障域和配额。生产与测试不能只用名称区分而共用无限权限账号。跨集群复制、全局 Namespace 等能力需要单独设计和演练,不能在故障时临时开启。
四、Worker 的生产部署
Worker 是业务代码真正运行的位置。共享且持续使用的 Task Queue 至少部署两个 Worker 实例,并分散到不同节点或可用区。容量不能只看 CPU,还要看:
- Schedule-to-Start 延迟;
- Activity 和 Workflow Task 执行时长;
- Worker Slot 使用率;
- Poller 数量;
- Sticky Cache 命中与回放成本;
- 下游连接池与限流;
- Task Queue 积压。
Task Queue 应按业务和资源特征拆分。CPU 密集、IO 密集、需要专用网络、需要 GPU 或访问本地文件的 Activity 不应全部混在同一个队列。
order-workflow 工作流决策
payment-activity 支付调用
warehouse-activity 仓储与物流
manual-review 人工复核
Worker 应作为不可变制品由 CI/CD 发布,连接地址、Namespace 和凭据通过运行时注入。滚动发布不要一次停止全部 Worker,否则 Sticky Cache 同时失效会引发集中回放。
五、超时、重试和幂等
1. Activity 超时
- Schedule-to-Close:从调度到最终完成的总时限;
- Start-to-Close:一次尝试的执行时限;
- Schedule-to-Start:任务等待 Worker 的时限;
- Heartbeat:长 Activity 报告进度和取消感知。
超时必须来自业务边界,不能统一设置为一个很大的值。Schedule-to-Start 超时通常不应被滥用为容量告警替代品。
2. 重试策略
区分临时错误和永久错误。网络超时、限流和短暂不可用可以指数退避;参数错误、权限拒绝、余额不足等业务失败应明确为不可重试或转人工处理。无限快速重试会放大下游故障。
3. 幂等键
Activity 可能在“外部系统已经成功、完成事件尚未写回”时被再次执行。调用支付、创建工单、发货等接口必须携带稳定幂等键,例如 Workflow ID 加业务步骤名。
idempotency_key = orderId + ":capture-payment:v1"
Temporal 的“至少一次 Activity 执行”语义要求业务端真正实现去重,不能只靠日志判断。
六、工作流代码演进
正在运行的 Workflow 可能使用数月前的事件历史。直接改变分支、循环、Activity 顺序或计时逻辑可能导致非确定性错误。
推荐优先使用 Worker Versioning,把新 Workflow 固定到新版本 Worker,旧执行继续由旧版本处理。无法使用版本管理时,使用 SDK 的 Patch/Version 机制兼容历史。上线前必须用真实历史做 Replay 测试。
升级流程:
- 导出具有代表性的事件历史;
- 在 CI 中用新代码执行 Replay;
- 发布新 Worker 版本但不立即全量路由;
- 启动少量新执行验证;
- 逐步提升流量;
- 等旧执行完成后再下线旧 Worker;
- 保留可操作的回滚窗口。
七、长历史与 Continue-As-New
持续接收 Signal、频繁运行 Timer 或永久循环的 Workflow 会积累巨大事件历史,导致回放变慢和存储增长。应使用 Continue-As-New 生成同一业务流程的新 Run,并把必要状态传给下一次执行。
不要等历史接近平台限制才处理。根据事件数、历史大小或业务周期主动续接,并确保 Signal 在切换窗口不会丢失。大对象只传引用,避免每次事件都复制完整 Payload。
八、安全与数据保护
- 服务端和 Worker 之间启用 TLS/mTLS;
- Namespace 使用最小权限;
- 凭据放入密钥系统并定期轮换;
- Payload 中的个人信息和密钥进行客户端加密或根本不写入;
- Web UI 放在受控运维网并接入统一身份认证;
- 审计启动、终止、重置、批量操作和权限变更;
- 限制批量查询和重放工具权限。
事件历史具有长期保存特征,日志脱敏并不能移除已经写入历史的敏感 Payload。数据分类应在开发阶段完成。
九、监控与告警
服务端重点关注:请求错误率和延迟、Persistence 延迟、History Shard、Task Queue、数据库连接、服务间 RPC、Namespace 配额和节点资源。
Worker 重点关注:
- Schedule-to-Start 延迟持续升高;
- Task Queue Backlog 增长;
- Activity 失败、超时与重试率;
- Workflow Task 失败和非确定性错误;
- Worker Slot 长期饱和;
- Poller 数量异常下降;
- 长时间没有进展的执行。
业务告警应围绕“订单多久未完成”“审批在哪个状态停留”,而不是只看 Temporal Pod 是否存活。
十、备份、恢复与灾难演练
备份对象包括数据库、可见性存储、动态配置、Namespace 配置、TLS 证书、授权策略、部署清单和 Worker 制品。数据库备份必须验证一致性和可恢复性。
恢复演练建议:
- 在隔离环境恢复数据库;
- 使用兼容版本启动 Temporal Service;
- 验证 Namespace 和执行历史;
- 以只读方式检查关键 Workflow;
- 部署对应版本 Worker;
- 观察 Replay、Task Queue 和下游幂等;
- 验证 Signal、Query、取消和新建执行;
- 记录 RPO、RTO 与人工步骤。
不能只恢复最新 Worker。若旧执行仍绑定旧版本,必须保留对应制品和依赖。
十一、常见故障排查
Workflow 长时间 Running
查看 Pending Activity、Timer、Task Queue、Schedule-to-Start、最近事件和 Worker Poller。区分任务没被领取、Activity 卡住、等待外部 Signal,还是代码进入了正常长等待。
Activity 重复执行
确认是否发生超时、Worker 崩溃或完成事件写回失败。修复幂等,而不是简单关闭重试。
新版本出现非确定性错误
立即停止新版本接管旧历史,恢复兼容 Worker,用 Replay 找出代码路径差异,再通过版本管理或 Patch 修复。
Task Queue 积压
检查 Worker 数量、Slot、下游限流、长 Activity、分区不合理和发布期间 Sticky Cache 重建。扩容前确认瓶颈不是数据库或下游系统。
数据库延迟升高
同时检查慢查询、连接池、磁盘、锁、历史增长和批量操作。Temporal 服务扩容无法解决数据库写入瓶颈。
十二、生产检查清单
- [ ] Workflow 与 Activity 边界清晰;
- [ ] 所有外部副作用具备幂等键;
- [ ] 超时和重试按业务分类设置;
- [ ] Worker 至少跨两个故障域;
- [ ] Task Queue 按资源特征拆分;
- [ ] 代码升级经过历史 Replay;
- [ ] 长工作流使用 Continue-As-New;
- [ ] Payload 不包含不受控敏感信息;
- [ ] 数据库和 Visibility 有备份与监控;
- [ ] 旧 Worker 制品可恢复;
- [ ] 已演练数据库恢复和 Worker 重建;
- [ ] 告警能够落到具体 Namespace、Queue 和 Workflow Type。
参考资料
- Temporal 生产部署:https://docs.temporal.io/production-deployment
- Temporal Worker 最佳实践:https://docs.temporal.io/best-practices/worker
- Temporal 服务端配置:https://docs.temporal.io/references/service-configuration
- Temporal 自建部署:https://docs.temporal.io/self-hosted-guide