Key Management Service (KMS) の API 操作を呼び出すと、エラーが返される場合があります。このトピックでは、失敗したリクエストを再試行するために指数バックオフ方式を使用する方法について説明します。
背景情報
API 操作の呼び出し時にエラーが発生した場合、アプリケーションでリクエストを再試行できます。
一部の Alibaba Cloud SDK は自動再試行をサポートしています。たとえば、.NET 用の Alibaba Cloud SDK の自動再試行ポリシーを設定できます。SDK が自動再試行をサポートしていない場合は、このトピックで説明されている方法を使用できます。
再試行方法
サーバーエラー (5xx) が返された場合、またはリクエストがスロットリングのために拒否された場合は、次のいずれかの方法を使用できます。
単純な再試行
指定された期間内に一定の間隔でリクエストを再試行します。
指数バックオフ
連続したエラーの場合、最大バックオフ時間と最大再試行回数に基づいて、再試行間の待機時間を指数関数的に増加させます。この方法は、再試行プロセス中に返される一定のエラーを防ぐのに役立ちます。たとえば、この方法を使用して、スロットリングのために拒否されたリクエストを再試行できます。これにより、短期間に返されるスロットリングエラーの数が減少します。
重要専用ゲートウェイは、API 呼び出しの数に上限を設けず、インスタンスの計算リソースとストレージリソースを使用して、できるだけ多くの API 呼び出しを処理します。したがって、スロットリングエラーは発生しません。
指数バックオフの擬似コード
次の擬似コードは、再試行間の待機時間を指数関数的に増加させる方法を示しています。
initialDelay = 200
retries = 0
DO
wait for (2^retries * initialDelay) milliseconds // (2のretries乗 * initialDelay)ミリ秒待機します
status = CallSomeAPI()
IF status == SUCCESS
retry = false // 成功したので、API の呼び出しを停止します。
ELSE IF status = THROTTLED || status = SERVER_NOT_READY
retry = true // スロットリングまたはサーバービジーのため失敗したので、再試行します。
ELSE
retry = false // その他のエラーが発生したので、API の呼び出しを停止します。
END IF
retries = retries + 1
WHILE (retry AND (retries < MAX_RETRIES))KMS で指数バックオフ方式を使用してスロットリングエラーを処理する
次の Java コードは、Decrypt 操作の呼び出し時に返されるスロットリングエラーを処理するために指数バックオフ方式を使用する方法を示しています。
コードを簡単に変更して、HTTP エラー 503 など、特定のサーバーエラーを処理できます。
クライアントが特定の期間内に開始するリクエスト数を推定できます。次に、推定結果に基づいて、コード内の初期遅延 (
initialDelay) と最大再試行回数 (maxRetries) を調整します。
Alibaba Cloud アカウントの AccessKey ペアは、すべての API 操作に対する権限を持っています。AccessKey ペアを使用して操作を実行することは、リスクの高い操作です。API 操作の呼び出しや日常的な O&M の実行には、RAM ユーザーを使用することをお勧めします。プロジェクトコードに AccessKey ID と AccessKey Secret を保存しないことをお勧めします。保存すると、AccessKey ペアが漏洩し、アカウントに属するすべてのリソースのセキュリティが侵害される可能性があります。
この例では、身元認証を実装するために、AccessKey ペアは ALIBABA_CLOUD_ACCESS_KEY_ID 環境変数と ALIBABA_CLOUD_ACCESS_KEY_SECRET 環境変数に保存されています。
認証情報を設定する方法の詳細については、「アクセス認証情報を管理する」をご参照ください。
環境変数を設定するために使用される方法は、オペレーティングシステムによって異なります。詳細については、「Linux、macOS、および Windows で環境変数を設定する」をご参照ください。
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.http.FormatType;
import com.aliyuncs.http.HttpClientConfig;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.http.ProtocolType;
import com.aliyuncs.kms.model.v20160120.DecryptRequest;
import com.aliyuncs.kms.model.v20160120.DecryptResponse;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
public class CmkDecrypt {
private static DefaultAcsClient kmsClient;
private static DefaultAcsClient kmsClient(String regionId, String accessKeyId, String accessKeySecret) {
IClientProfile profile = DefaultProfile.getProfile(regionId, accessKeyId, accessKeySecret);
HttpClientConfig clientConfig = HttpClientConfig.getDefault();
profile.setHttpClientConfig(clientConfig);
return new DefaultAcsClient(profile);
}
private static String kmsDecrypt(String cipherTextBlob) throws ClientException {
final DecryptRequest request = new DecryptRequest();
request.setSysProtocol(ProtocolType.HTTPS);
request.setAcceptFormat(FormatType.JSON);
request.setSysMethod(MethodType.POST);
request.setCiphertextBlob(cipherTextBlob);
DecryptResponse response = kmsClient.getAcsResponse(request);
return response.getPlaintext();
}
public static long getWaitTimeExponential(int retryCount) {
final long initialDelay = 200L;
long waitTime = ((long) Math.pow(2, retryCount) * initialDelay);
return waitTime;
}
public static void main(String[] args) {
String regionId = "xxxxx"; //"cn-shanghai"
String accessKeyId = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID");
String accessKeySecret = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
String cipherTextBlob = "xxxxxx";
int maxRetries = 5;
kmsClient = kmsClient(regionId, accessKeyId, accessKeySecret);
for (int i = 0; i < maxRetries; i++) {
try {
String plainText = kmsDecrypt(cipherTextBlob);
return;
} catch (ClientException e) {
if (e.getErrCode().contains("Rejected.Throttling")) {// 再試行が必要
try {
Thread.sleep(getWaitTimeExponential(i + 1));
} catch (InterruptedException ignore) {
}
}
}
}
}
}