重試模式:建構具韌性的應用程式

  1. 情境與問題
  2. 解決方案
  3. 重試策略
  4. 實作考量
  5. 測試與驗證
  6. 何時使用此模式
  7. 與斷路器結合
  8. 相關模式
  9. 參考資料

當你的應用程式與遠端服務通訊時——資料庫、API、訊息佇列——可能會出錯。網路中斷、伺服器忙碌或暫時性逾時都可能導致請求失敗。重試模式幫助你的應用程式優雅地處理這些暫時性故障,將潛在的失敗轉化為成功。

情境與問題

分散式系統經常面臨暫時性故障:

  • 網路連線中斷:元件之間的短暫斷線
  • 服務不可用:部署或重啟期間的暫時性服務中斷
  • 逾時:服務在高負載下回應時間過長
  • 節流:服務在過載時拒絕請求

這些故障通常會自我修正。暫時過載的資料庫可能現在拒絕你的連線,但在清除積壓工作後一秒鐘就會接受。如果沒有重試機制,你的應用程式會將這些暫時性問題視為永久性故障,不必要地降低使用者體驗。

解決方案

設計你的應用程式以預期暫時性故障並透明地處理它們。重試模式引入一種機制,自動重試失敗的操作,將對業務功能的影響降到最低。

Z3JhcGggTFIKICAgIEFbIuaHieeUqOeoi+W8jyJdIC0tPiBCWyLph43oqabpgo/ovK8iXQogICAgQiAtLT4gQ1si6YGg56uv5pyN5YuZIl0KICAgIEMgLS0+fCLmiJDlip8ifCBEWyLlm57lgrPntZDmnpwiXQogICAgQyAtLT58Iuaaq+aZguaAp+aVhemanCJ8IEVbIuetieW+heS4pumHjeippiJdCiAgICBFIC0tPiBDCiAgICBDIC0tPnwi6LaF6YGO5pyA5aSn6YeN6Kmm5qyh5pW4InwgRlsi6JmV55CG5L6L5aSWIl0KICAgIAogICAgc3R5bGUgQSBmaWxsOiNlMWY1ZmYKICAgIHN0eWxlIEIgZmlsbDojZmZmNGUxCiAgICBzdHlsZSBDIGZpbGw6I2ZmZTFlMQogICAgc3R5bGUgRCBmaWxsOiNkM2Y5ZDg=

💡 內建重試機制

許多現代客戶端函式庫和框架都包含可設定的重試邏輯。在實作自訂重試程式碼之前,請先查看你的函式庫文件。

!!!

重試策略

根據故障類型和應用程式需求選擇重試策略:

1. 取消

何時使用:故障表示永久性問題或即使重試也不會成功的操作。

範例

  • 驗證失敗
  • 無效的請求參數
  • 找不到資源錯誤

動作:立即取消操作並回報例外。

2. 立即重試

何時使用:故障不尋常或罕見,例如網路封包損毀。

範例

  • 隨機網路傳輸錯誤
  • 暫時性連線重設

動作:立即重試請求,不延遲。

3. 延遲後重試

何時使用:故障常見且與連線或服務負載相關。

範例

  • 連線逾時
  • 服務忙碌回應
  • 節流錯誤

動作:等待後再重試,使用以下延遲策略之一:

固定延遲:每次重試之間等待相同的時間。

嘗試 1 → 等待 2 秒 → 嘗試 2 → 等待 2 秒 → 嘗試 3

遞增延遲:線性增加等待時間。

嘗試 1 → 等待 2 秒 → 嘗試 2 → 等待 4 秒 → 嘗試 3 → 等待 6 秒 → 嘗試 4

指數退避:每次失敗後將等待時間加倍。

嘗試 1 → 等待 1 秒 → 嘗試 2 → 等待 2 秒 → 嘗試 3 → 等待 4 秒 → 嘗試 4 → 等待 8 秒 → 嘗試 5

帶抖動的指數退避:在指數延遲中加入隨機性,以防止多個客戶端同時重試(「驚群」問題)。

實作考量

記錄策略

適當記錄故障以避免警報疲勞:

  • 早期故障:記錄為資訊項目
  • 成功重試:記錄在除錯層級
  • 最終故障:僅在所有重試都用盡後記錄為錯誤

這種方法為操作人員提供可見性,而不會用自我修正問題的警報淹沒他們。

Z3JhcGggVEIKICAgIEFbIuiri+axguWkseaVlyJdIC0tPiBCWyLoqJjpjITvvJpJTkZPIC0g5ZiX6KmmIDEg5aSx5pWXIl0KICAgIEIgLS0+IENbIuetieW+heS4pumHjeippiJdCiAgICBDIC0tPiBEWyLoq4vmsYLlho3mrKHlpLHmlZciXQogICAgRCAtLT4gRVsi6KiY6YyE77yaSU5GTyAtIOWYl+ippiAyIOWkseaVlyJdCiAgICBFIC0tPiBGWyLnrYnlvoXkuKbph43oqaYiXQogICAgRiAtLT4gR3si5oiQ5Yqf77yfIn0KICAgIEcgLS0+fCLmmK8ifCBIWyLoqJjpjITvvJpERUJVRyAtIOWYl+ippiAzIOaIkOWKnyJdCiAgICBHIC0tPnwi5ZCm77yI5pyA5aSn6YeN6Kmm5qyh5pW477yJInwgSVsi6KiY6YyE77yaRVJST1IgLSDmiYDmnInph43oqablt7LnlKjnm6EiXQogICAgCiAgICBzdHlsZSBBIGZpbGw6I2ZmZTFlMQogICAgc3R5bGUgSCBmaWxsOiNkM2Y5ZDgKICAgIHN0eWxlIEkgZmlsbDojZmY2YjZi

