メニュー
アマゾン ウェブ サービス
全般的なリファレンス (Version 1.0)

AWS でのエラーの再試行とエクスポネンシャルバックオフ

特定のリクエストの処理中には、DNS サーバー、スイッチ、ロードバランサーなど、ネットワーク上のさまざまなコンポーネントが原因でエラーが発生する可能性があります。ネットワーク環境内でこれらのエラー応答を処理する一般的な手法としては、クライアントアプリケーションに再試行を実装する方法が挙げられます。この手法によってアプリケーションの信頼性が向上し、開発者のオペレーションコストが軽減されます。

各 AWS SDK は、自動再試行ロジックを実装しています。AWS SDK for Java は自動的にリクエストを再試行します。再試行は、ClientConfiguration クラスを使用して設定できます。たとえば、最小のレイテンシーで再試行なしでリクエストを実行するウェブページでは、再試行ロジックを停止させることがあります。再試行を無効にするには、ClientConfiguration クラスで 0 に値 maxErrorRetry を指定します。

AWS SDK を使用していない場合は、サーバーエラー (5xx) またはスロットリングエラーを受け取る元のリクエストを再試行する必要があります。ただし、クライアントエラー (4xx) は、再試行する前にリクエストを修正して問題を解決する必要があることを示しています。

単純な再試行に加えて、各 AWS SDK は効果的なフロー制御を行うために、エクスポネンシャルバックオフアルゴリズムを実装します。エクスポネンシャルバックオフは、再試行間の待機時間を累進的に長くして、連続的なエラー応答を受信するという考えに基づいています。最大遅延間隔で最大回数の再試行を実行する必要があります。再試行の最大遅延間隔と最大回数は必ずしも固定値ではなく、実行する操作や局所的な要因(ネットワークのレイテンシーなど)に基づいて設定する必要があります。

ほとんどのエクスポネンシャルバックオフアルゴリズムは、衝突の連続を防ぐためにジッター(ランダム化された遅延)を使用します。この場合は、そうした衝突を回避しようとしていないので、乱数を使用する必要はありません。ただし、同時クライアントを使用すると、ジッターはリクエストを迅速に処理する助けになります。詳細については、「Exponential Backoff and Jitter」のブログ投稿を参照してください。

次の擬似コードは、増分遅延を使用して状態をポーリングする 1 つの方法を示しています。

Do some asynchronous operation.

retries = 0

DO
    wait for (2^retries * 100) milliseconds

    status = Get the result of the asynchronous operation.

    IF status = SUCCESS
        retry = false
    ELSE IF status = NOT_READY
        retry = true
    ELSE IF status = THROTTLED
        retry = true
    ELSE
        Some other error occurred, so stop calling the API.
        retry = false
    END IF

    retries = retries + 1

WHILE (retry AND (retries < MAX_RETRIES))

次のコードは、Java でこの増分遅延を実行する方法を示しています。

public enum Results {
    SUCCESS, 
    NOT_READY, 
    THROTTLED, 
    SERVER_ERROR
}

/*
 * Performs an asynchronous operation, then polls for the result of the
 * operation using an incremental delay.
 */
public static void doOperationAndWaitForResult() {

    try {
        // Do some asynchronous operation.
        long token = asyncOperation();

        int retries = 0;
        boolean retry = false;

        do {
            long waitTime = Math.min(getWaitTimeExp(retries), MAX_WAIT_INTERVAL);

            System.out.print(waitTime + "\n");

            // Wait for the result.
            Thread.sleep(waitTime);

            // Get the result of the asynchronous operation.
            Results result = getAsyncOperationResult(token);

            if (Results.SUCCESS == result) {
                retry = false;
            } else if (Results.NOT_READY == result) {
                retry = true;
            } else if (Results.THROTTLED == result) {
                retry = true;
            } else if (Results.SERVER_ERROR == result) {
                retry = true;
            }
            else {
                // Some other error occurred, so stop calling the API.
                retry = false;
            }

        } while (retry && (retries++ < MAX_RETRIES));
    }

    catch (Exception ex) {
    }
}

/*
 * Returns the next wait interval, in milliseconds, using an exponential
 * backoff algorithm.
 */
public static long getWaitTimeExp(int retryCount) {

    long waitTime = ((long) Math.pow(2, retryCount) * 100L);

    return waitTime;
}