3. 错误处理基础理论:错误分类、传播模型与幂等补偿
在分布式支付系统中,错误是常态而非异常。中间件必须对错误进行精确分类、可控传播,并通过幂等与补偿机制保证最终一致性。本章从三个核心维度构建错误处理的理论基础。
3.1 错误分类:业务错误、系统错误、网络错误
支付链路中的错误按来源与影响范围可分为三类。错误的分类决定了中间件的处理策略:是重试、降级、告警,还是直接拒绝。
| 错误类型 | 定义 | 典型场景 | 处理策略 |
|---|---|---|---|
| 业务错误 | 因业务规则校验失败产生的可预期错误 | 余额不足、风控拦截、重复支付、优惠券过期 | 直接拒绝,返回明确错误码与提示,不重试 |
| 系统错误 | 服务内部状态异常或资源耗尽导致的错误 | 数据库连接池满、OOM、线程死锁、配置错误 | 快速失败,触发熔断/降级,记录完整堆栈 |
| 网络错误 | 通信链路不稳定或超时导致的临时性错误 | TCP 超时、DNS 解析失败、TLS 握手中断 | 有限次重试(指数退避),配合幂等防止重复 |
实践要点:业务错误必须携带结构化错误码(如
PAY_BALANCE_INSUFFICIENT),便于客户端精确处理;系统错误应区分“可恢复”与“不可恢复”,不可恢复错误直接触发告警而非重试。
3.2 错误传播模型
错误在微服务调用链中的传播方式直接影响系统稳定性。支付中间件通常采用以下三种传播模型:
- 直接传播(Propagate): 将底层错误原样抛给上层调用方。适用于业务错误,保持语义透明。
- 包装传播(Wrap): 将底层错误包装为通用异常,附加上下文信息(如 TraceId、服务名)。适用于系统错误,防止内部细节泄露。
- 静默吞没(Silent): 捕获错误后记录日志,返回默认值或空结果。仅适用于非关键路径(如日志上报、非核心指标采集)。
支付链路中的传播规则: 核心支付链路(扣款、转账)禁止静默吞没;所有跨服务调用必须携带错误传播上下文(TraceId + SpanId),确保错误根因可追溯。
3.3 幂等性与补偿机制
3.3.1 幂等性设计
幂等性是指同一操作无论执行多少次,其结果与执行一次相同。在支付系统中,幂等性是防止重复扣款、重复退款的核心保障。
幂等性实现方案对比:
| 方案 | 原理 | 适用场景 | 注意事项 |
|---|---|---|---|
| 全局唯一ID | 请求携带唯一幂等键,服务端去重 | 支付下单、退款申请 | 幂等键需全局唯一,建议使用 UUID 或雪花算法 |
| 状态机驱动 | 业务状态单向流转,重复请求不改变已终态 | 订单状态更新、转账流程 | 状态转换图需严格定义,禁止回退 |
| 乐观锁 | 使用版本号或时间戳,更新时校验 | 库存扣减、余额变更 | 高并发下需控制重试次数,避免活锁 |
| 去重表 | 数据库唯一索引约束,重复插入失败 | 消息消费、回调处理 | 需定期清理过期记录,避免表膨胀 |
幂等陷阱: 仅依赖数据库唯一索引无法覆盖“请求已处理但响应超时”的场景。必须结合请求去重缓存(如 Redis SETNX)与状态机双重校验,才能实现真正的幂等。
3.3.2 补偿机制
当分布式事务中的部分操作失败时,补偿机制用于回滚已成功的操作,恢复系统到一致状态。支付系统常用的补偿模式包括:
- TCC(Try-Confirm-Cancel): 预留资源 → 确认提交 → 取消回滚。适用于账户冻结、库存预留。
- Saga 模式: 将长事务拆分为多个本地事务,每个事务对应一个补偿操作。失败时按逆序执行补偿。
- 本地消息表 + 定时任务: 将补偿操作记录为消息,由定时任务扫描并执行。
3.4 错误处理策略总结
支付中间件在接收到不同类型的错误时,应遵循以下决策树:
- 判断错误类型: 业务错误 → 直接拒绝,返回错误码;系统/网络错误 → 进入重试判断。
- 判断是否可重试: 幂等操作 → 有限次重试(通常 3 次,指数退避);非幂等操作 → 快速失败,触发补偿。
- 判断补偿可行性: 支持补偿 → 执行 Saga/TCC 回滚;不支持补偿 → 人工介入,发送告警。
核心原则: 业务错误不重试,系统错误限重试,网络错误必重试;所有重试必须配合幂等机制;所有补偿操作必须可观测、可追溯。