手册

支付工作人员的重试算法 (Retry Algorithms For Payment Workers)

指数退避、抖动、上限、延迟与重试差异以及断路器——将上一节中的分类法转换为工作代码。

分布式支付引擎

部分 12 的 22

一系列分布式支付架构弥合了捕获和完成之间的差距。

Distributed payment engine architecture diagram

我们在上一节中定义了四个错误类别。本节为可重试的三个类别(超时后查询、速率限制、基础设施)设置实际算法:等待多长时间、尝试多少次、何时完全停止并打开断路器。```text Attempt 1 → başarısız → bekle (backoff) → Attempt 2 → başarısız → bekle (daha uzun) → Attempt 3 → başarısız → cap'e ulaşıldı → defer / dead-letter


## 第一次提到的概念```text
📦 Exponential Backoff
Her denemede bekleme süresini katlayarak artıran strateji: base * 2^attempt.

📦 Jitter
Backoff süresine eklenen rastgele sapma; çok sayıda worker'ın aynı anda tekrar denemesini (thundering herd) önler.

📦 Cap
Bekleme süresinin ve/veya deneme sayısının üst sınırı; sonsuz retry döngüsünü engeller.

📦 Circuit Breaker
Bir bağımlılık sürekli başarısız olduğunda istekleri tamamen durduran, zamanla yeniden deneyen koruma mekanizması.
```无抖动退避会导致数百个同时失败的作业在同一毫秒内重试,从而使本已紧张的 PSP 陷入更糟糕的境地。

## 退避公式以及为什么持续等待是不够的

固定的 1 秒等待很简单,但它有两个问题:如果 PSP 正在经历短突发,1 秒可能不够;如果PSP已经恢复,1秒是不必要的慢。指数退避在第一次尝试时很快,在后续尝试中更加谨慎:```text
delay = min(cap, base * 2^attempt) + random(0, jitterRange)

attempt 0 → ~200ms
attempt 1 → ~400ms
attempt 2 → ~800ms
attempt 3 → ~1600ms
...
attempt N → cap'e ulaşır (örn. 30s)
```如果不添加抖动,这个公式是危险的:所有同时失败的工作人员在 200 毫秒、400 毫秒、800 毫秒后再次尝试,并以同步波击中 PSP。添加随机量(`full jitter` 或 `decorrelated jitter`)会发出此波。

## 重试和延迟的区别

**重试**,工作人员在短暂等待后(通常在几秒钟内)在同一进程中重试同一请求。 **延迟**是指作业被放回到数据库或队列中,并在一段时间(几分钟甚至几小时)后再次被占用。速率受限错误通常可以通过重试来解决;但是,如果 PSP 本身正在经历大规模中断,则在重试循环中停留几分钟将耗尽工作人员和资源 - 此时推迟是让作业“休眠”一段时间的更安全方法。```text
Rate limited → retry (saniyeler, backoff ile)
Uzun süreli PSP kesintisi → defer (dakikalar, ayrı bir zamanlanmış tekrar)
```## 断路器:何时完全停止尝试

当对依赖项的请求反复失败时,每个新请求只会重现已知的结果;它只会消耗资源并增加延迟。断路器工作在三种状态:```text
Closed   → istekler normal şekilde gönderilir
  │ hata eşiği aşıldı
  ▼
Open     → istekler hemen reddedilir, PSP'ye hiç gitmez
  │ soğuma süresi geçti
  ▼
Half-Open → sınırlı sayıda deneme istek gönderilir
  ├─ başarılı → Closed
  └─ başarısız → Open
```断路器并不能代替重试;它是上层,可以在重试产生浪费时及早检测到。当断路器打开时,工作人员应将自己引导至延迟队列,而不是继续徒劳地尝试。

## 尝试多少次,上限是多少

这些数字不应该是任意的;它应该与 PSP 自己的 SLA 和工作的业务价值相称。对于高额支付,8-10 次尝试和 5 分钟的总窗口可能是合理的;对于低优先级后台进程,3 次尝试可能就足够了。

## 经常混淆的区别```text
❌ Retry = defer
✓ Retry saniyeler içinde aynı process'te olur; defer işi dakikalarca bekletir

❌ Jitter isteğe bağlı bir iyileştirmedir
✓ Jitter'sız backoff, thundering herd riskini gerçek hale getirir

❌ Circuit breaker retry'ın alternatifidir
✓ Circuit breaker, retry'ı ne zaman durduracağını söyleyen üst katmandır
```## 完全抖动与无抖动退避

|标准|无抖动 |全抖动|
| --- | --- | --- |
|同步波风险|高|低|
|在 PSP 上加载图案 |突如其来的高峰|分散|
|应用复杂度 |低|少高 |

## 设置重试算法时的检查表

1. 退避公式是否有上限,或者冷却时间理论上可以无限增长吗?
2. 是否应用了抖动或所有工作人员同时重试?
3. 限速重试/延迟和长期中断之间是否有区别?
4. 断路器打开时,工作人员真的会停止向 PSP 发送请求吗?
5. 尝试次数和总窗口是由作业的实际作业价值决定的,还是一个随机数?
6. 断路器的分闸/半分闸/闭合转换是否经过测量监控?

## 本文中您应该记住的内容

1. 仅仅依靠指数退避是不够的;产生无抖动的同步波。
2. 重试和延迟不是同一个动词:一个是秒,另一个是分钟-小时。
3. 断路器并不是重试的替代方案,而是一层保护,可以尽早告知重试何时变得浪费。
4.应根据工作的实际价值有意识地选择尝试次数和上限。

> 良好的重试算法不会隐藏失败;它控制失败的成本。

在下一节中,我们将深入探讨这些重试的工作原理:数据库支持的作业队列和租用机制如何防止同一个作业同时由两个工作进程处理?

FAQ

Frequently asked questions

什么是指数退避?

每次尝试都会以指数方式增加等待时间的策略:base * 2^attempt。

什么是抖动?

退避时间添加随机偏差;它可以防止多个工作人员同时再次尝试(惊群)。

“重试=延迟”正确吗?

重试发生在同一进程中,数秒内; defer 使作业等待几分钟

本节修复了什么?

此处不应混淆两个不同的动词: **重试** 在同一个工作线程中立即重试;如果**延迟**,则让作业等待一段时间,然后将其放回队列中。它们听起来都像是“再试一次”,但时机和责任不同。仅靠指数退避是不够的;产生无抖动的同步波。我们在上一节中定义了四个错误类别。本节为可重试的三个类别(超时后查询、速率限制、基础设施)设置实际算法:等待多长时间、尝试多少次、何时完全停止并打开断路器。

学到的工程原理

  • 无抖动退避产生同步故障波。
  • 重试是秒,延迟是分钟或小时——不是同一个动词。
  • 当重试变得浪费时,断路器会提前停止重试。

继续阅读

继续阅读

系列中的下一个

随笔

数据库支持租赁作品

带条件更新的租赁,可以挽救陷入困境的工作的观察者,以及为什么仅仅取消消息不足以支付生产费用。

系列中的下一个

随笔

付款错误分类

超时、429、5xx、业务下降和基础设施错误不是一回事。每个类别都需要不同的重试策略。

同系列

随笔

付款对账工人施工

扫地机如何改善漂移:虽然PSP成功,但本地注册可能会过期;如何恢复老化的 FinalizePending。

Paylaş