
本文介绍如何通过 exchangefilterfunction 为 spring webclient 全局统一配置基于 reactor 的重试策略,避免在每个请求中重复定义 retrywhen 逻辑,提升代码复用性与可维护性。
本文介绍如何通过 exchangefilterfunction 为 spring webclient 全局统一配置基于 reactor 的重试策略,避免在每个请求中重复定义 retrywhen 逻辑,提升代码复用性与可维护性。
在响应式编程中,Spring WebClient 是构建非阻塞 HTTP 客户端的首选。然而,若为每个 bodyToMono() 或 bodyToFlux() 调用单独配置 .retryWhen(...)(如仅对 5xx 错误进行指数退避重试),不仅冗余,还易导致策略不一致和维护困难。
幸运的是,WebClient 支持通过 ExchangeFilterFunction 实现全局、声明式、可复用的重试逻辑。该过滤器会在每次请求执行后拦截响应或异常,并允许你对 Mono/Flux 流进行增强处理——包括插入统一的重试行为。
以下是一个生产就绪的实现方案:
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import reactor.retry.Retry;
import java.time.Duration;
public class WebClientRetryConfig {
// 定义全局重试策略:最多重试 3 次,初始延迟 1 秒,仅对 5xx 服务端错误生效
public static final ExchangeFilterFunction RETRY_FILTER = ExchangeFilterFunction.ofResponseProcessor(clientResponse -> {
if (clientResponse.statusCode().is5xxServerError()) {
return clientResponse
.bodyToMono(String.class) // 可选:读取原始响应体用于日志或诊断
.then(Mono.error(new RuntimeException("5xx error: " + clientResponse.statusCode())));
}
return Mono.just(clientResponse);
}).andThen(ExchangeFilterFunction.ofRequestProcessor(request -> {
// 此处不修改请求,仅作为链式占位;核心重试逻辑在下游 onErrorResume 中触发
return Mono.just(request);
})).andThen(ExchangeFilterFunction.ofResponseProcessor(clientResponse -> {
// 实际重试发生在 bodyToMono() 后的流上,因此需在 filter 中“包装”响应流
return clientResponse.bodyToMono(Void.class)
.onErrorResume(throwable -> {
if (throwable instanceof RuntimeException && throwable.getMessage().startsWith("5xx error:")) {
return Mono.error(throwable);
}
return Mono.error(throwable);
})
.then(Mono.just(clientResponse))
.retryWhen(Retry.backoff(3, Duration.ofSeconds(1))
.filter(throwable ->
throwable instanceof RuntimeException &&
throwable.getMessage().startsWith("5xx error:")));
}));
// 构建带重试能力的 WebClient 实例
public static WebClient createRetryEnabledWebClient() {
return WebClient.builder()
.filter(RETRY_FILTER)
.build();
}
}
⚠️ 注意事项:
- ExchangeFilterFunction 不能直接在 HttpClient 层(Reactor Netty)设置重试,因为 reactor.netty.http.client.HttpClient 本身不暴露对响应体流的重试控制点;重试必须作用于 WebClient 封装后的 Mono
或更下游的 bodyToMono() 流。 - 上述方案中,我们通过 bodyToMono(Void.class) 触发响应体读取,从而将 5xx 显式转为异常,再结合 retryWhen 实现精准重试。若跳过此步,clientResponse.statusCode() 虽可判断状态,但 bodyToMono() 才是真正抛出 WebClientResponseException 的地方。
- 更简洁且推荐的做法是:统一在 bodyToMono() / bodyToFlux() 后应用重试,并封装为工具方法或自定义 WebClient 扩展类,例如:
public class RetryableWebClient {
private final WebClient webClient;
public RetryableWebClient(WebClient webClient) {
this.webClient = webClient;
}
public <t> Mono<t> getAndRetry(String uri, Class<t> responseType) {
return webClient.get()
.uri(uri)
.retrieve()
.bodyToMono(responseType)
.retryWhen(Retry.backoff(3, Duration.ofSeconds(1))
.filter(t -> t instanceof WebClientResponseException wcre
&& wcre.getStatusCode().is5xxServerError()));
}
}</t></t></t>
✅ 总结:虽然无法直接在 reactor.netty.http.client.HttpClient 上配置重试,但借助 ExchangeFilterFunction 或封装工具方法,完全可以实现一次定义、处处复用的重试策略。推荐优先使用后者——逻辑清晰、调试友好、易于单元测试,且与 WebClient 的响应式生命周期完全对齐。











