本文目录导读:

手动循环重试(最简单)
public class SimpleRetryDemo {
public String callRemoteService() {
int maxRetries = 3;
int retryDelay = 1000; // 毫秒
for (int i = 0; i < maxRetries; i++) {
try {
// 调用远程服务
return doRemoteCall();
} catch (Exception e) {
if (i == maxRetries - 1) {
throw new RuntimeException("服务调用失败,已重试" + maxRetries + "次", e);
}
try {
Thread.sleep(retryDelay);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
throw new RuntimeException("重试被中断", ex);
}
}
}
throw new RuntimeException("未知错误");
}
private String doRemoteCall() {
// 模拟远程调用
return "success";
}
}
缺点: 代码侵入性强,不易维护,不支持灵活配置
使用Spring Retry(推荐)
添加依赖
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
启用重试
@Configuration
@EnableRetry
public class RetryConfig {
}
使用注解方式
@Service
public class OrderService {
@Retryable(
value = {RemoteAccessException.class, TimeoutException.class},
maxAttempts = 3,
backoff = @Backoff(delay = 1000, multiplier = 2) // 指数退避
)
public String createOrder(Order order) {
// 调用远程订单服务
return remoteOrderService.create(order);
}
@Recover
public String recover(RemoteAccessException e, Order order) {
// 所有重试失败后的兜底处理
log.error("创建订单失败,进行降级处理", e);
return "fallback_order_id";
}
}
编程式重试
@Service
public class RetryTemplateService {
@Autowired
private RetryTemplate retryTemplate;
public String callWithRetry() {
return retryTemplate.execute(context -> {
// 重试次数检查
if (context.getRetryCount() > 0) {
log.info("第{}次重试", context.getRetryCount());
}
return remoteService.call();
}, context -> {
// 重试耗尽后的恢复逻辑
log.error("重试耗尽,执行降级");
return "fallback";
});
}
}
使用Resilience4j(功能更全面)
添加依赖
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-retry</artifactId>
</dependency>
配置重试
@Configuration
public class Resilience4jConfig {
@Bean
public Retry retry() {
RetryConfig config = RetryConfig.custom()
.maxAttempts(3)
.waitDuration(Duration.ofMillis(1000))
.retryExceptions(IOException.class, TimeoutException.class)
.ignoreExceptions(BusinessException.class) // 业务异常不重试
.build();
RetryRegistry registry = RetryRegistry.of(config);
return registry.retry("myService");
}
}
使用方式
@Service
public class PaymentService {
@Autowired
private Retry retry;
public String processPayment(Payment payment) {
// 方式1: 函数式
Supplier<String> retryableSupplier = Retry.decorateSupplier(
retry, () -> remotePaymentService.pay(payment)
);
return retryableSupplier.get();
// 方式2: 直接调用
// return retry.executeSupplier(() -> remotePaymentService.pay(payment));
}
}
使用Guava Retrying(轻量级)
添加依赖
<dependency>
<groupId>com.github.rholder</groupId>
<artifactId>guava-retrying</artifactId>
<version>2.0.0</version>
</dependency>
实现示例
public class GuavaRetryDemo {
public String callWithRetry() {
Retryer<String> retryer = RetryerBuilder.<String>newBuilder()
.retryIfExceptionOfType(IOException.class)
.retryIfResult(result -> result == null || result.isEmpty())
.withWaitStrategy(WaitStrategies.fixedWait(1, TimeUnit.SECONDS))
.withStopStrategy(StopStrategies.stopAfterAttempt(3))
.withRetryListener(new RetryListener() {
@Override
public <V> void onRetry(Attempt<V> attempt) {
log.info("第{}次重试, 耗时:{}ms",
attempt.getAttemptNumber(),
attempt.getDelaySinceFirstAttempt());
}
})
.build();
try {
return retryer.call(() -> remoteService.call());
} catch (ExecutionException | RetryException e) {
log.error("重试失败", e);
return "fallback";
}
}
}
关键设计原则
1 重试策略
// 固定延迟 @Backoff(delay = 1000) // 指数退避(推荐,避免雪崩) @Backoff(delay = 1000, multiplier = 2) // 1s, 2s, 4s, 8s... // 随机延迟(避免惊群效应) @Backoff(delay = 1000, random = true)
2 重试条件
// 只对特定异常重试
@Retryable(value = {NetworkException.class, TimeoutException.class})
// 忽略特定异常(业务异常不重试)
@Retryable(exceptionExpression = "message.contains('timeout')")
3 幂等性设计
// 重试接口必须是幂等的
public interface IdempotentService {
// 相同参数多次调用结果一致
String process(String orderId, @Idempotent String token);
}
4 熔断与重试结合
@CircuitBreaker(name = "orderService", fallbackMethod = "fallback")
@Retryable(maxAttempts = 3)
public String createOrder() {
// 先熔断保护,再重试
}
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 简单项目 | 手动循环 | 轻量,无需依赖 |
| Spring项目 | Spring Retry | 与Spring完美集成 |
| 微服务 | Resilience4j | 功能全面,支持熔断限流 |
| 快速开发 | Guava Retrying | API简洁,灵活配置 |
核心建议:
- 必须设置最大重试次数,防止无限重试
- 使用指数退避策略,避免雪崩效应
- 只重试可恢复的异常(网络超时、服务抖动)
- 业务异常不要重试(参数错误、余额不足)
- 重试接口必须幂等,多次调用结果一致
- 设置超时时间,避免长时间等待
- 做好监控和告警,及时发现重试频繁的问题