5.3 KiB
5.3 KiB
name, version, license
| name | version | license |
|---|---|---|
| workflow-orchestration-patterns | 1.0.0 | MIT |
工作流编排模式 —— 详细模式与实操示例
关键设计决策:工作流 vs 活动
基本原则(来源:temporal.io/blog/workflow-engine-principles):
- 工作流 = 编排逻辑与决策
- 活动 = 外部交互(API、数据库、网络调用)
工作流(编排)
特征:
- 包含业务逻辑与协调
- 必须具有确定性(相同输入 → 相同输出)
- 不能直接进行外部调用
- 状态在故障时自动保留
- 即使基础设施发生故障,也可运行数年
工作流示例任务:
- 决定执行哪些步骤
- 处理补偿逻辑
- 管理超时与重试
- 协调子工作流
活动(外部交互)
特征:
- 处理所有外部系统交互
- 可以是非确定性的(API 调用、数据库写入)
- 内置超时与重试逻辑
- 必须是幂等的(调用 N 次 = 调用一次)
- 生命周期短(通常数秒至数分钟)
活动示例任务:
- 调用支付网关 API
- 写入数据库
- 发送电子邮件或通知
- 查询外部服务
设计决策框架
是否涉及外部系统?→ 活动
是否为编排/决策逻辑?→ 工作流
核心工作流模式
1. Saga 模式与补偿
目的:实现分布式事务,具备回滚能力
模式(来源:temporal.io/blog/compensating-actions-part-of-a-complete-breakfast-with-sagas):
每个步骤:
1. 在执行之前先注册补偿
2. 执行该步骤(通过活动)
3. 失败时,按相反顺序(后进先出)运行所有补偿
示例:支付工作流
- 预留库存(补偿:释放库存)
- 扣款(补偿:退款)
- 履约订单(补偿:取消履约)
关键要求:
- 补偿操作必须是幂等的
- 在执行步骤之前先注册补偿
- 按相反顺序运行补偿
- 优雅处理部分失败
2. 实体工作流(Actor 模型)
目的:代表单个实体实例的长期运行工作流
模式(来源:docs.temporal.io/evaluate/use-cases-design-patterns):
- 一个工作流执行 = 一个实体(购物车、账户、库存项)
- 工作流在实体生命周期内持续存在
- 通过信号接收状态变更
- 支持通过查询获取当前状态
使用场景示例:
- 购物车(添加商品、结账、过期)
- 银行账户(存款、取款、余额查询)
- 产品库存(库存更新、预订)
优势:
- 封装实体行为
- 保证每个实体的一致性
- 天然的事件溯源
3. 扇出/扇入(并行执行)
目的:并行执行多个任务,汇总结果
模式:
- 启动子工作流或并行活动
- 等待所有任务完成
- 汇总结果
- 处理部分失败
扩缩规则(来源:temporal.io/blog/workflow-engine-principles):
- 不要扩缩单个工作流
- 对于 100 万个任务:启动 1K 个子工作流 × 每个处理 1K 个任务
- 使每个工作流的规模有界
4. 异步回调模式
目的:等待外部事件或人工审批
模式:
- 工作流发送请求并等待信号
- 外部系统异步处理
- 发送信号以恢复工作流
- 工作流继续处理响应
使用场景:
- 人工审批工作流
- Webhook 回调
- 长时间运行的外部进程
状态管理与确定性
自动状态保留
Temporal 的工作原理(来源:docs.temporal.io/workflows):
- 完整程序状态自动保留
- 事件历史记录每个命令和事件
- 从崩溃中无缝恢复
- 应用程序恢复到故障前状态
确定性约束
工作流作为状态机执行:
- 重放行为必须一致
- 相同输入 → 每次相同输出
工作流中禁止的操作(来源:docs.temporal.io/workflows):
- ❌ 线程、锁、同步原语
- ❌ 随机数生成(
random()) - ❌ 全局状态或静态变量
- ❌ 系统时间(
datetime.now()) - ❌ 直接文件 I/O 或网络调用
- ❌ 非确定性库
工作流中允许的操作:
- ✅
workflow.now()(确定性时间) - ✅
workflow.random()(确定性随机数) - ✅ 纯函数与计算
- ✅ 调用活动(非确定性操作)
版本化策略
挑战:在旧执行仍在运行时修改工作流代码
解决方案:
- 版本化 API:使用
workflow.get_version()进行安全变更 - 新的工作流类型:创建新工作流,将新执行路由到它
- 向后兼容:确保旧事件能正确重放
弹性与错误处理
重试策略
默认行为:Temporal 会无限重试活动
配置重试:
- 初始重试间隔
- 退避系数(指数退避)
- 最大间隔(重试延迟上限)
- 最大尝试次数(最终失败)
不可重试的错误:
- 无效输入(校验失败)
- 业务规则违反
- 永久性失败(资源未找到)
幂等性要求
为何至关重要(来源:docs.temporal.io/activities):
- 活动可能被执行多次
- 网络故障会触发重试
- 重复执行必须是安全的
实现策略:
- 幂等键(去重)
- 先检查再执行 + 唯一约束
- 使用更新插入操作替代插入
- 跟踪已处理的请求 ID
活动心跳
目的:检测停滞的长时间运行活动
模式:
- 活动定期发送心跳
- 包含进度信息
- 若未收到心跳则超时
- 支持基于进度的重试