

# REL 5  如何設計分散式系統中的互動以緩解或承受故障？
<a name="w2aac19b9b7b9"></a>

分散式系統倚賴通訊網路來互連元件 (例如，伺服器或服務)。即使這些網路上的資料遺失或延遲，您的工作負載仍必須可靠運作。分散式系統的元件必須以不會對其他元件或工作負載造成負面影響的方式運作。這些最佳實務讓工作負載能夠承受壓力或故障，更快速地從其中復原，並減輕這類受損的影響。最終縮短平均復原時間 (MTTR)。

**Topics**
+ [REL05-BP01 實作適度降級以將適用的硬相依性轉換為軟相依性](rel_mitigate_interaction_failure_graceful_degradation.md)
+ [REL05-BP02 調節請求](rel_mitigate_interaction_failure_throttle_requests.md)
+ [REL05-BP03 控制和限制重試呼叫](rel_mitigate_interaction_failure_limit_retries.md)
+ [REL05-BP04 快速失敗和限制佇列](rel_mitigate_interaction_failure_fail_fast.md)
+ [REL05-BP05 設定用戶端逾時](rel_mitigate_interaction_failure_client_timeouts.md)
+ [REL05-BP06 盡可能讓服務無狀態](rel_mitigate_interaction_failure_stateless.md)
+ [REL05-BP07 實作緊急控制桿](rel_mitigate_interaction_failure_emergency_levers.md)

# REL05-BP01 實作適度降級以將適用的硬相依性轉換為軟相依性
<a name="rel_mitigate_interaction_failure_graceful_degradation"></a>

 當元件的相依性狀況不良，元件本身仍可運作，但以降級的方式運作。例如，當相依性呼叫失敗時，容錯移轉為預先決定的靜態回應。 

 考慮由服務 A 呼叫的服務 B，並接著呼叫服務 C。 

