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
相关产品推荐
相关产品推荐

