temporal工作流引擎要求workflow函数必须是纯函数,禁止time.now()等非确定性操作;需用workflow.sideeffect()封装一次性操作,activity负责i/o并须配置超时与重试策略。

Temporal 工作流引擎在 Go 中不是“加个 SDK 就能跑”的简单集成,它强制你重构业务逻辑的组织方式——不区分 Workflow 和 Activity,代码上线后必出 Non-deterministic error,工作流卡死在 Running 状态,重放失败,日志里只有一句模糊的 Workflow execution failed: non-deterministic error。
Workflow 函数里调用 time.Now() 或 rand.Intn() 会直接导致重放失败
Workflow 函数必须是纯函数:相同输入、相同历史事件序列,必须产生完全一致的执行路径。任何非确定性操作都会破坏这个前提。
-
time.Now()、rand.Intn()、os.Getenv()、http.Get()、数据库查询、文件读写 —— 全部禁止出现在 Workflow 函数体内 - 真正需要“当前时间”?由客户端启动时传入
startTime time.Time参数,Workflow 内只做计算,不获取 - 真要封装一次性非确定性值(比如生成唯一 ID)?必须用
workflow.SideEffect(),它会在首次执行时运行并持久化结果,重放时直接返回缓存值 - 所有延时操作必须用
workflow.Sleep(),不能用time.Sleep()
workflow.ExecuteActivity() 必须配 ActivityOptions,且不能复用 context
Activity 是唯一允许做 I/O 的地方,但它的容错能力完全依赖显式配置的超时与重试策略。没设超时?Worker 进程卡住、任务堆积、整个队列阻塞。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 每个
workflow.ExecuteActivity()调用前,必须新建workflow.ActivityOptions并通过workflow.WithActivityOptions()绑定到子 context - 关键参数不能省:
StartToCloseTimeout(从调度到完成总耗时)、ScheduleToStartTimeout(排队等待上限)、RetryPolicy(例如&temporal.RetryPolicy{MaximumAttempts: 3}) - 别把支付、发短信、扣库存塞进同一个 Activity 函数——它们响应时间、失败率、重试策略完全不同,混在一起会导致快接口被慢接口拖垮
- Activity 函数签名必须是
func(ctx context.Context, ...interface{}) (result interface{}, err error),第一个参数是context.Context,不是workflow.Context
Worker 注册顺序和函数名拼写错误,Activity 永远不会被执行
Workflow 启动成功、日志显示 Scheduled,但 Activity 卡在 Started 或根本不出 ActivityTaskStartedEvent —— 大概率是 Worker 没注册上对应函数。
- Worker 启动前,必须显式调用
w.RegisterWorkflow(YourWorkflowFunc)和w.RegisterActivity(YourActivityFunc) - 函数名必须完全一致:Workflow 函数名(如
OrderWorkflow)要和workflow.ExecuteActivity()第二个参数传入的函数名(如OrderWorkflow)严格匹配,大小写、下划线都不能错 - Worker 的 Task Queue 名(如
"order-queue")必须和workflow.ExecuteActivity()所用的 queue 名一致;客户端启动 Workflow 时指定的 Task Queue 也要对得上 - 本地调试建议用
temporaltest.NewServer()启内存服务,避免 Docker 网络或配置干扰;上线前务必用tctl workflow describe --workflow-id xxx查看历史事件流,确认ActivityTaskScheduledEvent和ActivityTaskStartedEvent是否成对出现
最易被忽略的一点:Workflow 函数内所有分支逻辑(if/else、switch、循环)都必须基于确定性输入或 Temporal 提供的确定性 API(如 workflow.Now()、workflow.GetInfo())驱动;一旦某个分支隐式依赖了未被捕获的环境状态(比如某次重放时恰好某个 map key 存在而另一次不存在),就会触发不可追踪的非确定性错误。










