# 微服務架構設計 - 重試 Retry Pattern
# Retry Pattern Introduction
Retry Pattern 是 Resiliency Pattern (韌性模式) 的核心之一,藉由重新嘗試錯誤的操作來幫助增強服務的可用性,啟動應用程式去處理短暫的錯誤 (可能是網路或是服務錯誤),藉由簡單的重試來增強系統的穩定性,近幾年的雲原生或是應用程式需要去處理短暫的錯誤,總之重試錯誤的操作是個不錯的選擇
# 了解適合重試的錯誤操作
- 檢查 Response 並取得 Status Code 得知並驗證錯誤並找到問題的答案,如果重試後得到成功是暫時性的錯誤
- 如果下游服務超載請不要重試,檢查 Response 與 Status Code 來所得所需資訊
- 普遍來說,當我們知道重試的全部影響或了解相關操作的整個生命週期時,才能進行重試
# 使用指數退避重試 Exponential back-off for retry
- 使用指數退避重試試增加每次重試的嘗試時間
- 使用指數退避重試的隨機化,而不是每次重試的標準模板
- 取決於操作,一個立即的重試可以在指數退避重試之前,但最多一次立即重試
# 決定重試次數與重試間隔
- 不要無限期的重試
- 識別與決定重試次數與間隔是很難的決定,取決於操作類型,使用有限的重試,並在重試後使用 Circuit Breaker 讓下游服務恢復
- 如果操作是來自部分的使用者互動,那重試次數應該被限制與時間限制在 ms ~ s 之間,總體來說整體操作與重試不應該超過數秒。
- 如果操作是部份的背景執行,如資料流水線與定時執行任務,那重試次數可以多一點,時間間隔也可以多一點。
- 檢查來自嘗試操作錯誤 Response ,他可能會提供 retry-after Header 或是類似的東西,如果他存在用於你的重試間隔中,不要忘記去檢查操作的 Status Code/Response/Header 每次都要,如果下游服務可能改變且傳送 Error 但沒有重試
# 確保重試沒有操作完成當使用者正在等待回復
- 嘗試從使用者得到 Timeout 值以及如果不支援用預設邏輯操作,當重試他需要被檢查總操作時間,之後使用者應該決定請求是否因為 Timeout 而失敗
- 基於操作可能取消或了解使用者不再等待回應的方式 (如關閉連線)。如果這些訊號可用,請使用他們並且不要重試。
# 考慮制定伺服器範圍的重試預算
- 其中一個重試挑戰是他們可以導致階層式的故障,放慢速度並未下游服務提供喘息的機會從故障中恢復非常重要,根據服務可以定義重試預算
- 使用 Circuit Breaker 如果超出重試預算
# 透過在多個層級發出重試來避免放大重試
- 思考整個服務並且決定我們需要給定重試的程度,如有疑問,請勿重試並讓操作的使用者接聽該呼叫。
- 一般來說,當重試於多程度的完成可能導致階層性的錯誤與降級 \
# 監控與紀錄重試與操作
- 紀錄異常與錯誤代碼,重試次數,API 花費時間,導致原因以及從部分服務收到的資料,分析與理解根本原因
- 實作可能導致警報的傳輸與監控系統,當錯誤率超過限制
# 檢查錯誤的操作一致性
- 設置基於操作的警報當 API 或是 Server 顯示出高量錯誤
- 嘗試建立足夠智能的 Health Check API 可以了解內部不可使用的服務狀態如果服務與資料庫斷線了但是儲存到資料庫可以成功,讓 HA 去檢查即便可以使用。
- 訂閱依賴資源的 HA 並且檢查指標是否正常
# 其他考量
- 當重試考慮的範疇,有很多種請求操作取決於何種錯誤的程度
- 不要在重試之間等待同步
- 確保下游操作在操作多次期間沒有副作用
- 考慮重試策略可以影響其他租客共享應用程式或是用共享資源與服務,並且分給每個租客重試的預算與限制
# 參考資料
- Medium - Best practices for retry pattern