You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Retry自定义RetryPolicy仅重试一次问题求助(已设最大次数>1)

嘿,我完全懂你刚上手Spring Retry时遇到的这个糟心问题——明明把最大重试次数设得大于1,结果实际只重试了一次。我来帮你排查常见的坑,再给你一套能正常跑通的实现方案。

先捋捋可能的问题根源

你遇到的情况大概率是下面几个原因之一:

  • 自定义RetryPolicy没正确结合重试次数限制,比如只判断了HTTP 5xx状态码,却没校验当前尝试次数是否超过最大值
  • 对maxAttempts的理解有误:这个参数是「总尝试次数」,比如设3的话是1次初始调用+2次重试,不是单纯的重试次数
  • HTTP调用逻辑里没把5xx状态码转为异常抛出,导致RetryTemplate根本没感知到失败
  • RetryTemplate的配置没正确关联自定义重试策略

给你一套可运行的实现代码

第一步:定义封装5xx状态的自定义异常

首先我们需要把HTTP 5xx的情况转为可被RetryTemplate识别的异常:

public class HttpServerErrorException extends RuntimeException {
    private final int statusCode;

    public HttpServerErrorException(int statusCode, String message) {
        super(message);
        this.statusCode = statusCode;
    }

    public int getStatusCode() {
        return statusCode;
    }
}

第二步:实现结合次数控制的自定义重试策略

这里我们把「5xx状态码判断」和「最大重试次数限制」结合起来,避免只判断状态码忽略次数的问题:

public class Http5xxRetryPolicy implements RetryPolicy {
    private final int maxAttempts;
    private final SimpleRetryPolicy simpleRetryPolicy;

    // 构造方法传入总尝试次数
    public Http5xxRetryPolicy(int maxAttempts) {
        this.maxAttempts = maxAttempts;
        this.simpleRetryPolicy = new SimpleRetryPolicy(maxAttempts);
    }

    @Override
    public boolean canRetry(RetryContext context) {
        // 先判断是否还在允许的总尝试次数内
        if (!simpleRetryPolicy.canRetry(context)) {
            return false;
        }
        // 再判断是否是我们要处理的5xx异常
        Throwable lastThrowable = context.getLastThrowable();
        return lastThrowable instanceof HttpServerErrorException 
                && ((HttpServerErrorException) lastThrowable).getStatusCode() >= 500 
                && ((HttpServerErrorException) lastThrowable).getStatusCode() < 600;
    }

    @Override
    public RetryContext open(RetryContext parent) {
        return simpleRetryPolicy.open(parent);
    }

    @Override
    public void close(RetryContext context) {
        simpleRetryPolicy.close(context);
    }

    @Override
    public void registerThrowable(RetryContext context, Throwable throwable) {
        simpleRetryPolicy.registerThrowable(context, throwable);
    }
}

第三步:正确配置RetryTemplate Bean

这里我们把自定义策略和退避策略(可选,控制重试间隔)都配置好:

@Configuration
public class RetryConfig {

    @Bean("MyRetryTemplate")
    public RetryTemplate myRetryTemplate() {
        RetryTemplate retryTemplate = new RetryTemplate();

        // 设置总尝试次数为3(1次调用+2次重试)
        Http5xxRetryPolicy retryPolicy = new Http5xxRetryPolicy(3);
        retryTemplate.setRetryPolicy(retryPolicy);

        // 可选:添加固定间隔退避策略,每次重试间隔1秒
        FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();
        backOffPolicy.setBackOffPeriod(1000); // 单位:毫秒
        retryTemplate.setBackOffPolicy(backOffPolicy);

        return retryTemplate;
    }
}

第四步:在HTTP调用逻辑中触发重试

关键是要手动判断HTTP响应状态码,抛出我们定义的异常,让RetryTemplate感知到失败:

@Service
public class ExternalApiService {
    private final RestTemplate restTemplate;
    private final RetryTemplate myRetryTemplate;

    // 通过构造注入RetryTemplate
    public ExternalApiService(RestTemplate restTemplate, @Qualifier("MyRetryTemplate") RetryTemplate myRetryTemplate) {
        this.restTemplate = restTemplate;
        this.myRetryTemplate = myRetryTemplate;
    }

    public String callApi(String url) {
        return myRetryTemplate.execute(context -> {
            ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
            // 遇到5xx状态码就抛出异常,触发重试
            if (response.getStatusCode().is5xxServerError()) {
                throw new HttpServerErrorException(response.getStatusCodeValue(), 
                        "服务器错误:" + response.getStatusCode());
            }
            return response.getBody();
        });
    }
}

排查建议

如果还是有问题,可以在Http5xxRetryPolicy的canRetry方法里加日志,看看每次重试的判断逻辑:

@Override
public boolean canRetry(RetryContext context) {
    int totalAttempts = context.getRetryCount() + 1; // retryCount是已重试次数,加1是总尝试次数
    System.out.println("当前总尝试次数:" + totalAttempts);
    Throwable lastThrowable = context.getLastThrowable();
    if (lastThrowable != null) {
        System.out.println("捕获到的异常:" + lastThrowable.getMessage());
    }
    // 原判断逻辑...
}

这样就能清楚看到是次数到了上限,还是异常判断逻辑出了问题。

内容的提问来源于stack exchange,提问作者SkillsIndexOutOfBounds

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 07:08:53