全部产品
Search
文档中心

密钥管理服务:使用指数退避方法对请求错误进行重试

更新时间:Mar 10, 2026

当您调用KMS的API时,有时会返回错误信息。本文介绍了如何使用指数退避方法对请求错误进行重试。

背景信息

当您调用服务接口时,有时会在某一环节出现错误,此时您可以在应用程序中进行重试。

一些阿里云SDK支持通过配置,自动实现对请求的错误重试。例如:使用阿里云的.NET SDK可以配置重试的策略。当自动重试方式不适用时,您可以使用本文介绍的重试方法对请求错误进行重试。

重试策略

请求出现错误时,如果是服务器错误(5xx)或请求限流错误,则可以通过如下重试策略对请求错误进行重试:

  • 简单重试。

    例如:总共重试10秒,每秒重试一次。

  • 指数退避。

    对于连续错误响应,重试等待间隔越来越长,您需要按照最长延迟间隔和最大重试次数进行重试。指数退避可以防止在重试过程中持续不断地发生冲突。例如:在短时间内发出超过限流配额的请求时,通过指数退避的方式,可以有效规避持续的限流错误。

重要

专属网关对API请求不设置调用次数上限,而是采用“尽力而为”的方式,即最大限度地使用实例计算和存储资源来处理API请求,所以不会产生限流错误。

指数退避的伪代码

以下代码介绍了如何使用增量延迟方法重试某个操作。

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的方式来实现身份验证为例。

import com.aliyun.kms20160120.Client;
import com.aliyun.kms20160120.models.DecryptRequest;
import com.aliyun.kms20160120.models.DecryptResponse;
import com.aliyun.tea.*;
import com.aliyun.teautil.models.RuntimeOptions;

import java.nio.charset.StandardCharsets;

public class Main {
    private static  Client kmsClient;

    private static Client kmsClient(String regionId, String accessKeyId, String accessKeySecret) throws Exception {
        com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
            .setAccessKeyId(accessKeyId)
            .setAccessKeySecret(accessKeySecret);
        config.setRegionId(regionId) ;
    return new Client(config);
    }

    private static String kmsDecrypt(String cipherTextBlob) throws Exception {
        DecryptRequest decryptRequest = new DecryptRequest()
                // 设置待解密的密文。
                .setCiphertextBlob(cipherTextBlob);
        DecryptResponse response = kmsClient.decryptWithOptions(decryptRequest, new RuntimeOptions());
        // 从响应中获取解密后的明文。
        String plaintext = response.getBody().getPlaintext();

        System.out.println("Ciphertext (Base64): " + cipherTextBlob);
        System.out.println("Decrypted Plaintext: " + plaintext);
        return plaintext;
    }

    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) throws Exception {
        String regionId = "地域ID"; //"cn-shanghai"
        String accessKeyId = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID");
        String accessKeySecret = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
        String cipherTextBlob ="a2V5LXNoaDY4M**********w=";
        int maxRetries = 5;

        kmsClient =  kmsClient(regionId, accessKeyId, accessKeySecret);

        for (int i = 0; i < maxRetries; i++) {
            try {
                String plainText = kmsDecrypt(cipherTextBlob);
                return;
            } catch (Exception e) {
                e.printStackTrace();
                if (e.getMessage().contains("Rejected.Throttling")) {//need retry
                    try {
                        Thread.sleep(getWaitTimeExponential(i + 1));
                    } catch (InterruptedException ignore) {
                    }
                } else {
                    break; // 非限流错误,停止重试。
                }
            }
        }
    }
}