效能影響

調整重試策略以符合業務需求:

互動式應用程式(網頁應用程式、行動應用程式):

  • 快速失敗,較少重試次數
  • 嘗試之間使用短延遲
  • 顯示使用者友善訊息(「請稍後再試」)

批次應用程式(資料處理、ETL 作業):

  • 使用更多重試嘗試
  • 採用指數退避與較長延遲
  • 優先考慮完成而非速度

⚠️ 避免激進重試

激進的重試策略(許多重試且延遲最小)可能會使情況惡化:

  • 進一步降低已經過載的服務
  • 降低應用程式的回應性
  • 在系統中造成級聯故障

考慮在重試之外實作斷路器模式,以防止壓垮失敗的服務。

!!!

冪等性

在應用重試之前,確保操作是冪等的(多次執行是安全的)。非冪等操作可能導致意外的副作用:

問題情境

  1. 服務接收請求並成功處理
  2. 服務因網路問題無法傳送回應
  3. 客戶端重試,導致重複處理

解決方案

  • 設計操作為自然冪等
  • 使用唯一請求識別碼來偵測重複
  • 實作伺服器端去重邏輯

例外類型

不同的例外需要不同的重試策略:

例外類型 重試策略 範例
暫時性網路錯誤 延遲後重試 連線逾時、DNS 解析失敗
服務忙碌/節流 指數退避重試 HTTP 429、HTTP 503
驗證失敗 立即取消 無效憑證、過期權杖
無效請求 立即取消 HTTP 400、格式錯誤的資料
找不到資源 立即取消 HTTP 404

交易一致性

在交易中重試操作時:

  • 微調重試策略以最大化成功機率
  • 最小化回滾交易步驟的需求
  • 考慮分散式情境的補償交易
  • 確保重試邏輯不違反交易隔離層級

測試與驗證

🧪 測試檢查清單

  • 針對各種故障條件進行測試(逾時、連線錯誤、服務不可用)
  • 驗證正常和故障情境下的效能影響
  • 確認下游服務沒有過度負載
  • 檢查並行重試的競爭條件
  • 驗證不同故障階段的記錄輸出
  • 測試交易回滾情境

!!!

巢狀重試策略

避免分層多個重試策略:

問題:任務 A(有重試策略)呼叫任務 B(也有重試策略)。這會造成指數級的重試嘗試和不可預測的延遲。

解決方案:設定低層級任務快速失敗並回報故障。讓高層級任務根據自己的策略處理重試。

Z3JhcGggVEIKICAgIEFbIuS7u+WLmSBBPGJyLz7vvIjph43oqabnrZbnlaXvvIkiXSAtLT4gQlsi5Lu75YuZIEI8YnIvPu+8iOeEoemHjeippu+8iSJdCiAgICBCIC0tPnwi5b+r6YCf5aSx5pWXInwgQQogICAgQSAtLT58IuagueaTmuetlueVpemHjeippiJ8IEIKICAgIAogICAgc3R5bGUgQSBmaWxsOiNlMWY1ZmYKICAgIHN0eWxlIEIgZmlsbDojZmZmNGUx

何時使用此模式

使用重試模式當

  • 你的應用程式與遠端服務或資源互動
  • 故障預期是暫時性且短暫的
  • 重複失敗的請求有很大機會成功
  • 操作是冪等的或可以變成冪等的

不要使用重試模式當

  • 故障可能是長期的(改用斷路器)
  • 處理非暫時性故障(業務邏輯錯誤、驗證失敗)
  • 解決可擴展性問題(改為擴展服務)
  • 操作有重大副作用且不是冪等的

與斷路器結合

重試和斷路器模式相輔相成:

  • 重試:透過再次嘗試操作來處理暫時性故障
  • 斷路器:當已知服務停機時防止重試
c3RhdGVEaWFncmFtLXYyCiAgICBbKl0gLS0+IENsb3NlZDog5q2j5bi46YGL5L2cCiAgICBDbG9zZWQgLS0+IE9wZW46IOi2hemBjuaVhemanOmWvuWAvAogICAgT3BlbiAtLT4gSGFsZk9wZW46IOmAvuaZguW3sumBjgogICAgSGFsZk9wZW4gLS0+IENsb3NlZDog5oiQ5YqfCiAgICBIYWxmT3BlbiAtLT4gT3Blbjog5aSx5pWXCiAgICAKICAgIG5vdGUgcmlnaHQgb2YgQ2xvc2VkCiAgICAgICAg6KuL5rGC6YCa6YGOCiAgICAgICAg5aSx5pWX5pmC6YeN6KmmCiAgICBlbmQgbm90ZQogICAgCiAgICBub3RlIHJpZ2h0IG9mIE9wZW4KICAgICAgICDoq4vmsYLnq4vljbPlpLHmlZcKICAgICAgICDkuI3lmJfoqabph43oqaYKICAgIGVuZCBub3RlCiAgICAKICAgIG5vdGUgcmlnaHQgb2YgSGFsZk9wZW4KICAgICAgICDlhYHoqLHmnInpmZDoq4vmsYIKICAgICAgICDmuKzoqabmnI3li5nmgaLlvqkKICAgIGVuZCBub3Rl

這些模式一起提供全面的故障處理:

  1. 重試處理暫時性故障
  2. 斷路器防止壓垮失敗的服務
  3. 即使在長時間中斷期間,系統仍保持回應

相關模式

斷路器:防止應用程式重複嘗試執行可能失敗的操作,使其能夠繼續運作而無需等待故障修復。

節流:控制應用程式實例、服務或租戶的資源消耗。

速率限制:管理傳送到服務的請求速率,以避免壓垮它。

參考資料

分享到