

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

# 斷路器模式
<a name="circuit-breaker"></a>

## 意圖
<a name="circuit-breaker-intent"></a>

當呼叫先前造成重複逾時或失敗時，斷路器模式可防止發起人服務重試對其他服務 (*來電者*) 的呼叫。模式也會用來偵測受話方服務何時再次運作。

## 動機
<a name="circuit-breaker-motivation"></a>

當多個微服務協同處理請求時，一或多個服務可能會變得無法使用或呈現高延遲。當複雜的應用程式使用微服務時，一個微服務中的中斷可能會導致應用程式失敗。微服務會透過遠端程序呼叫進行通訊，而網路連線中可能發生暫時性錯誤，進而導致失敗。（暫時性錯誤可以透過[使用具有退避模式的重試](retry-backoff.md)來處理。) 在同步執行期間，逾時或失敗的層疊可能會導致使用者體驗不佳。

不過，在某些情況下，故障可能需要更長的時間才能解決，例如，當受話方服務或資料庫爭用導致逾時時。在這種情況下，如果呼叫服務重複重試呼叫，這些重試可能會導致網路爭用和資料庫執行緒集區耗用。此外，如果多個使用者重複重試應用程式，這會使問題惡化，並可能導致整個應用程式的效能降低。

Michael Nygard 在他的書籍 *Release It* (Nygard 2018) 中熱門斷路器模式。此設計模式可防止發起人服務重試先前導致重複逾時或失敗的服務呼叫。它也可以偵測受話方服務何時再次運作。

斷路器物件的運作方式類似斷路器，會在電路異常時自動中斷目前的 。發生故障時，斷路器會關閉或觸發目前的流程。同樣地，斷路器物件位於發起人和受話方服務之間，如果受話方無法使用，則會跳轉。

