

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 使用輪詢重試模試
<a name="retry-backoff"></a>

## 意圖
<a name="retry-backoff-intent"></a>

使用退避模式的重試透過透明重試因暫時性錯誤而失敗的操作來改善應用程式穩定性。

## 動機
<a name="retry-backoff-motivation"></a>

在分散式架構中，暫時性錯誤可能是由於服務限流、暫時失去網路連線或暫時服務無法使用所造成。由於這些暫時性錯誤而失敗的自動重試操作可改善使用者體驗和應用程式彈性。不過，頻繁重試可能會使網路頻寬過載並導致爭用。指數退避是一種技術，透過增加指定重試次數的等待時間來重試操作。

## 適用性
<a name="retry-backoff-applicability"></a>

在下列情況下，使用重試與退避模式：
+ 您的服務經常調節請求以防止過載，導致呼叫程序*發生 429 個請求*例外狀況。
+ 網路是分散式架構中看不見的參與者，而暫時性網路問題會導致失敗。
+ 正在呼叫的服務暫時無法使用，導致失敗。除非您使用此模式引入退避逾時，否則頻繁重試可能會導致服務降級。

## 問題和考量
<a name="retry-backoff-issues"></a>
+ **冪等性**：如果對 方法的多個呼叫與系統狀態的單一呼叫具有相同的效果，則操作會被視為冪等性。當您使用具有退避模式的重試時，操作應該是等冪的。否則，部分更新可能會損毀系統狀態。
+ **網路頻寬**：如果太多次重試佔用網路頻寬，會導致回應時間變慢，則可能會發生服務降級。
+ **快速失敗案例**：對於非暫時性錯誤，如果您可以判斷失敗的原因，則使用斷路器模式快速失敗會更有效率。
+ **退避率**：引入指數退避可能會影響服務逾時，導致最終使用者的等待時間較長。

## 實作
<a name="retry-backoff-implementation"></a>

### 高層級架構
<a name="retry-high-level-arch"></a>

下圖說明 Service A 如何重試對 Service B 的呼叫，直到成功傳回回應為止。如果服務 B 在嘗試幾次後仍未傳回成功回應，服務 A 可以停止重試並將失敗傳回給其發起人。

![使用退避模式重試的高階架構](http://docs.aws.amazon.com/zh_tw/prescriptive-guidance/latest/cloud-design-patterns/images/retry-backoff-1.png)


### 使用 AWS 服務實作
<a name="retry-aws-services"></a>

下圖顯示客戶支援平台上的票證處理工作流程。來自不滿意客戶的票證會透過自動呈報票證優先順序來加速。`Ticket info` Lambda 函數會擷取票證詳細資訊並呼叫 `Get sentiment` Lambda 函數。`Get sentiment` Lambda 函數透過將描述傳遞至 [Amazon Comprehend](https://aws.amazon.com/comprehend/) （未顯示） 來檢查客戶情緒。

如果呼叫 `Get sentiment` Lambda 函數失敗，工作流程會重試操作三次。可讓您設定退避值，以 AWS Step Functions 允許指數退避。

在此範例中，最多會設定三次重試，增加乘數為 1.5 秒。如果第一次重試發生在 3 秒之後，第二次重試發生在 3 x 1.5 秒 = 4.5 秒之後，而第三次重試發生在 4.5 x 1.5 秒 = 6.75 秒之後。如果第三個重試失敗，工作流程會失敗。退避邏輯不需要任何自訂程式碼，由 提供做為組態 AWS Step Functions。

![使用 AWS 服務以退避模式重試](http://docs.aws.amazon.com/zh_tw/prescriptive-guidance/latest/cloud-design-patterns/images/retry-backoff-2.png)


### 範本程式碼
<a name="retry-sample-code"></a>

下列程式碼顯示使用退避模式進行重試的實作。

```
public async Task DoRetriesWithBackOff()
  {
     int retries = 0;
     bool retry;
     do
     {
       //Sample object for sending parameters
       var parameterObj = new InputParameter { SimulateTimeout = "false" };
       var content = new StringContent(JsonConvert.SerializeObject(parameterObj), 
                                System.Text.Encoding.UTF8, "application/json");
       var waitInMilliseconds = Convert.ToInt32((Math.Pow(2, retries) - 1) * 100);
       System.Threading.Thread.Sleep(waitInMilliseconds);
       var response =  await _client.PostAsync(_baseURL, content);
       switch (response.StatusCode)
       {
         //Success
         case HttpStatusCode.OK:
           retry = false;
           Console.WriteLine(response.Content.ReadAsStringAsync().Result);
           break;
         //Throttling, timeouts 
         case HttpStatusCode.TooManyRequests:
         case HttpStatusCode.GatewayTimeout:
           retry = true;
           break;
         //Some other error occured, so stop calling the API
         default:
           retry = false;
           break;
       }
       retries++;
     } while (retry && retries < MAX_RETRIES);
  }
```

### GitHub 儲存庫
<a name="retry-github-repo"></a>

如需此模式範例架構的完整實作，請參閱 GitHub 儲存庫，網址為 https：//[https://github.com/aws-samples/retry-with-backoff](https://github.com/aws-samples/retry-with-backoff)。

## 相關內容
<a name="retry-backoff-resources"></a>
+ [具有抖動的逾時、重試和退避 ](https://aws.amazon.com/builders-library/timeouts-retries-and-backoff-with-jitter/)(Amazon Builders' Library)