根本问题是控制流与业务流耦合过紧,导致状态无法跨进程保持、重试边界模糊、失败定位困难、拓扑结构不可见、超时降级策略混杂、流程状态易丢失、日志缺乏统一追踪,且同步异步模式未合理分离。

为什么不能用 if-else 拼接微服务调用链
直接在 Go 代码里写 if err != nil 后续调用回滚、再套一层 switch status 分支,短期能跑通,但一加审批节点就错乱,一改超时逻辑就全崩。根本问题是:控制流和业务流耦合太紧,状态无法跨进程保持,重试边界模糊,失败后无法定位卡在哪一步。
- 节点间依赖关系靠代码顺序隐式表达,拓扑结构不可见,无法做环路检测
- 每个服务调用的超时、重试、降级策略混在同一个
context.WithTimeout里,支付要 5 秒超时,人工审批却要等 72 小时 - 进程重启后,
map[string]interface{}里的流程状态直接丢失,没地方恢复 - 日志打散在各服务中,缺乏统一
workflowInstanceId关联,排查时得翻 4 个服务的日志
gRPC-Go 编排必须区分同步与异步模式
不是所有微服务调用都适合串行等待。下单流程里库存检查和支付可以同步,但发短信、更新推荐模型、写审计日志就得异步解耦——否则一个短信网关抖动,整单卡死。
-
同步编排:用
Unary RPC嵌套调用,但必须配合拦截器统一注入workflowInstanceId和traceId,否则下游服务无法关联上下文 -
异步编排:上游完成即发消息到 Kafka/Redis Stream,下游服务监听消费;注意
codes.Unavailable这类临时错误要触发重试,而codes.InvalidArgument应直接失败不重试 - 避免在 gRPC handler 里直接启动 goroutine 处理后续步骤——goroutine 无生命周期管理,panic 会丢失,也无法被工作流引擎追踪
选工作流引擎前先确认部署形态
Conductor2 和 Temporal 看起来都是“Go 工作流”,但前者是库(import "github.com/jnorthrup/conductor2"),后者是服务(需部署 temporal-server)。选错会导致开发环境能跑,上线后才发现缺数据库或消息队列。
- 如果项目已用 Redis,且只需要轻量级任务编排(如用户注册后发邮件+初始化资料),
conductor2配redis-backend是最省事的选择 - 如果流程涉及长时间运行(如订单履约超 24 小时)、需要精确的 Saga 补偿、或已有团队熟悉 Temporal 的 Web UI,那
temporal-goSDK 更合适 -
ADK Go 2.0适合多智能体场景,它的图节点天然支持emitting暂停等待人工操作,但要求所有节点函数签名符合func(ctx context.Context, input any) (output any, err error) - 别被 “轻量” 二字误导:
moryflow的 YAML 定义虽简单,但它不支持节点级重试配置,所有步骤共用同一套超时参数
状态持久化粒度决定可靠性上限
把整个 data map[string]interface{} 序列化存 Redis,看着方便,但并发高时容易覆盖、反序列化失败、字段类型丢失。真正稳定的方案是分层存储。
- 高频变更字段(当前节点名、重试次数、最后更新时间)存在 Redis,key 为
workflow:<instanceid>:state</instanceid>,设 TTL 避免脏数据堆积 - 关键业务字段(审批意见、原始表单 JSON、支付流水号)走 PostgreSQL,建
workflow_instances表,每条记录带version字段做乐观锁 - 只存 diff:节点执行后,对比输入输出,仅将变化字段写入持久层,用
jsonpatch或自定义DirtyFields结构体标记 - 事务边界必须对齐:比如调用支付服务成功后,要在一个 DB 事务里同时更新
payment_status = 'success'和current_node = 'send_notification',否则出现“已扣款但流程卡住”
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