[分散式運算的缺點](https://en.wikipedia.org/wiki/Fallacies_of_distributed_computing)是 Peter Deutsch 和其他在 Sun Microsystems 所做的一組聲明。他們說初次接觸分散式應用程式的程式設計人員，一定會做出錯誤的假設。網路可靠性、零延遲預期和頻寬限制會導致軟體應用程式以最少的錯誤處理方式寫入網路錯誤。

在網路中斷期間，應用程式可能會無限期等待回覆並持續使用應用程式資源。在網路可用時未重試操作也可能導致應用程式降級。如果 API 因為網路問題而呼叫資料庫或外部服務逾時，沒有斷路器的重複呼叫可能會影響成本和效能。

## 適用性
<a name="circuit-breaker-applicability"></a>

在下列情況下使用此模式：
+ 呼叫者服務進行最有可能失敗的呼叫。
+ 受話方服務呈現的高延遲 （例如，當資料庫連線緩慢時） 會導致呼叫者服務逾時。
+ 呼叫者服務會進行同步呼叫，但呼叫者服務無法使用或顯示高延遲。

## 問題和考量
<a name="circuit-breaker-issues"></a>
+ **服務無關實作：**為了防止程式碼膨脹，建議您以微服務無關和 API 驅動的方式實作斷路器物件。
+ **受話方電路關閉：**當受話方從效能問題或失敗中復原時，他們可以將電路狀態更新為 `CLOSED`。這是斷路器模式的延伸，如果您的復原時間目標 (RTO) 需要，則可以實作。
+ **多執行緒呼叫：**過期逾時值定義為在再次路由呼叫之前，電路保持跳閘的期間，以檢查服務可用性。在多個執行緒中呼叫受話方服務時，失敗的第一個呼叫會定義過期逾時值。您的實作應確保後續呼叫不會無限移動過期逾時。
+ **強制開啟或關閉電路：**系統管理員應該能夠開啟或關閉電路。這可以透過更新資料庫資料表中的過期逾時值來完成。
+ 可**觀測性：**應用程式應設定記錄，以識別斷路器開啟時失敗的呼叫。

## 實作
<a name="circuit-breaker-implementation"></a>

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

在下列範例中，發起人是訂單服務，來電者是付款服務。

當沒有失敗時，訂單服務會將斷路器的所有呼叫路由至付款服務，如下圖所示。

![\[沒有故障的斷路器模式。\]](http://docs.aws.amazon.com/zh_tw/prescriptive-guidance/latest/cloud-design-patterns/images/circuit-breaker-1.png)


如果付款服務逾時，斷路器可以偵測逾時並追蹤失敗。

![\[具有付款服務失敗的斷路器。\]](http://docs.aws.amazon.com/zh_tw/prescriptive-guidance/latest/cloud-design-patterns/images/circuit-breaker-2.png)


如果逾時超過指定的閾值，應用程式會開啟電路。當電路開啟時，斷路器物件不會將呼叫路由到付款服務。當訂單服務呼叫付款服務時，它會傳回立即失敗。

![\[斷路器會停止路由至付款服務。\]](http://docs.aws.amazon.com/zh_tw/prescriptive-guidance/latest/cloud-design-patterns/images/circuit-breaker-3.png)


斷路器物件會定期嘗試查看對付款服務的呼叫是否成功。

![\[斷路器會定期重試付款服務。\]](http://docs.aws.amazon.com/zh_tw/prescriptive-guidance/latest/cloud-design-patterns/images/circuit-breaker-4.png)


當呼叫付款服務成功時，電路會關閉，所有進一步的呼叫都會再次路由到付款服務。

![\[具有工作付款服務的斷路器。\]](http://docs.aws.amazon.com/zh_tw/prescriptive-guidance/latest/cloud-design-patterns/images/circuit-breaker-5.png)


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

範例解決方案使用 中的快速工作流程[AWS Step Functions](https://aws.amazon.com/step-functions/)來實作斷路器模式。Step Functions 狀態機器可讓您設定模式實作所需的重試功能和決策型控制流程。

解決方案也會使用 [Amazon DynamoDB](https://aws.amazon.com/dynamodb/) 資料表做為資料存放區來追蹤電路狀態。這可以替換為記憶體內資料存放區，例如 [Amazon ElastiCache (Redis OSS)](https://aws.amazon.com/elasticache/redis/)，以獲得更好的效能。

當服務想要呼叫另一個服務時，它會以受話方服務的名稱啟動工作流程。工作流程會從 DynamoDB `CircuitStatus`資料表取得斷路器狀態，該資料表存放目前降級的服務。如果 `CircuitStatus`包含受話方的未過期記錄，表示電路已開啟。Step Functions 工作流程會傳回立即失敗並結束 `FAIL` 狀態。

如果`CircuitStatus`資料表不包含受話方的記錄或包含過期的記錄，則服務會正常運作。狀態機器定義的`ExecuteLambda`步驟會呼叫透過參數值傳送的 Lambda 函數。如果呼叫成功，Step Functions 工作流程會以 `SUCCESS` 狀態結束。

![\[使用 AWS Step Functions 和 DynamoDB 實作斷路器。\]](http://docs.aws.amazon.com/zh_tw/prescriptive-guidance/latest/cloud-design-patterns/images/circuit-breaker-6.png)


如果服務呼叫失敗或發生逾時，應用程式會在定義的次數內以指數退避重試。如果服務呼叫在重試後失敗，工作流程會將記錄插入具有 的 服務`CircuitStatus`資料表中`ExpiryTimeStamp`，而工作流程會以 `FAIL` 狀態結束。只要斷路器開啟，對相同服務的後續呼叫就會立即傳回故障。狀態機器定義的`Get Circuit Status`步驟會根據 `ExpiryTimeStamp`值檢查服務可用性。過期的項目會使用 DynamoDB 存留時間 (TTL) 功能從`CircuitStatus`資料表中刪除。

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

下列程式碼使用 `GetCircuitStatus` Lambda 函數來檢查斷路器狀態。

```
var serviceDetails = _dbContext.QueryAsync<CircuitBreaker>(serviceName, QueryOperator.GreaterThan,
                new List<object>
                    {currentTimeStamp}).GetRemainingAsync();

if (serviceDetails.Result.Count > 0)
{
    functionData.CircuitStatus = serviceDetails.Result[0].CircuitStatus;
}
else
{
    functionData.CircuitStatus = "";
}
```

下列程式碼顯示 Step Functions 工作流程中的 Amazon States Language 陳述式。

```
"Is Circuit Closed": {
    "Type": "Choice",
    "Choices": [
    {
        "Variable": "$.CircuitStatus",
        "StringEquals": "OPEN",
        "Next": "Circuit Open"
    },
    {
        "Variable": "$.CircuitStatus",
        "StringEquals": "",
        "Next": "Execute Lambda"
    }
    ]
},
"Circuit Open": {
    "Type": "Fail"
}
```

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

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

## 部落格參考
<a name="circuit-breaker-blog"></a>
+ [搭配 AWS Step Functions 和 Amazon DynamoDB 使用斷路器模式](https://aws.amazon.com/blogs/compute/using-the-circuit-breaker-pattern-with-aws-step-functions-and-amazon-dynamodb/)

## 相關內容
<a name="circuit-breaker-resources"></a>
+ [Strangler 無花果模式](strangler-fig.md)
+ [使用輪詢重試模試](retry-backoff.md)