![\[圖表：顯示從服務 B 呼叫服務 C 時失敗。服務 B 傳回降級回應給服務 A。\]](http://docs.aws.amazon.com/zh_tw/wellarchitected/2022-03-31/framework/images/graceful-degradation.png)


 當服務 B 呼叫服務 C 時，其會從服務 C 收到錯誤或逾時。缺少來自服務 C 回應 (及其包含的資料) 的服務 B，會傳回其所能執行的回應。這可能是上次快取的良好值，或者服務 B 可使用預先決定的靜態回應，取代原可能從服務 C 收到的內容。然後它可以將降級的回應傳回給發起人，即服務 A。如果沒有此靜態回應，則服務 C 中的故障會透過服務 B 串聯到服務 A，導致失去可用性。 

 根據硬相依性可用性方程式中的乘法因數 (請參閱 [https://docs.aws.amazon.com/wellarchitected/latest/reliability-pillar/availability.html#dbedbedda68f9a15ACLX122](https://docs.aws.amazon.com/wellarchitected/latest/reliability-pillar/availability.html#dbedbedda68f9a15ACLX122))，C 可用性的任何下降都會嚴重影響 B 的有效可用性。透過傳回靜態回應，服務 B 可減輕 C 的故障；而且雖然降級，但使服務 C 的可用性看起來像 100% (假設它在錯誤情況下可靠地傳回靜態回應)。請注意，靜態回應是傳回錯誤的簡單替代方法，並不會嘗試以不同方式重新計算回應。這種以完全不同的機制來嘗試達到相同結果的嘗試稱為備用行為，並且是一種可避免的反模式。 

 適度降級的另一個範例是 *斷路器模式*。當故障是暫時性時，應該使用重試策略。如果情況並非如此，且操作可能會失敗，則斷路器模式會阻止用戶端執行可能失敗的請求。在正常處理請求時，斷路器合閘，請求流過。當遠端系統開始傳回錯誤或出現高延遲時，斷路器會開啟，並忽略相依性，或是將結果取代為更輕鬆取得但不完整的回應 (可能只是回應快取)。系統會定期嘗試呼叫該相依性，以確定其是否已復原。發生這種情況時，斷路器將閉合。 

![\[圖表：顯示處於開啟和關閉狀態的斷路器。\]](http://docs.aws.amazon.com/zh_tw/wellarchitected/2022-03-31/framework/images/circuit-breaker.png)


 除圖中所示的關閉和開啟狀態外，在可設定的期間後，斷路器可在開啟狀態下轉換為半開啟狀態。在此狀態下，它會定期嘗試以比平常低得多的速率來呼叫服務。此探查用於檢查服務的運作狀態。在半開啟狀態下成功數次之後，斷路器會轉換為關閉，並恢復正常請求。 

 **若未建立此最佳實務，暴露的風險等級：** 高 

## 實作指引
<a name="implementation-guidance"></a>
+  實作適度降級以將適用的硬相依性轉換為軟相依性。當元件的相依性狀況不良，元件本身仍可運作，但以降級的方式運作。例如，當相依性呼叫失敗時，容錯移轉為預先決定的靜態回應。 
  +  透過傳回靜態回應，您的工作負載可減輕在其相依性中發生的故障。
    +  [Well-Architected 實驗室：第 300 級：實作運作狀態檢查和管理相依性以提升可靠性](https://wellarchitectedlabs.com/Reliability/300_Health_Checks_and_Dependencies/README.html) 
  +  偵測重試操作何時可能失敗，並防止用戶端以斷路器模式進行失敗的呼叫。
    +  [CircuitBreaker](https://martinfowler.com/bliki/CircuitBreaker.html) 

## 資源
<a name="resources"></a>

 **相關文件：** 
+  [Amazon API Gateway：調節 API 請求以獲得更佳的輸送量](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-request-throttling.html) 
+  [CircuitBreaker (摘要說明「Release It\$1」書籍中的斷路器)](https://martinfowler.com/bliki/CircuitBreaker.html) 
+  [AWS 中的錯誤重試和指數退避](https://docs.aws.amazon.com/general/latest/gr/api-retries.html) 
+  [Michael Nygard「Release It\$1 設計和部署生產就緒型軟體」](https://pragprog.com/titles/mnee2/release-it-second-edition/) 
+  [Amazon Builders' Library：避免分散式系統的備用](https://aws.amazon.com/builders-library/avoiding-fallback-in-distributed-systems) 
+  [Amazon Builders' Library：避免無法逾越的佇列待辦項目](https://aws.amazon.com/builders-library/avoiding-insurmountable-queue-backlogs) 
+  [Amazon Builders' Library：快取挑戰和策略](https://aws.amazon.com/builders-library/caching-challenges-and-strategies/) 
+  [Amazon Builders' Library：逾時、重試、退避與抖動](https://aws.amazon.com/builders-library/timeouts-retries-and-backoff-with-jitter/) 

 **相關影片：** 
+  [重試、退避和抖動：AWS re:Invent 2019：Amazon Builders' Library 簡介 (DOP328)](https://youtu.be/sKRdemSirDM?t=1884) 

 **相關範例：** 
+  [Well-Architected 實驗室：第 300 級：實作運作狀態檢查和管理相依性以提升可靠性](https://wellarchitectedlabs.com/Reliability/300_Health_Checks_and_Dependencies/README.html) 

# REL05-BP02 調節請求
<a name="rel_mitigate_interaction_failure_throttle_requests"></a>

 調節請求是一種緩解模式，用於回應意外增加的需求。有些請求會接受，但超過定義限制的請求會遭到拒絕，並傳回訊息，指出它們已受到調節。預期用戶端會退避並放棄請求，或以較慢的速率再試一次。 

 您的服務應設計為處理每個節點或儲存格可以處理的已知請求容量。此容量可以透過負載測試來建立。然後，您需要追蹤請求的到達率，如果臨時到達率超過此限制，則適當的回應是發出訊號，指出已對其進行調節。其讓使用者可以進行重試，而重試可能具有可用容量的其他節點或儲存格。Amazon API Gateway 提供了調節請求的方法。Amazon SQS 和 Amazon Kinesis 可以緩衝請求、平滑請求率，以及減少對可非同步處理的請求進行調節的需求。 

 **若未建立此最佳實務，暴露的風險等級：** 高 

## 實作指引
<a name="implementation-guidance"></a>
+  調節請求。這是一種緩解模式，用於回應意外增加的需求。有些請求會接受，但超過定義限制的請求會遭到拒絕，並傳回訊息，指出它們已受到調節。預期用戶端會退避並放棄請求，或以較慢的速率再試一次。 
  +  使用 Amazon API Gateway 
    +  [調節 API 請求以獲得更佳的輸送量](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-request-throttling.html) 

## 資源
<a name="resources"></a>

 **相關文件：** 
+  [Amazon API Gateway：調節 API 請求以獲得更佳的輸送量](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-request-throttling.html) 
+  [AWS 中的錯誤重試和指數退避](https://docs.aws.amazon.com/general/latest/gr/api-retries.html) 
+  [Amazon Builders' Library：避免分散式系統的備用](https://aws.amazon.com/builders-library/avoiding-fallback-in-distributed-systems) 
+  [Amazon Builders' Library：避免無法逾越的佇列待辦項目](https://aws.amazon.com/builders-library/avoiding-insurmountable-queue-backlogs) 
+  [Amazon Builders' Library：逾時、重試、退避與抖動](https://aws.amazon.com/builders-library/timeouts-retries-and-backoff-with-jitter/) 
+  [調節 API 請求以獲得更佳的輸送量](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-request-throttling.html) 

 **相關影片：** 
+  [重試、退避和抖動：AWS re:Invent 2019：Amazon Builders' Library 簡介 (DOP328)](https://youtu.be/sKRdemSirDM?t=1884) 

# REL05-BP03 控制和限制重試呼叫
<a name="rel_mitigate_interaction_failure_limit_retries"></a>

 使用指數退避以在逐漸延長間隔後重試。引進抖動來隨機化這些重試間隔，並限制重試次數上限。 

 分散式軟體系統中的典型元件包括伺服器、負載平衡器、資料庫和 DNS 伺服器。在操作中，且操作可能失敗時，任何這些項目都可能開始產生錯誤。處理錯誤的預設技術是在用戶端實作重試。此技術可提高應用程式的可靠性和可用性。不過，如果用戶端在發生錯誤時立即嘗試重試失敗的操作，則網路很快就會因為新的和重試的請求而大規模地變得飽和，且每個請求都會爭奪網路頻寬。這可能會導致 *重試暴風，* 而該情況會降低服務的可用性。此模式可能會持續進行，直到整個系統出現故障為止。 

 若要避免這類情境，應該使用一般指數退避等 *退避演算法* 。指數退避演算法會逐漸降低執行重試的速率，從而避免網路擁塞。 

 許多開發套件和軟體程式庫 (包括來自 AWS 的程式庫) 都會實作這些演算法的一個版本。不過， **絕對不要假設退避演算法存在，請一律測試並驗證是否如此。** 

 單靠簡單退避是不夠的，因為在分散式系統中，所有用戶端都可能會同時退避，從而建立重試呼叫的叢集。Marc Brooker 在其部落格文章 [指數退避和抖動](https://aws.amazon.com/blogs/architecture/exponential-backoff-and-italics%0djitter/)，說明了如何修改指數退避中的 wait () 函數，以防止重試呼叫的叢集。解決 *方法* 是在 wait() 函數中新增抖動。為避免重試太久，實作時應該將退避上限設為最大值。 

 最後，請務必設定 *最大重試次數* 或經過時間，之後重試就會失敗。AWS 開發套件預設情況下可實作此功能，並可以對其進行設定。對於堆疊中較低的服務，最大重試限制為零或一時可限制風險，但仍然有效，因為重試會委派給堆疊中較高的服務。 

 **若未建立此最佳實務，暴露的風險等級：** 高 

## 實作指引
<a name="implementation-guidance"></a>
+  控制和限制重試呼叫。使用指數退避以在逐漸延長間隔後重試。引進抖動來隨機化這些重試間隔，並限制重試次數上限。 
  +  [AWS 中的錯誤重試和指數退避](https://docs.aws.amazon.com/general/latest/gr/api-retries.html) 
    + Amazon SDK 預設會實作重試和指數退避。呼叫自己的相依服務時，在相依性層中實作類似的邏輯。根據您的使用案例確定逾時時間以及何時停止重試。

## 資源
<a name="resources"></a>

 **相關文件：** 
+  [Amazon API Gateway：調節 API 請求以獲得更佳的輸送量](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-request-throttling.html) 
+  [AWS 中的錯誤重試和指數退避](https://docs.aws.amazon.com/general/latest/gr/api-retries.html) 
+  [Amazon Builders' Library：避免分散式系統的備用](https://aws.amazon.com/builders-library/avoiding-fallback-in-distributed-systems) 
+  [Amazon Builders' Library：避免無法逾越的佇列待辦項目](https://aws.amazon.com/builders-library/avoiding-insurmountable-queue-backlogs) 
+  [Amazon Builders' Library：快取挑戰和策略](https://aws.amazon.com/builders-library/caching-challenges-and-strategies/) 
+  [Amazon Builders' Library：逾時、重試、退避與抖動](https://aws.amazon.com/builders-library/timeouts-retries-and-backoff-with-jitter/) 

 **相關影片：** 
+  [重試、退避和抖動：AWS re:Invent 2019：Amazon Builders' Library 簡介 (DOP328)](https://youtu.be/sKRdemSirDM?t=1884) 

# REL05-BP04 快速失敗和限制佇列
<a name="rel_mitigate_interaction_failure_fail_fast"></a>

 如果工作負載無法成功回應請求，則快速失敗。如此將可釋放與請求關聯的資源，並且使服務在資源用盡時復原。如果工作負載能成功回應，但請求率太高，則改為使用佇列來緩衝請求。不過，請勿允許可能導致處理用戶端已放棄的過時請求之長佇列。 

 此最佳實務適用於該請求的伺服器端或接收者。 

 請注意，佇列可以在系統的多個層級建立，而且可能會嚴重阻礙快速復原的能力，因為較舊的過時請求 (不再需要回應) 在較新的請求之前處理。請注意佇列存在的位置。它們通常隱藏在記錄至資料庫的工作流程或工作中。 

 **若未建立此最佳實務，暴露的風險等級：** 高 

## 實作指引
<a name="implementation-guidance"></a>
+  快速失敗和限制佇列。如果工作負載無法成功回應請求，則快速失敗。如此將可釋放與請求關聯的資源，並且使服務在資源用盡時復原。如果工作負載能成功回應，但請求率太高，則改為使用佇列來緩衝請求。不過，請勿允許可能導致處理用戶端已放棄的過時請求之長佇列。 
  +  服務受壓時實作快速失敗。
    +  [快速失敗](https://www.martinfowler.com/ieeeSoftware/failFast.pdf) 
  +  限制佇列：在佇列式系統中，當處理停止但訊息持續送達時，待處理訊息可能大量積存，使得處理時間增加。工作可能太晚完成而無效，基本上會導致佇列要防範的可用性問題。
    +  [Amazon Builders' Library：避免無法逾越的佇列待辦項目](https://aws.amazon.com/builders-library/avoiding-insurmountable-queue-backlogs) 

## 資源
<a name="resources"></a>

 **相關文件：** 
+  [AWS 中的錯誤重試和指數退避](https://docs.aws.amazon.com/general/latest/gr/api-retries.html) 
+  [快速失敗](https://www.martinfowler.com/ieeeSoftware/failFast.pdf) 
+  [Amazon Builders' Library：避免分散式系統的備用](https://aws.amazon.com/builders-library/avoiding-fallback-in-distributed-systems) 
+  [Amazon Builders' Library：避免無法逾越的佇列待辦項目](https://aws.amazon.com/builders-library/avoiding-insurmountable-queue-backlogs) 
+  [Amazon Builders' Library：快取挑戰和策略](https://aws.amazon.com/builders-library/caching-challenges-and-strategies/) 
+  [Amazon Builders' Library：逾時、重試、退避與抖動](https://aws.amazon.com/builders-library/timeouts-retries-and-backoff-with-jitter/) 

 **相關影片：** 
+  [重試、退避和抖動：AWS re:Invent 2019：Amazon Builders' Library 簡介 (DOP328)](https://youtu.be/sKRdemSirDM?t=1884) 

# REL05-BP05 設定用戶端逾時
<a name="rel_mitigate_interaction_failure_client_timeouts"></a>

 適當設定逾時、系統性對其進行驗證，並且不要依賴預設值，因為它們通常設定得太高。 

 此最佳實務適用於請求的用戶端或寄件者。 

 針對任何遠端呼叫 (通常為跨程序的任何呼叫) 同時設定連線逾時和請求逾時。許多框架都提供內建的逾時功能，但請注意，許多框架都有無限或過高的預設值。太高的值會降低逾時的實用性，因為當用戶端等待逾時發生時，資源會持續耗用。太低的值可能會增加後端流量和延遲，原因是重試的請求過多。在某些情況下，這可能導致完全停機，原因是正在重試所有請求。 

 若要進一步了解 Amazon 如何透過抖動使用逾時、重試和退避功能，請參閱 [https://aws.amazon.com/builders-library/timeouts-retries-and-backoff-with-jitter/?did=ba_card&trk=ba_card](https://aws.amazon.com/builders-library/timeouts-retries-and-backoff-with-jitter/?did=ba_card&trk=ba_card)。 

 **若未建立此最佳實務，暴露的風險等級：** 高 

## 實作指引
<a name="implementation-guidance"></a>
+  針對任何遠端呼叫 (通常為跨程序的任何呼叫) 同時設定連線逾時和請求逾時。許多框架都提供內建的逾時功能，但請注意，許多框架都有無限或過高的預設值。太高的值會降低逾時的實用性，因為當用戶端等待逾時發生時，資源會持續耗用。太低的值可能會增加後端流量和延遲，原因是重試的請求過多。在某些情況下，這可能導致完全停機，原因是正在重試所有請求。 
  +  [AWS SDK：重試與逾時](https://docs.aws.amazon.com/sdk-for-net/v3/developer-guide/retries-timeouts.html) 

## 資源
<a name="resources"></a>

 **相關文件：** 
+  [AWS SDK：重試與逾時](https://docs.aws.amazon.com/sdk-for-net/v3/developer-guide/retries-timeouts.html) 
+  [Amazon API Gateway：調節 API 請求以獲得更佳的輸送量](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-request-throttling.html) 
+  [AWS 中的錯誤重試和指數退避](https://docs.aws.amazon.com/general/latest/gr/api-retries.html) 
+  [Amazon Builders' Library：逾時、重試、退避與抖動](https://aws.amazon.com/builders-library/timeouts-retries-and-backoff-with-jitter/) 

 **相關影片：** 
+  [重試、退避和抖動：AWS re:Invent 2019：Amazon Builders' Library 簡介 (DOP328)](https://youtu.be/sKRdemSirDM?t=1884) 

# REL05-BP06 盡可能讓服務無狀態
<a name="rel_mitigate_interaction_failure_stateless"></a>

 服務不應要求狀態，或應該卸載狀態，以便在不同的用戶端請求之間，不依賴磁碟上和記憶體中本機儲存的資料。這讓伺服器能夠任意置換，而不會對可用性造成影響。Amazon ElastiCache 或 Amazon DynamoDB 是卸載狀態的適當目的地。 

![\[在這個無狀態的 Web 應用程式中，工作階段狀態會卸載至 Amazon ElastiCache。\]](http://docs.aws.amazon.com/zh_tw/wellarchitected/2022-03-31/framework/images/stateless-webapp.png)


 當使用者或服務與應用程式互動時，他們通常會執行形成工作階段的一系列互動。工作階段是使用者在使用應用程式時，在不同請求之間持續存在的唯一資料。無狀態應用程式是一種不需要了解先前互動，也不會儲存工作階段資訊的應用程式。 

 一旦設計為無狀態，您就可以使用 AWS Lambda 或 AWS Fargate 等無伺服器運算服務。 

 除了伺服器替換，無狀態應用程式的另一個好處是它們可以水平擴展，因為任何可用的運算資源 (例如，EC2 執行個體和 AWS Lambda 函數) 都可以處理所有請求。 

 **若未建立此最佳實務，暴露的風險等級：** 中 

## 實作指引
<a name="implementation-guidance"></a>
+  讓您的應用程式無狀態。無狀態應用程式支援水平擴展，並且可以容忍單個節點的故障。 
  +  移除實際上可以儲存在請求參數中的狀態。
  +  在檢查了是否需要狀態之後，將狀態追蹤移至彈性多可用區域資料儲存，例如 Amazon ElastiCache、Amazon RDS、Amazon DynamoDB 或第三方分散式資料解決方案。儲存無法移動到彈性資料儲存的狀態。
    +  某些資料 (如 Cookie) 可以在標頭或查詢參數中傳遞。 
    +  重構以移除可以在請求中快速傳遞的狀態。
    +  某個請求可能實際上並不需要某些資料，這些資料可以隨需擷取。
    +  移除可以非同步擷取的資料。
    +  確定滿足所需狀態要求的資料儲存。 
    +  考慮將 NoSQL 資料庫用於非關聯式資料。

## 資源
<a name="resources"></a>

 **相關文件：** 
+  [Amazon Builders' Library：避免分散式系統的備用](https://aws.amazon.com/builders-library/avoiding-fallback-in-distributed-systems) 
+  [Amazon Builders' Library：避免無法逾越的佇列待辦項目](https://aws.amazon.com/builders-library/avoiding-insurmountable-queue-backlogs) 
+  [Amazon Builders' Library：快取挑戰和策略](https://aws.amazon.com/builders-library/caching-challenges-and-strategies/) 

# REL05-BP07 實作緊急控制桿
<a name="rel_mitigate_interaction_failure_emergency_levers"></a>

 緊急控制桿是可緩解對工作負載的可用性影響的快速程序。 

 **若未建立此最佳實務，暴露的風險等級：** 中 

## 實作指引
<a name="implementation-guidance"></a>
+  實作緊急控制桿。這是可緩解對工作負載的可用性影響的快速程序。它們可以在沒有根本原因的情況下操作。理想的緊急控制桿會提供完全決定性啟用和停用準則，將解析器的認知負擔降至零。控制桿通常是手動的，但也可以自動化 
  +  範例控制桿包括 
    +  封鎖所有機器人流量 
    +  提供靜態頁面而非動態頁面 
    +  減少對相依性的呼叫頻率 
    +  調節來自相依性的呼叫 
  +  實作和使用緊急控制桿的秘訣 
    +  當啟用控制桿時，請少做，而非多做 
    +  保持簡單，避免雙模式行為 
    +  定期測試您的控制桿 
  +  以下是非緊急控制桿動作的範例 
    +  新增容量 
    +  呼叫依賴您服務的用戶端服務擁有者，並要求他們減少呼叫 
    +  變更程式碼並將其釋出 