シミュレーター・スクリプト - backoff_simulator.py
さて、先ほど紹介したExponential Backoff And Jitterの解説記事の末尾で触れられていますが、この検証スクリプトは、GitHub上で公開されています。
当該Pythonスクリプトをダウンロード後、python backoff_simulator.pyを実行しますと、backoff_results.csvというCSVファイルが生成されます(グラフ化したい場合は、このCSVファイルをgnuplotなどのグラフ生成ツールで描画してください)。
また、擬似コードで表現されていた各Jitterのロジックですが、Pythonソースコードで確認することができます。
例えば、Exponential backoffのロジックは、以下のコードです。
def expo(self, n):
return min(self.cap, pow(2, n)*self.base)
次に、Full Jitterのロジックです。random.uniform(0, v)は、0からvまでの間の浮動小数点数を返します。
class ExpoBackoffFullJitter(Backoff):
def backoff(self, n):
v = self.expo(n)
return random.uniform(0, v)
Equal Jitterのロジックは、以下のコードです。
class ExpoBackoffEqualJitter(Backoff):
def backoff(self, n):
v = self.expo(n)
return v/2 + random.uniform(0, v/2)
最後にDecorrlated(無相関) Jitterのロジックです。self.expo(n)が使われていない、ただのランダムな待ち時間であることがお分かりいただけるでしょうか。
class ExpoBackoffDecorr(Backoff):
def __init__(self, base, cap):
Backoff.__init__(self, base, cap)
self.sleep = self.base
def backoff(self, n):
self.sleep = min(self.cap, random.uniform(self.base, self.sleep * 3))
return self.sleep
ぜひソースコード全体もご確認いただき、理解を深めていただきたいと思います。
リトライに適したシチュエーション
本記事の冒頭のシナリオでは、500番台のHTTPステータスコードを検知して、リトライを開始しました。これ以外のステータスコードの場合、振る舞いを変える必要があるでしょうか。リトライに適さないシチュエーションはあるでしょうか。
そこで、下表の通り、簡単にまとめてみました。
| HTTP Status Code | リトライの必要性 |
|---|---|
| 200(Success) | リトライ不要(正常にレスポンスを受け取っているため) |
| 3XX(Redirection) | リトライ不要(ステータスコードの指示通り追加処理不要) |
| 4XX(Client Error) | リトライ非推奨(クライアント側に原因があるため) |
| 503(Service Unavailable) | リトライ推奨 |
| 504(Gateway Timeout) | リトライ推奨 |
| その他の5XX(Server Error) | ケースバイケースだが、基本的にはリトライ推奨 |
まず、200番台や300番台は、Successであり、Redirectionを意味していますから、それぞれその指示に従うことになります。
400番台は、クライアント側に原因があることを示していますので、そのリクエスト自体が何度やっても成功しないということを意味しています。したがって、400番台の場合はリトライすべきではありません。
500番台は、サーバが負荷分散されている前提ではありますが、基本的にはリトライ対象とするということになります。503 Service Unavailableや504 Gateway Timeoutなどは、他のサーバにリクエストを向けることで成功する可能性があります。サーバから応答がかえってこず、クライアント側でタイムアウトとなるような場合も同様です。
一方で、500 Internal Server Errorの場合は、サーバ側アプリケーション全体の不具合やデプロイの失敗という可能性もあるかもしれません。ある程度リトライを行なったのち、エラーとしてクライアント側に処理させた方が都合がいい場合もあるでしょう。
スロットリングに関して
HTTPステータスコード別の基本的な振る舞いについて述べましたが、少し発展的な話題として、Web APIサービス側がスロットリングする場合を取り上げます。
スロットリングとは、負荷調整や悪意ある攻撃を防ぐことを目的として、Web APIサービス側で一定時間内に処理できるリクエスト数を制限する機能のことを言います。一般的には、"Rate Limiting"や"Rate Exceeded"などのエラーメッセージが返ってきます。
HTTPステータスコードに関して、記事執筆時点では、AWSでもサービスによってさまざまなステータスコードが返ってきます。AWS CLI の _retry.jsonをご参照いただくとお分かりいただけますが、400 Bad Requestを返すサービスが多い一方で、503 Service Unavailableや、509 Bandwidth Limit Exceededを返すサービスもあります。
RFCでは、2012年より429 Too Many Requestsというステータスコードが追加となっていますので、今後スロットリングを示す場合には、429 Too Many Requestsを返却するサービスが多くなるかもしれません。
スロットリングが発生した場合のクライアント側の対応は、一旦リクエストを中止して期間を十分置いてからの再実施が基本的な対応になると思います。エクスポネンシャル・バックオフ・アンド・ジッターを利用する場合でも、少し長めのリトライ間隔を設定しておいた方が望ましいでしょう。
中締め
本記事では、エクスポネンシャル・バックオフ・リトライについてご紹介しました。
AWS Solution Architectブログの記事「Exponential Backoff And Jitter」を題材に、さまざまなリトライのアルゴリズムとその効果について見てきました。実際のPythonソースコードについても抜粋しましたので、ぜひご参考いただき、みなさんがお使いの言語でもトライいただければと思います。また、リトライに適したシチュエーションとは何かについても最後に触れました。Web APIサーバでの事例を念頭におきましたので、HTTPステータスコードを題材に説明しましたが、みなさんが作成されるアプリケーションにも参考にしていただけるところがあったのではないかと思います。
次回以降、以下の2つについてご紹介します。
- 結果整合性(Eventual Consistency)
- 冪(べき)等性(Idempotency)
アマゾン ウェブ サービス ジャパン株式会社ではクラウドサポートエンジニアを絶賛募集中です。誰よりもAWSの仕組みを深く知り、また実際のAWSのユースケースを幅広く知ることができるとても刺激的な仕事です。
AWSサポートエンジニアに興味を持ってくださった方は、こちらから採用イベント(オープンハウス)への登録、もしくはポジションへのご応募をお願いいたします。
