本文目录导读:

针对Java接口重试流程的统一化,通常有三种主流方案,从简单到复杂,适合不同场景,我会帮你梳理出最实用、可落地的方案。
核心思路:统一重试流程的要素
一个完整的重试流程包含:
- 触发条件:什么异常需要重试?
- 重试策略:重试次数、间隔时间、退避算法
- 终止条件:最大重试次数、超时时间
- 监控与日志:每次重试的记录
- 幂等性保障:重试不会产生副作用
方案对比与选择
| 方案 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| Spring Retry | Spring Boot项目,简单重试 | 注解驱动,使用简单 | 功能有限,不适合复杂策略 |
| Guava Retryer | 非Spring项目,中等复杂度 | 灵活的策略配置 | 需要手动集成 |
| Resilience4j | 微服务架构,高并发场景 | 功能全面,支持熔断/限流 | 学习曲线稍高 |
实战方案详解
方案1:Spring Retry(推荐Spring项目使用)
POM依赖:
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
启用配置:
@Configuration
@EnableRetry
public class RetryConfig {
// 启用Spring Retry
}
核心代码 - 统一注解使用:
@Service
@Slf4j
public class OrderService {
@Retryable(
value = {RemoteCallException.class, TimeoutException.class},
maxAttempts = 3,
backoff = @Backoff(delay = 1000, multiplier = 1.5, maxDelay = 5000)
)
@Recover
public OrderResponse createOrder(OrderRequest request) {
log.info("创建订单,第{}次重试",
RetryContext.getContext().getRetryCount());
// 实际的远程调用
return remoteOrderApi.create(request);
}
/**
* 所有重试失败后的降级方法
* 返回值必须与原方法一致,参数可多一个Throwable
*/
@Recover
public OrderResponse recover(RemoteCallException e, OrderRequest request) {
log.error("创建订单失败,已重试3次", e);
// 返回降级结果,比如缓存数据或默认值
return OrderResponse.fallback(request);
}
}
优点:与Spring深度集成,使用注解非常简洁。
方案2:Guava Retryer(适合非Spring项目)
POM依赖:
<dependency>
<groupId>com.github.rholder</groupId>
<artifactId>guava-retrying</artifactId>
<version>2.0.0</version>
</dependency>
核心代码 - 统一重试模板:
@Component
public class RetryTemplate {
private static final Retryer<Object> retryer = RetryerBuilder.<Object>newBuilder()
// 触发条件:只重试指定的异常
.retryIfExceptionOfType(IOException.class)
.retryIfRuntimeException()
// 重试策略:指数退避,最多3次
.withWaitStrategy(WaitStrategies.exponentialWait(1000, 5000, TimeUnit.MILLISECONDS))
.withStopStrategy(StopStrategies.stopAfterAttempt(3))
// 监听器:记录日志
.withRetryListener(new RetryListener() {
@Override
public <V> void onRetry(Attempt<V> attempt) {
if (attempt.hasException()) {
log.warn("重试第{}次,异常:{}",
attempt.getAttemptNumber(),
attempt.getExceptionCause().getMessage());
} else {
log.info("第{}次重试成功", attempt.getAttemptNumber());
}
}
})
.build();
/**
* 统一的重试执行方法
*/
public <T> T execute(Callable<T> callable) {
try {
return (T) retryer.call(callable);
} catch (ExecutionException | RetryException e) {
throw new RuntimeException("重试全部失败", e);
}
}
}
使用示例:
@Service
public class PaymentService {
@Autowired
private RetryTemplate retryTemplate;
public PaymentResponse pay(PaymentRequest request) {
return retryTemplate.execute(() -> {
// 实际的远程调用逻辑
return paymentClient.pay(request);
});
}
}
方案3:Resilience4j(微服务首选,最完善)
POM依赖:
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot2</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
application.yml配置:
resilience4j.retry:
instances:
paymentService:
maxAttempts: 3
waitDuration: 1s
exponentialBackoffMultiplier: 1.5
retryExceptions:
- java.io.IOException
- com.example.TimeoutException
ignoreExceptions:
- com.example.BusinessException # 业务异常不重试
核心代码 - 统一使用:
@Service
@Slf4j
public class PaymentService {
@Retry(name = "paymentService", fallbackMethod = "paymentFallback")
public PaymentResponse processPayment(PaymentRequest request) {
// 实际的支付调用
return paymentGateway.pay(request);
}
// 降级方法(参数顺序:原方法参数 + Throwable)
public PaymentResponse paymentFallback(PaymentRequest request, Throwable t) {
log.error("支付失败,已重试3次,请求ID:{}", request.getOrderId(), t);
return PaymentResponse.failed("支付服务暂时不可用");
}
}
高级用法 - 组合重试+熔断+限流:
@Service
public class OrderService {
@Retry(name = "orderService")
@CircuitBreaker(name = "orderService", fallbackMethod = "orderFallback")
@RateLimiter(name = "orderService")
public OrderResult createOrder(Order order) {
return remoteOrderService.create(order);
}
}
监控与指标(自动暴露给Prometheus):
resilience4j.metrics: enabled: true # 然后在Grafana中配置对应面板
统一重试的最佳实践
定义统一的重试注解(Spring项目)
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Retryable(
value = {RetryableException.class},
maxAttempts = 3,
backoff = @Backoff(delay = 2000, multiplier = 2.0)
)
public @interface ApiRetry {
}
使用:
@Service
public class UserService {
@ApiRetry // 自定义统一注解
public UserResponse getUser(String userId) {
return remoteUserApi.query(userId);
}
}
统一异常处理 + 业务异常不重试
@Component
public class RetryExceptionClassifier {
/**
* 判断是否应该重试
*/
public boolean shouldRetry(Throwable throwable) {
// 网络超时、服务不可用 -> 重试
if (throwable instanceof TimeoutException ||
throwable instanceof ServiceUnavailableException) {
return true;
}
// 业务异常 -> 不重试
if (throwable instanceof BusinessException) {
return false;
}
// 默认:非检查异常重试
return !(throwable instanceof RuntimeException &&
throwable instanceof IllegalArgumentException);
}
}
全局重试日志与监控
@Aspect
@Component
@Slf4j
public class RetryLogAspect {
@Around("@annotation(org.springframework.retry.annotation.Retryable)")
public Object logRetry(ProceedingJoinPoint joinPoint) throws Throwable {
String methodName = joinPoint.getSignature().toShortString();
int attemptCount = 1;
long startTime = System.currentTimeMillis();
try {
return joinPoint.proceed();
} catch (Throwable t) {
log.warn("方法[{}]第{}次重试失败,耗时{}ms",
methodName, attemptCount,
System.currentTimeMillis() - startTime);
// 这里可以将重试次数记录到监控系统(Prometheus/Micrometer)
// metrics.counter("retry.count", "method", methodName).increment();
}
}
}
推荐方案总结
| 项目类型 | 推荐方案 | 原因 |
|---|---|---|
| 单体Spring Boot | Spring Retry | 简单、无额外依赖 |
| Spring Cloud微服务 | Resilience4j | 功能全、可观测性好 |
| 非Spring项目 | Guava Retryer | 轻量、灵活 |
| 高并发/分布式系统 | Resilience4j + Sentinel | 熔断+限流+重试组合 |
核心建议:
- 永远不要写手动的
while(retryCount < 3)循环 - 确保被重试的方法幂等(多次调用结果一致)
- 重试次数不要超过3次,间隔建议指数退避
- 记录每次重试的完整异常栈,方便排查
- 配合超时时间使用,避免阻塞线程池
如果你能提供具体的项目背景(比如是Dubbo服务、REST API还是数据库操作),我可以给出更针对性的实现方案。