
本文介绍如何为 spring webflux 的 webclient 统一配置基于 reactor retry 的全局重试策略,避免在每个请求中重复定义 retrywhen 逻辑,提升代码复用性与可维护性。
本文介绍如何为 spring webflux 的 webclient 统一配置基于 reactor retry 的全局重试策略,避免在每个请求中重复定义 retrywhen 逻辑,提升代码复用性与可维护性。
在响应式编程中,WebClient 是 Spring WebFlux 推荐的非阻塞 HTTP 客户端。虽然其底层基于 Reactor Netty(reactor.netty.http.client.HttpClient),但Netty 层的 HttpClient 并不直接支持声明式重试逻辑——重试是 Reactive Stream 的背压与错误恢复语义,必须作用于 Mono/Flux 流上,而非连接层。
因此,无法通过配置 reactor.netty.http.client.HttpClient 实例来实现“自动重试所有请求”。真正的解决方案是:将重试逻辑封装为可复用的 ExchangeFilterFunction,并将其注册到全局 WebClient.Builder 中。
✅ 推荐方案:使用 ExchangeFilterFunction 实现全局重试
ExchangeFilterFunction 是 WebClient 的拦截器机制,可在请求执行前后插入自定义逻辑。我们将重试策略内聚于此,并限定仅对服务端 5xx 错误触发指数退避重试:
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 WebClientConfig {
// 构建具备全局重试能力的 WebClient
public static WebClient createRetryCapableWebClient() {
ExchangeFilterFunction retryFilter = ExchangeFilterFunction.ofResponseProcessor(clientResponse -> {
if (clientResponse.statusCode().is5xxServerError()) {
return Mono.error(new WebClientResponseException(
clientResponse.statusCode(),
clientResponse.statusText(),
clientResponse.headers().asHttpHeaders(),
new byte[0],
null
));
}
return Mono.just(clientResponse);
}).andThen(ExchangeFilterFunction.ofRequestProcessor(request -> {
// 可选:统一添加请求头、traceId 等
return Mono.just(request);
})).andThen(ExchangeFilterFunction.ofResponseProcessor(response -> {
// 此处不处理成功响应,仅用于链式组合;重试由下游 retryWhen 触发
return Mono.just(response);
}));
// 应用重试策略:最多 3 次,初始延迟 1s,指数退避,仅针对 5xx
ExchangeFilterFunction retryStrategy = ExchangeFilterFunction.ofResponseProcessor(clientResponse -> {
if (clientResponse.statusCode().is5xxServerError()) {
return Mono.error(new WebClientResponseException(
clientResponse.statusCode(),
clientResponse.statusText(),
clientResponse.headers().asHttpHeaders(),
new byte[0],
null
));
}
return Mono.just(clientResponse);
}).andThen((request, next) ->
next.exchange(request)
.retryWhen(Retry.backoff(3, Duration.ofSeconds(1))
.filter(throwable -> throwable instanceof WebClientResponseException
&& ((WebClientResponseException) throwable).getStatusCode().is5xxServerError())
.onRetry((retrySignal) -> {
System.out.printf("Retrying request (%d/%d) after %dms%n",
retrySignal.iteration(),
3,
retrySignal.totalElapsedFirst() != null ? retrySignal.totalElapsedFirst().toMillis() : 0);
})
)
);
return WebClient.builder()
.filter(retryStrategy)
.build();
}
}
? 关键说明:
- retryWhen 必须作用于 next.exchange(request) 返回的 Mono
,因此需在 ExchangeFilterFunction 的链中嵌套调用; - 使用 filter() 精确控制重试条件(如仅限 5xx),避免对客户端错误(4xx)或网络异常无意义重试;
- onRetry() 是可选钩子,便于可观测性(日志、指标上报);
- 若需区分不同 API 的重试策略(如 /health 不重试,/payment 重试 5 次),仍可在具体调用处叠加局部 retryWhen,它会与全局策略叠加生效(注意顺序与语义)。
⚠️ 注意事项与最佳实践
- 不要滥用重试:对幂等性不确定的 POST/PUT 请求,应确保服务端支持幂等(如通过 Idempotency-Key),否则重试可能导致重复提交;
- 超时优先于重试:务必为 WebClient 配置连接、响应超时(.codecs(...).defaultTimeout(...)),防止重试卡死;
-
避免阻塞操作:block() 仅适用于测试或极少数特殊场景,生产环境请始终返回 Mono
或 Flux ; - 异常分类要严谨:WebClientResponseException 包含 4xx/5xx,但 IOException、TimeoutException 等需额外 onErrorResume 或 retryWhen(...predicate...) 处理;
- Metrics & Tracing:建议结合 Micrometer 记录重试次数、失败率,并通过 Span 标记重试事件,提升故障排查效率。
通过以上方式,你即可实现真正意义上的“一次配置、处处生效”的 WebClient 全局重试能力,兼顾简洁性、可维护性与生产就绪性。











