翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。
バックオフパターンで再試行
Intent
バックオフによる再試行のパターンでは、一時的なエラーによって失敗したオペレーションを透過的に再試行して、アプリケーションの安定性を向上させます。
導入する理由
分散アーキテクチャでは、サービススロットリング、ネットワーク接続の一時的な喪失、一時的なサービス利用不可などによって、一時的なエラーが発生する可能性があります。こうした一時的なエラーによって失敗したオペレーションを自動的に再試行すると、ユーザーエクスペリエンスやアプリケーションのレジリエンスが向上します。しかし、頻繁な再試行は、ネットワーク帯域幅への過負荷や、競合を招きかねません。エクスポネンシャルバックオフという手法を使用すると、指定した回数だけ再試行の待機時間を延長し、オペレーションを再試行できます。
適用対象
バックオフによる再試行のパターンは次の場合に使用します。
-
サービス側で頻繁にリクエストをスロットリングしてオーバーロードを防いでいるため、呼び出しプロセスで「429 Too many requests」の例外が発生している。
-
分散アーキテクチャでは、ネットワークの動作が視野から外れやすいため、一時的なネットワークの問題によって障害が発生している。
-
呼び出しているサービスが一時的に使用できず、障害が発生している。このパターンを使用してバックオフタイムアウトを導入しない限り、頻繁な再試行がサービス低下を招く可能性がある。
問題点と考慮事項
-
べき等性: あるメソッドを複数回呼び出したときの効果と、あるシステム状態を 1 回呼び出したときの効果が同じ場合、そのオペレーションにはべき等性があると見なされます。バックオフによる再試行のパターンを使用する場合、そのオペレーションは、べき等でなければなりません。そうでない場合は、部分的な更新によってシステム状態が損なわれる可能性があります。
-
ネットワーク帯域幅: 再試行回数が多すぎ、ネットワーク帯域幅が占有されると、応答が遅延するようになり、サービス品質が低下する可能性があります。
-
フェイルファストのシナリオ: 一時的でないエラーの原因を特定できる場合は、サーキットブレーカーパターンを使用してフェイルファストを実践した方が効率的です。
-
バックオフレート: エクスポネンシャルバックオフを導入すると、サービスのタイムアウトに影響が及ぶため、エンドユーザーの待ち時間が長くなる可能性があります。
実装
高レベルのアーキテクチャ
次の図は、サービス A が、正常なレスポンスが返るまでサービス B への呼び出しを再試行する方法を示しています。数回試行しても、サービス B から成功のレスポンスが返らない場合、サービス A は再試行を停止し、呼び出し元に失敗のステータスを返すことができます。
AWS のサービスを使用した実装
次の図は、カスタマーサポートプラットフォームでのチケット処理ワークフローを示しています。不満を持つ顧客からのチケットは、チケットの優先度を自動的に上げ、その対応を迅速化します。Ticket info Lambda 関数はチケットの詳細を抽出し、Get sentiment Lambda 関数を呼び出します。Get sentiment Lambda 関数は、Amazon Comprehend
Get sentiment Lambda 関数の呼び出しが失敗した場合、オペレーションが 3 回再試行されます。エクスポネンシャルバックオフバックオフを有効にするには、AWS Step Functions でバックオフ値を設定します。
この例では、最大 3 回の再試行を 1.5 秒の増加乗数で設定しています。最初の再試行を 3 秒後に行う場合、2 回目の再試行は 3 x 1.5 秒 = 4.5 秒後に、3 回目の再試行は 4.5 x 1.5 秒 = 6.75 秒後に行われます。3 回目の再試行が失敗すると、ワークフローは失敗します。バックオフロジックにはカスタムコードは不要で、AWS Step Functions の設定項目を利用します。
サンプルコード
次のコードは、バックオフによる再試行のパターンの実装を示しています。
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 リポジトリ
このパターンのサンプルアーキテクチャの完全な実装については、https://github.com/aws-samples/retry-with-backoff
関連情報
-
ジッターを伴うタイムアウト、再試行、およびバックオフ
(Amazon Builders' Library)