重試是把雙面刃:下游只是抖一下時它救場,下游真的過載時它火上澆油。天真的「立即重試 + 固定退避」會讓客戶端同步化,掀起遠超尖峰的雷群(thundering herd)。安全的重試是「加抖動的截斷指數退避 + 重試預算 + 下游能喊停」。
🧠 Intuition
TIP
真實教訓(Pokemon GO):客戶端「單次立即重試後固定退避」在大規模故障時同步化,產生高達平時峰值 20 倍的請求尖峰。修法是引入 jitter(抖動)+ 截斷指數退避,把重試在時間上打散。
- 雷群的成因:故障後大量客戶端在同一時刻一起重試 → 尖峰疊加。固定退避讓大家「同步」,正是問題所在。
- 截斷指數退避(truncated exponential backoff):每次重試等待時間指數成長(1s、2s、4s…)但設上限,避免無限延長。
- 抖動(jitter):在退避時間上加隨機擾動,把「同時重試」打散成「錯開重試」——這是打破同步的關鍵。
- 只在緊鄰失敗那層之上重試:多層都各自重試會指數爆炸(3 層各重試 3 次 = 27 倍放大)。
⚖️ Tradeoffs
重試預算(retry budget)— 給重試設天花板
- Per-request budget:單一請求最多重試 ~3 次,避免個別請求無限打。
- Per-client budget:整個客戶端的重試量不超過總請求的 ~10%——如此即使整個 DC 都壞,總負載也只放大約 1.1 倍而非 3 倍。
- 判斷情境:少量後端過載(較常見)→ 重試多半打到別的健康 task,有用;大部分後端都過載 → 重試沒用,讓錯誤直接上傳給使用者。
- 別把「重試」當免費補救——它消耗的是本就稀缺的過載容量。
讓下游能「喊停」— overloaded; don't retry
- 客戶端在請求 metadata 帶上「已嘗試次數」;backend 若看到很多 high-attempt 請求,代表重試正在放大災情。
- 此時 backend 改回傳特殊錯誤「overloaded; don’t retry」,明確要求上游停止重試、改走降級或直接失敗,阻斷組合爆炸。
- 搭配 deadline:對使用者已無益的請求該及時放棄,別為它持有資源、也別再為它重試。
- 與熔斷器分工:退避管「單次別打太密」、熔斷管「整體先別打了」——兩者常一起用。
🔑 Takeaways
- 天真重試會製造雷群;安全重試 = 截斷指數退避 + jitter,把同步的重試尖峰在時間上打散。
- 用重試預算設天花板(單請求 ~3 次、客戶端 ~10%),並只在緊鄰失敗那層之上重試以免指數爆炸。
- 讓下游能回「overloaded; don’t retry」喊停、配合 deadline 及早放棄;退避與熔斷分工互補。
No notes yet — jot your takeaways or Q&A here.