VM0 把工作流骨架和节点连起来之后,正常路径通常能跑通,但失败路径往往还是空的:骨架只回答“节点按什么顺序执行”,不回答“执行挂了谁管、重试几次、什么时候叫人”。建议把这件事拆成两半——平台侧产出结构与节点,业务侧为每个节点补上三样东西:失败分类、重试与转人工规则、告警接收人和阈值依据。判断标准很简单:规则写在配置里、能被日志和一次人为失败验证,才算补完;只写在文档或口头约定里,等于没补。
骨架跑通只说明正常路径可用,失败路径需要业务方逐节点补齐。可以先按“暂时性失败可重试、终局性失败直接转人工”给每个动作分类,再为重试设次数和间隔,重试耗尽或遇到终局失败时才发告警并指定接收人。阈值不要凭印象定,用同节点历史失败记录统计;样本不足时在配置里标注待观察。最后人为制造一次失败,确认重试、告警、转人工三条路径真的会执行。
列出每条链路里可能失败的动作,区分暂时性失败和终局性失败
先把 VM0 生成的工作流按节点展开,逐个节点列出“出站动作”,也就是真正可能失败的地方。常见的有:调用外部 HTTP 接口、写业务库、投递消息队列、读写对象存储、调用第三方系统、触发另一个工作流。只列动作不够,还要在动作旁标注它是否幂等、是否可重复执行。
分类依据建议以错误码和异常类型为准,而不是凭感觉。超时、连接被拒、连接重置、限流返回(如 HTTP 429)、服务端 5xx、数据库锁等待超时,一般归为暂时性失败,可以重试;鉴权失败(401/403)、参数校验失败(400)、资源不存在、唯一键冲突、业务规则明确拒绝,一般归为终局性失败,重试没有意义,应直接转人工。分不清的先按终局性处理,因为重复写入造成的脏数据,清理成本通常高于转人工。
分类结果建议直接落到节点配置里,而不是另开一张表。这样查看节点的人能同时看到“它做什么”和“它挂了怎么办”,不会出现配置和文档对不上的情况。
给暂时性失败设定重试次数与间隔,并写明超过后转人工
重试参数通常写在工作流节点的策略字段里,或在平台的重试策略页面按节点覆盖。可以先给一个通用接入骨架,再按节点调整:
retry:
maxAttempts: 3
backoff: exponential
initialInterval: 5s
maxInterval: 1m
retryOn: [timeout, connection_refused, http_429, http_5xx]
onExhausted:
action: manual
notify: biz-oncall
record: [node_id, attempt, error_code, payload_hash, last_failed_at]
转人工的触发条件建议只设两条:重试次数用尽,或首次判定为终局性失败。不要设“重试两次先通知一次”这类中间态,通知会变多而信息量不变。
放弃时的记录字段要能支撑事后排查,至少包含 node_id、attempt、最后一次 error_code 和 error_message、输入内容的哈希、首次失败时间和最后一次失败时间、最终处理动作。字段存在哪里需要结合环境确认,但原则是转人工的通知里能直接看到这些值,不需要再翻日志。
确定告警接收人和触发条件,避免每次重试都发通知
接收人按业务归属设定,而不是默认发给平台运维。做法是在节点或工作流上挂一个“归属组”,由该组决定通知渠道:值班邮箱、IM 群、工单系统。节点上没有归属信息时,建议先补这个字段,再谈告警。
告警与重试的先后关系是:先重试,重试耗尽或判定终局失败之后才发告警。每次 attempt 都发通知,会让真正需要人介入的那条被淹没,也会让人开始忽略通知。
静默期建议按“同一 node_id + 同一 error_code”去重,在窗口内只发一次。静默期不能设得过长,否则持续失败会被压成一条早期通知;如果某节点在静默期内又失败,可以只累加计数并在下一次通知里带上次数。这些规则能否生效,可以观察通知渠道里同一错误是否只出现一次来验证。
用历史记录统计同类失败出现的频率,作为阈值依据
阈值应当来自运行记录。统计口径建议按 node_id 加 error_code 分组,统计失败次数、首次出现时间和最近出现时间,同时对照该节点同期的总执行次数。示例查询:
select node_id, error_code, count(*) as fail_cnt,
min(occurred_at) as first_seen, max(occurred_at) as last_seen
from workflow_run_step
where status = 'failed'
and occurred_at >= now() - interval '30 days'
group by node_id, error_code
order by fail_cnt desc;
样本时间范围通常取近 30 天,或覆盖一个完整业务周期(例如一次月末结算),具体取值需要结合业务节奏确认。表名和状态值要按实际环境替换。
数据不足时不要硬定阈值。可以在配置里保留默认值并加一条标注,例如 threshold_source: pending-observation,同时记录样本量;等在观察窗口内积累到足够实例后再收紧。这样别人看到配置时知道这个数字的来路,而不是误以为它经过验证。
人为制造一次失败,验证重试、告警、转人工三条路径
配置写完不等于会执行。建议在非生产环境或隔离的测试节点上做一次演练:
- 把某个节点的目标地址改成一个必定超时的地址,或在调用前插入一条主动抛错的语句,触发暂时性失败。
- 触发运行,观察日志中是否出现多次 attempt、时间间隔是否符合 backoff 设置。观察点是重试次数和间隔,不是最终成功与否。
- 把重试次数临时调到 1,让失败快速耗尽,观察是否发出告警,以及通知里是否带上记录字段。观察点是通知渠道和通知内容。
- 把错误改成鉴权类错误,观察是否跳过重试直接转人工。观察点是有没有多余的 attempt 记录。
- 在静默期内再次触发同一错误,观察是否被去重。观察点是通知条数。
哪一步没生效,就回到对应位置改:重试参数在节点策略字段,通知渠道和接收人在归属组配置,去重规则在告警策略,记录字段在 onExhausted 或平台的失败记录设置。改完再跑一次同样的演练,确认行为一致,才算这条链路补齐。