在 Spring Cloud 中实现重试调用可以通过多种方式完成,以下是三种主流实现方案(基于 RestTemplate、OpenFeign 和 Resilience4j),包含完整代码示例:
方案一:使用 RestTemplate + Spring Retry (推荐基础方案)
核心原理
- Spring Retry:基于 AOP 实现声明式重试,支持注解驱动。
- 结合 Feign:通过自定义
FeignClient
的拦截器或配置类集成。
配置步骤
- 添加依赖:xml
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
- 启用重试功能:java
@SpringBootApplication
@EnableRetry // 启用Spring Retry
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
- 自定义重试配置:java
@Configuration
public class FeignConfig {
@Bean
public Request.Options options() {
return new Request.Options(1000, 3000); // 超时设置
}
@Bean
public Retryer feignRetryer() {
return new Retryer() {
private int maxAttempts = 3;
private int attempt = 1;
@Override
public boolean continueOrPropagate(RetryContext context) {
if (attempt++ <= maxAttempts) {
System.out.println("Retrying attempt " + attempt);
return true;
}
return false;
}
@Override
public RetryContext open(RetryCallback callback) {
return new RetryContext() {};
}
@Override
public void close(RetryContext context) {}
@Override
public void registerThrowable(RetryContext context, Throwable throwable) {}
};
}
}
适用场景
- 需更灵活的重试策略(如基于异常类型重试)。
- 需与熔断、限流等功能组合使用。
// 1. 添加依赖
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
// 2. 配置重试模板
@Configuration
@EnableRetry
public class RetryConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
// 3. 实现重试服务
@Service
public class PaymentService {
@Autowired
private RestTemplate restTemplate;
@Retryable(
value = {ResourceAccessException.class}, // 触发重试的异常
maxAttempts = 3, // 最大重试次数
backoff = @Backoff(delay = 1000) // 重试间隔(ms)
)
public String callPaymentService(String orderId) {
String url = "http://PAYMENT-SERVICE/pay/" + orderId;
return restTemplate.getForObject(url, String.class);
}
// 重试全部失败后的降级处理
@Recover
public String fallback(ResourceAccessException e, String orderId) {
return "Payment service unavailable. Order: " + orderId;
}
}
方案二:使用 OpenFeign + 内置重试 (推荐微服务方案)
核心原理
- Ribbon:作为客户端负载均衡器,提供重试机制。
- Feign:默认集成 Ribbon,通过配置 Ribbon 参数控制重试逻辑。
配置步骤
- 添加依赖(若使用 Spring Cloud Netflix):xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
- 配置重试参数(
application.yml
):yaml
ribbon:
ConnectTimeout: 1000 # 连接超时时间
ReadTimeout: 3000 # 读取超时时间
MaxAutoRetries: 1 # 同一实例的重试次数
MaxAutoRetriesNextServer: 2 # 切换实例的重试次数
OkToRetryOnAllOperations: false # 是否对所有请求重试(POST默认不重试)
retryableStatusCodes: 500,502,503,504 # 触发重试的HTTP状态码
- 禁用 Feign 原生重试器(避免与 Ribbon 冲突):java
@Configuration
public class FeignConfig {
@Bean
public Retryer feignRetryer() {
return Retryer.NEVER_RETRY; // 禁用Feign默认重试
}
}
适用场景
- 已使用 Ribbon 和 Feign 的项目。
- 简单的同步请求重试,仅需基础的重试策略。
方案二:Spring Retry(声明式重试)
核心原理
- Spring Retry:基于 AOP 实现声明式重试,支持注解驱动。
- 结合 Feign:通过自定义
FeignClient
的拦截器或配置类集成。
配置步骤
- 添加依赖:xml
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
- 启用重试功能:java
@SpringBootApplication
@EnableRetry // 启用Spring Retry
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
- 自定义重试配置:java
@Configuration
public class FeignConfig {
@Bean
public Request.Options options() {
return new Request.Options(1000, 3000); // 超时设置
}
@Bean
public Retryer feignRetryer() {
return new Retryer() {
private int maxAttempts = 3;
private int attempt = 1;
@Override
public boolean continueOrPropagate(RetryContext context) {
if (attempt++ <= maxAttempts) {
System.out.println("Retrying attempt " + attempt);
return true;
}
return false;
}
@Override
public RetryContext open(RetryCallback callback) {
return new RetryContext() {};
}
@Override
public void close(RetryContext context) {}
@Override
public void registerThrowable(RetryContext context, Throwable throwable) {}
};
}
}
适用场景
- 需更灵活的重试策略(如基于异常类型重试)。
- 需与熔断、限流等功能组合使用。
// 1. 添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
// 2. 配置Feign客户端
@FeignClient(
name = "payment-service",
configuration = FeignRetryConfig.class,
fallbackFactory = PaymentFallbackFactory.class
)
public interface PaymentClient {
@GetMapping("/pay/{orderId}")
String pay(@PathVariable String orderId);
}
// 3. 自定义重试配置
public class FeignRetryConfig {
@Bean
public Retryer feignRetryer() {
// 重试间隔100ms, 最大间隔1s, 最大尝试5次(含首次调用)
return new Retryer.Default(100, 1000, 5);
}
}
// 4. 实现降级处理
@Component
public class PaymentFallbackFactory implements FallbackFactory<PaymentClient> {
@Override
public PaymentClient create(Throwable cause) {
return orderId -> "Fallback payment for: " + orderId;
}
}
方案三:使用 Resilience4j (推荐生产环境)
核心原理
- Resilience4j:轻量级容错框架,提供重试、熔断、限流等功能。
- 与 Feign 集成:通过
resilience4j-spring-cloud2
模块实现。
配置步骤
- 添加依赖:xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
- 配置重试参数(
application.yml
):yaml
resilience4j.retry:
instances:
feign-service:
max-attempts: 3 # 最大重试次数
wait-duration: 1000ms # 重试间隔
enable-exponential-backoff: true # 启用指数退避
exponential-backoff-multiplier: 2 # 退避乘数
retry-exceptions: # 需要重试的异常
- java.io.IOException
- org.springframework.web.client.HttpServerErrorException
- 启用 Resilience4j 集成:
@Configuration
public class FeignConfig {
@Bean
public Feign.Builder feignBuilder(RetryRegistry retryRegistry) {
return Resilience4jFeign.builder()
.retryer(Retry.ofDefaults())
.build();
}
}
- 在 Feign 客户端中使用:
@FeignClient(name = "service-provider", fallback = ServiceFallback.class)
@CircuitBreaker(name = "feign-service", fallbackMethod = "fallback")
@Retry(name = "feign-service") // 指定重试配置
public interface ServiceClient {
@GetMapping("/api/data")
String getData();
}
适用场景
- 复杂的容错需求(如熔断、限流、舱壁隔离)。
- 响应式编程场景(支持 Reactor 模式)。
- 需更细粒度的监控和指标统计。
// 1. 添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>
// 2. 配置重试策略
@Configuration
public class ResilienceConfig {
@Bean
public RetryRegistry retryRegistry() {
RetryConfig config = RetryConfig.custom()
.maxAttempts(3)
.waitDuration(Duration.ofMillis(500))
.retryExceptions(IOException.class, TimeoutException.class)
.build();
return RetryRegistry.of(config);
}
}
// 3. 使用重试机制
@Service
public class OrderService {
@Autowired
private CircuitBreakerFactory factory;
@Autowired
private RestTemplate restTemplate;
public String createOrder(String productId) {
Retry retry = factory.create("orderRetry").getRetry();
return retry.executeSupplier(() -> {
String url = "http://ORDER-SERVICE/create?product=" + productId;
return restTemplate.postForObject(url, null, String.class);
});
}
}
通用配置建议 (application.yml)
# 所有HTTP客户端通用超时设置
spring:
cloud:
loadbalancer:
retry:
enabled: true
ribbon:
ConnectTimeout: 2000 # 连接超时(ms)
ReadTimeout: 5000 # 读取超时(ms)
MaxAutoRetries: 1 # 同一实例重试次数
MaxAutoRetriesNextServer: 2 # 切换实例次数
三种方案对比
特性 | Ribbon + Feign | Spring Retry | Resilience4j |
实现复杂度 | 低(原生集成) | 中(需自定义拦截器) | 高(需额外配置) |
重试策略灵活性 | 一般(固定间隔、次数) | 高(支持注解、自定义逻辑) | 极高(指数退避、谓词判断) |
熔断 / 限流支持 | 无(需额外整合 Hystrix) | 无(需额外整合) | 自带完整容错套件 |
异步 / 响应式支持 | 不支持 | 不支持 | 支持 Reactor 响应式编程 |
监控与指标 | 有限 | 有限 | 丰富(Micrometer 集成) |
社区活跃度 | 低(Netflix 停更) | 中 | 高(持续维护) |
最佳实践建议
- 简单场景:使用 Ribbon + Feign 原生配置,适合快速实现基础重试。
- 复杂场景:使用 Resilience4j,提供一站式容错解决方案,避免组件碎片化。
- 遗留系统:若已使用 Hystrix,可继续使用 Spring Retry 保持兼容性。
无论选择哪种方案,均需注意:
- 幂等性:仅对幂等请求(GET、PUT、DELETE)启用重试。
- 超时配置:合理设置
connectTimeout
和readTimeout
,避免与重试策略冲突。 - 监控告警:结合 Prometheus、Grafana 监控重试指标,避免级联故障。
最佳实践总结:
- 幂等性处理:重试仅适用于GET/PUT等幂等操作,非幂等操作(POST)需谨慎
- 重试策略:
-
- 基础场景:RestTemplate +
@Retryable
- 微服务间调用:OpenFeign + 自定义Retryer
- 生产环境:Resilience4j + 熔断降级
- 基础场景:RestTemplate +
- 关键参数:
-
- 最大重试次数:通常3-5次
- 退避策略:指数退避(如:首次500ms,第二次1000ms)
- 超时设置:比重试间隔短
- 监控:集成Micrometer + Prometheus监控重试指标
- 异常处理:
-
- 仅重试网络异常(ConnectTimeout/ReadTimeout)
- 不重试4xx客户端错误
重要提示:在分布式环境中,重试可能引发雪崩效应,建议:
- 配合熔断器(Hystrix/Resilience4j)使用
- 设置全局重试上限
- 采用随机抖动(jitter)避免重试风暴