当您调用KMS的API时,有时会返回错误信息。本文介绍了如何使用指数退避方法对请求错误进行重试。
背景信息
当您调用服务接口时,有时会在某一环节出现错误,此时您可以在应用程序中进行重试。
一些阿里云SDK支持通过配置,自动实现对请求的错误重试。例如:使用阿里云的.NET SDK可以配置重试的策略。当自动重试方式不适用时,您可以使用本文介绍的重试方法对请求错误进行重试。
重试策略
请求出现错误时,如果是服务器错误(5xx)或请求限流错误,则可以通过如下重试策略对请求错误进行重试:
简单重试。
例如:总共重试10秒钟,每秒钟重试一次。
指数退避。
对于连续错误响应,重试等待间隔越来越长,您需要按照最长延迟间隔和最大重试次数进行重试。指数退避可以防止在重试过程中持续不断的发生冲突。例如:在短时间发出超过限流配额数的请求时,通过指数退避的方式,可以有效规避持续的限流错误。
指数退避的伪代码
以下代码介绍了如何使用增量延迟方法重试某个操作。
initialDelay = 200
retries = 0
DO
wait for (2^retries * initialDelay) milliseconds
status = CallSomeAPI()
IF status == SUCCESS
retry = false // Succeeded, stop calling the API again.
ELSE IF status = THROTTLED || status == SERVER_NOT_READY
retry = true // Failed because of throttling or server busy, try again.
ELSE
retry = false // Some other error occurred, stop calling the API again.
END IF
retries = retries + 1
WHILE (retry AND (retries < MAX_RETRIES))
使用指数退避方法处理KMS限流
以下Java示例介绍了如何使用指数退避的方式,处理KMS调用Decrypt接口时遇到的限流错误。
您可以通过简单修改,对特定类型的服务器错误(例如:HTTP 503)进行重试。
您可以通过精细的预估客户端在特定时间段内发出的请求数,调整初始延迟值(
initialDelay
)和重试次数(maxRetries
)。
阿里云账号AccessKey拥有所有OpenAPI的访问权限,建议您使用RAM用户进行API访问或日常运维。强烈建议不要把AccessKey ID和AccessKey Secret保存到工程代码里,否则可能导致AccessKey泄露,威胁您账号下所有资源的安全。
本示例以将AccessKey配置在环境变量ALIBABA_CLOUD_ACCESS_KEY_ID和ALIBABA_CLOUD_ACCESS_KEY_SECRET的方式来实现身份验证为例。
更多认证信息配置方式,请参见Credentials 设置。
不同操作系统的环境变量配置方法不同,具体操作,请参见在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")) {//need retry
try {
Thread.sleep(getWaitTimeExponential(i + 1));
} catch (InterruptedException ignore) {
}
}
}
}
}
}