Spring Reactive中Filter内retryWhen()不生效问题排查
作为Spring Reactive新手,我用WebClient.Builder创建WebClient,其他功能正常,但请求超时后重试逻辑没触发。已经给构建器配置了自定义RetryFilter,调试能看到Filter被调用,但重试就是不执行,想知道是不是订阅模型的问题或者其他原因?
构建器配置代码
builder.filter(new RetryFilter())
RetryFilter代码
public class RetryFilter implements ExchangeFilterFunction { public RetryFilter() { } @Override public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) { return next.exchange(request) .flatMap(response -> response.statusCode().is5xxServerError() ? response.createException().flatMap(Mono::error) : Mono.just(response) ) .retryWhen(Retry.backoff(2, Duration.ofMillis(75))) .onErrorMap(e -> { if (Exceptions.isRetryExhausted(e)) { return e.getCause(); } return e; }); } }
WebClient使用代码
return client.retrieve() .toEntity(String.class) .flatMap(response -> unmarshalResponse(response, input));
问题排查与解决方案
1. 超时异常未被纳入重试范围
你的RetryFilter当前只显式处理了5xx状态码转成的异常,但请求超时抛出的TimeoutException(或Netty的ReadTimeoutException)可能没被正确捕获重试。默认Retry.backoff会重试所有异常,但显式指定重试范围能避免意外情况:
.retryWhen(Retry.backoff(2, Duration.ofMillis(75)) .retryOn(throwable -> throwable instanceof ClientResponseException || throwable instanceof TimeoutException || throwable instanceof ReadTimeoutException))
2. 未正确配置WebClient超时
如果WebClient没有设置超时时间,请求不会触发超时异常,自然不会触发重试。需要给WebClient添加超时配置:
builder.clientConnector(new ReactorClientHttpConnector(HttpClient.create() .responseTimeout(Duration.ofSeconds(1)))); // 设置1秒响应超时
3. 检查错误信号传递逻辑
Filter中response.createException().flatMap(Mono::error)的写法是正确的,能将5xx响应转为ClientResponseException触发重试,但要注意:响应体被createException消耗后,重试时会重新发起请求,这是正常行为。
另外,onErrorMap中处理重试耗尽异常的逻辑不会影响重试触发,因为它是在重试逻辑执行完成后才生效的。
4. 验证重试触发状态
可以在retryWhen中添加日志,确认是否有异常进入重试流程:
.retryWhen(Retry.backoff(2, Duration.ofMillis(75)) .doBeforeRetry(retrySignal -> System.out.println("触发重试,异常原因:" + retrySignal.failure().getMessage())))
如果没有输出日志,说明异常没有到达retryWhen阶段,需要排查是否有其他Filter或操作提前消耗了错误信号。
5. 确认订阅环境的正确性
确保你的WebClient调用是在Reactive环境中被订阅的(比如Spring WebFlux控制器返回Mono/Flux),如果是在非Reactive环境中调用block(),也不影响重试逻辑,但要避免在流程中手动取消订阅。
内容的提问来源于stack exchange,提问作者gwcoderguy

