當您調用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的方式來實現身分識別驗證為例。
更多認證資訊配置方式,請參見管理訪問憑據。
不同作業系統的環境變數配置方法不同,具體操作,請參見在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) {
}
}
}
}
}
}