ヒント
このコンテンツは、.NET Docs で入手できる、またはオフラインで読み取ることができる無料のダウンロード可能な PDF として入手できる、コンテナー化された .NET アプリケーションの電子ブックである .NET マイクロサービス アーキテクチャからの抜粋です。
前に説明したように、リモート サービスまたはリソースに接続しようとしたときに発生する可能性がある、復旧に可変の時間がかかる可能性があるエラーを処理する必要があります。 この種の障害を処理すると、アプリケーションの安定性と回復性が向上します。
分散環境では、低速なネットワーク接続やタイムアウトなどの一時的な障害や、リソースの応答が遅い場合、または一時的に使用できない場合に、リモート リソースとサービスの呼び出しが失敗する可能性があります。 これらの障害は通常、短時間で修正されます。堅牢なクラウド アプリケーションは、"再試行パターン" のような戦略を使用してそれらを処理するように準備する必要があります。
ただし、予期しないイベントが原因で障害が発生し、修正に時間がかかる場合もあります。 このような障害の重大度は、部分的な接続の損失からサービスの完全な不具合まで多岐にわたります。 このような状況では、成功する可能性が低い操作をアプリケーションが継続的に再試行するのは無意味な場合があります。
代わりに、操作が失敗したことを受け入れ、それに応じてエラーを処理するようにアプリケーションをコーディングする必要があります。
Http 再試行を不注意に使用すると、独自のソフトウェア内にサービス拒否 (DoS) 攻撃が発生する可能性があります。 マイクロサービスが失敗したり、実行速度が遅くなったりすると、複数のクライアントが失敗した要求を繰り返し再試行する可能性があります。 これは、障害が発生したサービスを対象とするトラフィックが指数関数的に増加する危険なリスクを生み出します。
そのため、試し続ける価値がないときに過剰な要求が停止するように、何らかの防御障壁が必要です。 その防御バリアは、まさにサーキット ブレーカーです。
サーキット ブレーカー パターンの目的は、"再試行パターン" とは異なります。 "再試行パターン" を使用すると、アプリケーションは操作が最終的に成功することを期待して操作を再試行できます。 サーキット ブレーカー パターンを使用すると、アプリケーションが失敗する可能性のある操作を実行できなくなります。 アプリケーションは、これら 2 つのパターンを組み合わせることができます。 ただし、再試行ロジックは、サーキット ブレーカーによって返される例外に対して機密性が高く、サーキット ブレーカーが障害が一時的ではないことを示す場合は再試行を中止する必要があります。
IHttpClientFactory
と Polly を使用してサーキット ブレーカー パターンを実装する
再試行を実装する場合と同様に、サーキット ブレーカーの推奨される方法は、Polly などの実績のある .NET ライブラリと、 IHttpClientFactory
とのネイティブ統合を利用することです。
IHttpClientFactory
送信ミドルウェア パイプラインにサーキット ブレーカー ポリシーを追加することは、IHttpClientFactory
を使用するときに既に持っているものに 1 つの増分コードを追加するのと同じくらい簡単です。
HTTP 呼び出しの再試行に使用されるコードに追加されるのは、次の増分コードに示すように、使用するポリシーの一覧にサーキット ブレーカー ポリシーを追加するコードだけです。
// Program.cs
var retryPolicy = GetRetryPolicy();
var circuitBreakerPolicy = GetCircuitBreakerPolicy();
builder.Services.AddHttpClient<IBasketService, BasketService>()
.SetHandlerLifetime(TimeSpan.FromMinutes(5)) // Sample: default lifetime is 2 minutes
.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>()
.AddPolicyHandler(retryPolicy)
.AddPolicyHandler(circuitBreakerPolicy);
AddPolicyHandler()
メソッドは、使用するHttpClient
オブジェクトにポリシーを追加する方法です。 この場合、サーキット ブレーカーの Polly のポリシーを追加しています。
よりモジュール化されたアプローチを実現するために、サーキット ブレーカー ポリシーは、次のコードに示すように、 GetCircuitBreakerPolicy()
と呼ばれる別のメソッドで定義されます。
// also in Program.cs
static IAsyncPolicy<HttpResponseMessage> GetCircuitBreakerPolicy()
{
return HttpPolicyExtensions
.HandleTransientHttpError()
.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30));
}
上記のコード例では、サーキット ブレーカー ポリシーは、Http 要求を再試行するときに連続して 5 つの障害が発生したときに回線を中断または開くように構成されています。 その場合、回線は 30 秒間中断されます。その期間中、呼び出しは実際に配置されるのではなく、サーキット ブレーカーによって直ちに失敗します。 このポリシーでは、 関連する例外と HTTP 状態コード がエラーとして自動的に解釈されます。
また、HTTP 呼び出しを実行しているクライアント アプリケーションまたはサービスとは異なる環境にデプロイされている特定のリソースに問題がある場合は、サーキット ブレーカーを使用してフォールバック インフラストラクチャに要求をリダイレクトする必要があります。 そうすることで、バックエンド マイクロサービスのみに影響を与えるが、クライアント アプリケーションには影響しないデータセンターで障害が発生した場合、クライアント アプリケーションはフォールバック サービスにリダイレクトできます。 Polly は、このフェールオーバー ポリシー シナリオを自動化するための新しい ポリシー を計画しています。
これらの機能はすべて、場所の透明性を備えた Azure によって自動的に管理されるのではなく、.NET コード内からフェールオーバーを管理している場合に使用されます。
使用の観点から見ると、HttpClient を使用する場合は、前のセクションで示したように、コードは HttpClient
でIHttpClientFactory
を使用する場合と同じであるため、ここで新しいものを追加する必要はありません。
eShopOnContainers で Http 再試行とサーキット ブレーカーをテストする
Docker ホストで eShopOnContainers ソリューションを起動するたびに、複数のコンテナーを開始する必要があります。 一部のコンテナーは、SQL Server コンテナーのように、起動と初期化に時間がかかります。 これは特に、イメージとデータベースを設定する必要があるため、eShopOnContainers アプリケーションを Docker に初めてデプロイする場合に当てはまります。 コンテナー間の依存関係をdocker-composeレベルで設定した場合でも、一部のコンテナーが他のコンテナーよりも起動が遅くなることがあります。このため、前のセクションで説明したように、残りのサービスが最初にHTTP例外を発生させる可能性があります。 コンテナー間の docker-compose 依存関係は、プロセス レベルに過ぎません。 コンテナーのエントリ ポイント プロセスが開始される可能性がありますが、SQL Server がクエリの準備ができていない可能性があります。 結果はエラーの連鎖になり、アプリケーションはその特定のコンテナーを使用しようとしたときに例外を取得できます。
また、アプリケーションがクラウドにデプロイされるときに、起動時にこの種類のエラーが表示される場合もあります。 その場合、オーケストレーターは、クラスターのノード間でコンテナーの数を分散するときに、コンテナーを 1 つのノードまたは VM から別のノード (つまり、新しいインスタンスを開始) に移動している可能性があります。
すべてのコンテナーを開始するときに "eShopOnContainers" がこれらの問題を解決する方法は、前に示した再試行パターンを使用することです。
eShopOnContainers でサーキット ブレーカーをテストする
回線を中断または開き、eShopOnContainers でテストする方法はいくつかあります。
1 つのオプションは、サーキット ブレーカー ポリシーで許容される再試行回数を 1 に減らし、ソリューション全体を Docker に再デプロイすることです。 1 回の再試行で、デプロイ中に HTTP 要求が失敗し、サーキット ブレーカーが開き、エラーが発生する可能性が高くなります。
もう 1 つのオプションは、 Basket マイクロサービスに実装されているカスタム ミドルウェアを使用することです。 このミドルウェアを有効にすると、すべての HTTP 要求がキャッチされ、状態コード 500 が返されます。 次のように、失敗した URI に対して GET 要求を行うことで、ミドルウェアを有効にすることができます。
GET http://localhost:5103/failing
この要求は、ミドルウェアの現在の状態を返します。 ミドルウェアが有効になっている場合、要求は状態コード 500 を返します。 ミドルウェアが無効になっている場合、応答はありません。GET http://localhost:5103/failing?enable
この要求により、ミドルウェアが有効になります。GET http://localhost:5103/failing?disable
この要求によってミドルウェアが無効になります。
たとえば、アプリケーションが実行されたら、任意のブラウザーで次の URI を使用して要求を行うことでミドルウェアを有効にすることができます。 注文マイクロサービスではポート 5103 が使用されることに注意してください。
http://localhost:5103/failing?enable
その後、図 8-5 に示すように、URI http://localhost:5103/failing
を使用して状態を確認できます。
図 8-5 "失敗" ASP.NET ミドルウェアの状態の確認 - この場合は無効です。
この時点で、Basket マイクロサービスは、呼び出すたびに状態コード 500 で応答します。
ミドルウェアが実行されたら、MVC Web アプリケーションからの注文を試すことができます。 要求が失敗するので、回路が開きます。
次の例では、MVC Web アプリケーションに注文を配置するためのロジックに catch ブロックがあることがわかります。 コードがオープン回線の例外をキャッチすると、待機するようにユーザーに指示するわかりやすいメッセージが表示されます。
public class CartController : Controller
{
//…
public async Task<IActionResult> Index()
{
try
{
var user = _appUserParser.Parse(HttpContext.User);
//Http requests using the Typed Client (Service Agent)
var vm = await _basketSvc.GetBasket(user);
return View(vm);
}
catch (BrokenCircuitException)
{
// Catches error when Basket.api is in circuit-opened mode
HandleBrokenCircuitException();
}
return View();
}
private void HandleBrokenCircuitException()
{
TempData["BasketInoperativeMsg"] = "Basket Service is inoperative, please try later on. (Business message due to Circuit-Breaker)";
}
}
概要を次に示します。 再試行ポリシーは、HTTP 要求を複数回試行し、HTTP エラーを取得します。 再試行回数がサーキット ブレーカー ポリシーに設定された最大数 (この場合は 5) に達すると、アプリケーションは BrokenCircuitException をスローします。 結果は、図 8-6 に示すようにわかりやすいメッセージになります。
図 8-6. エラーを UI に返すサーキット ブレーカー
回線を開く/中断するタイミングに応じて、さまざまなロジックを実装できます。 または、フォールバック データセンターまたは冗長バックエンド システムがある場合は、別のバックエンド マイクロサービスに対して HTTP 要求を試すことができます。
最後に、 CircuitBreakerPolicy
のもう 1 つの可能性は、 Isolate
(強制的に回路を開いて保持する) と Reset
(もう一度閉じる) を使用することです。 これらは、ポリシーで直接分離とリセットを呼び出すユーティリティ HTTP エンドポイントを構築するために使用できます。 このような HTTP エンドポイントは、アップグレードする場合など、ダウンストリーム システムを一時的に分離するために運用環境で、適切にセキュリティで保護された状態で使用することもできます。 または、障害が発生していると思われるダウンストリーム システムを保護するために、回線を手動でトリップすることもできます。
その他のリソース
.NET