3、错误处理哲学:分布式系统中错误处理的黄金法则、幂等性、补偿与重试策略

在分布式审批流系统中,网络抖动、服务宕机、数据不一致是常态而非异常。错误处理不是“事后补救”,而是状态机设计的核心骨架。本章从哲学层面提炼三条黄金法则,并深入幂等性、补偿与重试三大实战策略。

3.1 分布式错误处理的黄金法则

任何分布式操作都必须遵循以下三条法则,它们构成了错误处理决策树的根节点:

法则 核心原则 在审批流中的体现
法则一:先写后做 在发起任何远程调用或副作用前,先将“意图”持久化到数据库。 审批节点状态先标记为“处理中”,再发送通知或调用外部服务。
法则二:幂等守护 同一操作无论执行一次还是多次,对系统状态的影响必须一致。 “审批通过”操作重复执行不会导致重复扣款或重复发奖。
法则三:最终一致 允许短暂不一致,但必须通过补偿或重试在有限时间内达成一致。 审批完成后,下游库存扣减失败,通过定时任务补偿回滚。
架构师视角: 三条法则的优先级是递进的。先确保“意图不丢”(法则一),再保证“重复无害”(法则二),最后兜底“不一致可修复”(法则三)。违反任意一条,状态机都会出现不可逆的“幽灵状态”。

3.2 幂等性:状态机的防重盾牌

幂等性是分布式错误处理的基石。在审批流中,最常见的非幂等场景是“重复回调”与“重复提交”。

3.2.1 幂等实现的三种模式

模式 实现方式 适用场景 风险
唯一键去重 使用业务ID(如审批单号+节点ID)作为数据库唯一约束,重复插入直接忽略。 消息队列消费、回调接口 需保证唯一键在分布式环境下全局不重复
状态机前置校验 执行操作前检查当前状态是否允许目标状态转移。 用户重复点击“审批通过” 需配合乐观锁防止并发
Token 令牌机制 每次操作前从服务端获取一次性 Token,提交时校验并销毁。 前端防重复提交、支付回调 增加一次网络交互,需处理 Token 过期
// 唯一键去重示例(伪代码)
public boolean handleApprovalCallback(String approvalId, String nodeId) {
    String uniqueKey = approvalId + ":" + nodeId + ":APPROVED";
    try {
        // 数据库唯一约束保证幂等
        insertIntoEventLog(uniqueKey, "PROCESSING");
        // 执行实际状态转移
        stateMachine.transit(approvalId, nodeId, Event.APPROVE);
        updateEventLog(uniqueKey, "SUCCESS");
        return true;
    } catch (DuplicateKeyException e) {
        // 重复回调,直接返回成功
        log.warn("幂等拦截:重复回调 {}", uniqueKey);
        return true;
    }
}

3.3 补偿策略:优雅回滚的艺术

当状态机执行到一半,某个下游服务失败且无法重试时,补偿是唯一出路。补偿不是简单的“删除数据”,而是执行一个业务语义上的反向操作。

3.3.1 补偿的三种粒度

  • 事务级补偿: 在单个数据库事务内,通过 try-catch 直接回滚。适用于本地事务。
  • 服务级补偿: 调用下游服务的“撤销”接口(如退款、取消订单)。适用于跨服务调用。
  • 流程级补偿: 启动一个反向的审批流(如“撤回申请”),将整个流程恢复到上一个稳定状态。
避坑指南: 补偿操作本身也必须幂等。如果补偿失败,需要记录补偿日志并触发人工介入。不要设计“无限补偿”循环,应设置最大补偿次数。

3.4 重试策略:有限度的坚持

重试是应对瞬时故障的首选策略,但必须遵循“有限度、有间隔、有熔断”的原则。

3.4.1 重试策略对比

策略 说明 优点 缺点
固定间隔重试 每次等待固定时间(如 5s)后重试 实现简单 容易造成“惊群效应”,对下游压力大
指数退避 等待时间按指数增长(1s, 2s, 4s, 8s...) 对下游友好,减少瞬时压力 总等待时间可能过长
抖动退避 在指数退避基础上增加随机偏移 避免多个客户端同时重试 实现稍复杂
最大重试次数 达到上限后不再重试,转入补偿或人工 防止无限循环 需合理设置阈值

3.5 错误处理全景图

以下 SVG 图展示了从“操作发起”到“最终一致”的完整错误处理决策路径,包含幂等、重试与补偿的协同关系。

错误处理决策全景图 操作发起 幂等检查 幂等命中:直接返回 执行操作 成功:记录日志 失败:判断是否可重试 重试(指数退避) 补偿(反向操作) 说明:幂等检查是入口守卫,重试与补偿是出口兜底,三者共同保证状态机最终一致。

3.6 策略组合:从理论到实战

单一策略无法应对所有故障。以下表格展示了不同故障场景下的策略组合推荐:

故障场景 推荐策略组合 说明
网络超时(瞬时) 幂等 + 指数退避重试(3次) 重试成功后继续流程,失败则转入补偿
下游服务不可用(持续) 幂等 + 熔断 + 补偿 直接熔断,不重试,执行补偿回滚
数据冲突(乐观锁失败) 重试(固定间隔1次)+ 幂等 重新读取最新状态后重试,避免死锁
业务校验失败 不重试,直接补偿 业务规则错误,重试无意义,需人工介入
核心总结: 错误处理的本质是“确定性”。通过幂等保证重复无害,通过重试克服瞬时波动,通过补偿修复不可逆错误。三者构成一个闭环,让状态机在不可靠的分布式环境中依然保持可靠。