Go mod 好菜系列 - 0x1C temporal 这锅工作流不是把 if else 搬到云上
详细聊 Temporal 在长流程编排、重试、补偿和状态持久化里的价值,为什么它和普通任务队列不是一个层级,以及什么场景值得上工作流。
后台任务再往后走一步,就不只是“把某件事异步做完”了,而是“把一整串步骤按规则推进下去”。比如下单、支付、库存、通知、退款、补偿,这里面每一步都可能失败、超时、重试、回滚。这个阶段再靠几张任务表和几坨状态机硬撑,代码会越来越像打补丁。Temporal 就是很多团队在这个节点会考虑的一锅大菜。
Temporal 在解决什么问题
它解决的是 长期运行、跨多个步骤、需要可靠状态推进的业务流程。和普通任务队列相比,它更关心的是:
- 流程当前走到哪了
- 某一步失败后怎么重试
- 超时了怎么办
- 需要补偿时怎么往回收
- 整个流程状态怎么持久化
它为什么不是“高级版异步任务”那么简单
因为任务队列大多只擅长描述“做一件事”,而 Temporal 在描述“这件事做完以后,接下来按什么规则继续”。一旦业务跨多个系统、跨多个时间点,这种差别就会非常明显。
为什么很多人一听就觉得它重
因为它确实不轻。它不是一个“顺手引进来试试”的小工具,而是会改变你组织复杂流程代码的方式。所以它的价值不在小活上,而在那些你已经被复杂流程折腾得很痛的项目里。
什么场景它特别有吸引力
- 订单与支付编排
- 跨系统审批流程
- 长时间等待外部回调
- 多步骤补偿流程
- 定时推进且需要恢复执行上下文的业务
这类场景有个共性:流程不是“一下子做完”,而是中间会有等待、失败、重试和恢复。
它最大的价值是什么
很多人会说是“代码写工作流”,但我觉得更关键的是 把复杂流程的持久化与恢复语义托管出去。否则你自己实现时,很容易变成:
- 流程状态表一堆
- 补偿逻辑分散
- 恢复执行靠人工猜
- 线上问题一出没人敢动
workflow 和 activity 的参考写法
func OrderWorkflow(ctx workflow.Context, orderID string) error {
ao := workflow.ActivityOptions{StartToCloseTimeout: time.Minute}
ctx = workflow.WithActivityOptions(ctx, ao)
if err := workflow.ExecuteActivity(ctx, ReserveStock, orderID).Get(ctx, nil); err != nil {
return err
}
if err := workflow.ExecuteActivity(ctx, CreatePayment, orderID).Get(ctx, nil); err != nil {
_ = workflow.ExecuteActivity(ctx, ReleaseStock, orderID).Get(ctx, nil)
return err
}
return nil
}
func ReserveStock(ctx context.Context, orderID string) error {
return nil
}这段代码很适合用来理解 Temporal 的重心:不是某个 SDK 调用多优雅,而是流程推进、失败回收和状态恢复都被放进了同一种模型里。
什么时候没必要上
如果你的任务很短、步骤很少、失败处理很简单,那 Temporal 真的可能过重。不是所有异步流程都值得一个工作流引擎。技术的重量和问题的重量最好匹配起来。
小结
Temporal 这锅菜属于压轴款:
- 它适合复杂、长流程、可恢复的业务编排
- 和普通任务队列不在一个层级
- 最大的价值是托管流程推进、状态恢复和失败处理
- 简单异步别硬上,复杂流程里它会很有存在感