Spring RestClient如何实现类似WebClient过滤器的重试机制?
Spring RestClient如何实现类似WebClient过滤器的重试机制?
嗨,这个问题我之前做项目的时候刚好碰到过,完全懂你不想多套一层服务接口的痛点!结合你说的底层是Reactor HTTP客户端的情况,给你分享几个不用额外加服务层就能实现重试的方案:
方案一:直接在Reactor流上添加重试逻辑(细粒度控制)
如果你的HTTP接口方法返回的是Mono或Flux类型(毕竟底层是Reactor客户端),可以在调用接口方法后直接给响应流加上Reactor的重试操作符,这种方式适合针对单个接口调用做定制化重试:
比如你的HTTP接口定义是这样的:
@HttpExchange("/api/data") interface MyApiClient { @GetExchange Mono<Data> fetchData(); }
那调用的时候直接给流加重试:
myApiClient.fetchData() // 配置重试策略:最多3次,指数退避,只对5xx错误和网络异常重试 .retryWhen(Retry.backoff(3, Duration.ofSeconds(1)) .filter(throwable -> { if (throwable instanceof WebClientResponseException) { WebClientResponseException ex = (WebClientResponseException) throwable; return ex.getStatusCode().is5xxServerError(); } return throwable instanceof IOException; })) .subscribe(data -> { // 处理返回数据 });
这种方式简单直接,不用改客户端配置,想给哪个调用加就加哪个。
方案二:用Spring Retry注解全局/局部配置重试
如果希望把重试逻辑封装起来,不用每次调用都写retryWhen,可以用Spring Retry框架,直接在HTTP接口的方法上加注解就能生效:
- 首先引入Spring Retry的依赖(Maven示例):
<dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
- 配置重试模板Bean:
@Configuration @EnableRetry public class RetryConfig { @Bean public RetryTemplate apiRetryTemplate() { // 配置重试策略:最多3次 SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(); retryPolicy.setMaxAttempts(3); // 配置退避策略:每次重试间隔1秒 FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy(); backOffPolicy.setBackOffPeriod(1000); RetryTemplate template = new RetryTemplate(); template.setRetryPolicy(retryPolicy); template.setBackOffPolicy(backOffPolicy); return template; } }
- 给HTTP接口方法加
@Retryable注解:
@HttpExchange("/api/data") interface MyApiClient { @GetExchange // 指定针对5xx异常和网络异常重试,用我们配置的重试模板 @Retryable( retryFor = {WebClientResponseException.class, IOException.class}, template = "apiRetryTemplate", recover = "recoverFetchData" ) Mono<Data> fetchData(); // 重试失败后的兜底方法 @Recover Mono<Data> recoverFetchData(Throwable ex) { // 这里可以返回默认值或者抛出自定义异常 return Mono.just(new Data("default-value")); } }
这样所有调用这个接口方法的地方都会自动触发重试,不用手动处理流的逻辑。
方案三:自定义请求拦截器全局添加重试
如果你想给所有通过这个RestClient的HTTP接口调用都加上统一的重试逻辑,那可以给RestClient添加一个自定义的请求拦截器,在拦截器里给响应流加上重试:
@Bean public RestClient myApiRestClient() { return RestClient.builder() .baseUrl("https://your-api-domain.com") .clientConnector(new ReactorClientHttpConnector()) .requestInterceptor((request, body, execution) -> { // 执行请求后给响应流加重试逻辑 return execution.execute(request, body) .retryWhen(Retry.backoff(3, Duration.ofSeconds(1)) .filter(ex -> { // 只对服务器错误和网络异常重试 if (ex instanceof ClientHttpResponseException) { return ((ClientHttpResponseException) ex).getStatusCode().is5xxServerError(); } return ex instanceof IOException; })); }) .build(); } // 用上面的RestClient创建HTTP接口代理 @Bean public MyApiClient myApiClient(RestClient myApiRestClient) { return myApiRestClient.createClient(MyApiClient.class); }
这种方式一劳永逸,所有通过这个RestClient的接口调用都会自动带上重试逻辑,适合全局统一配置的场景。
我当时项目里用的是第三种方案,把重试逻辑全局配置好之后,所有HTTP接口的调用都不用再操心重试的事,省了好多重复代码。你可以根据自己的需求选最适合的方式~
内容来源于stack exchange
相关产品推荐
相关产品推荐

