基于错误类型配置不同Retry策略的Spring WebFlux实现方案
实现基于不同异常/响应的自定义重试策略
完全可行,你可以通过Reactor的Retry.from()方法构建自定义重试逻辑,精准区分429限流错误和其他5XX异常,分别应用对应的重试规则。以下是具体实现方案:
步骤1:定义常量与依赖
先定义重试相关的常量(可根据业务需求调整):
private static final int MAX_RETRY_ATTEMPTS = 3; private static final long INITIAL_BACKOFF_MILLIS = 100;
步骤2:构建自定义重试策略customStrategy
核心逻辑是通过Retry.from()处理每个重试信号,根据异常类型选择对应的重试规则:
import org.springframework.web.reactive.function.client.WebClientResponseException; import reactor.core.publisher.Mono; import reactor.util.retry.Retry; import reactor.util.retry.RetrySignal; import java.time.Duration; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; Retry customStrategy = Retry.from(retrySignals -> retrySignals.flatMap(signal -> { Throwable failure = signal.failure(); // 处理429限流错误:使用Retry-After头指定的延迟 if (failure instanceof WebClientResponseException.TooManyRequests tooManyRequestsEx) { String retryAfterHeader = tooManyRequestsEx.getHeaders().getFirst("Retry-After"); Duration retryDelay = resolveRetryAfterDelay(retryAfterHeader, signal.totalRetries()); // 未超过最大重试次数则延迟后重试,否则抛出异常 return signal.totalRetries() < MAX_RETRY_ATTEMPTS ? Mono.delay(retryDelay) : Mono.error(failure); } // 处理其他5XX服务端错误:使用指数退避 else if (failure instanceof WebClientResponseException webEx && webEx.getStatusCode().is5xxServerError()) { Duration backoffDelay = Duration.ofMillis(INITIAL_BACKOFF_MILLIS * (1 << signal.totalRetries())); return signal.totalRetries() < MAX_RETRY_ATTEMPTS ? Mono.delay(backoffDelay) : Mono.error(failure); } // 其他异常:不重试,直接抛出 else { return Mono.error(failure); } }) ); // 解析Retry-After头:支持秒数或RFC1123日期格式 private Duration resolveRetryAfterDelay(String retryAfterHeader, int retryCount) { if (retryAfterHeader == null) { // 无Retry-After头时,降级到指数退避延迟 return Duration.ofMillis(INITIAL_BACKOFF_MILLIS * (1 << retryCount)); } try { // 先尝试解析为秒数 long seconds = Long.parseLong(retryAfterHeader); return Duration.ofSeconds(seconds); } catch (NumberFormatException e) { // 尝试解析为RFC1123日期格式 try { ZonedDateTime retryAfterDate = ZonedDateTime.parse(retryAfterHeader, DateTimeFormatter.RFC_1123_DATE_TIME); Duration delay = Duration.between(ZonedDateTime.now(), retryAfterDate); // 若日期已过期,立即重试 return delay.isNegative() ? Duration.ZERO : delay; } catch (DateTimeParseException ex) { // 解析失败时,降级到指数退避延迟 return Duration.ofMillis(INITIAL_BACKOFF_MILLIS * (1 << retryCount)); } } }
步骤3:将策略应用到WebClient调用
直接将customStrategy传入retryWhen()即可:
webClient.post() .uri("/external-api/query") .bodyValue(inputQuery) .retrieve() .bodyToMono(QueryResult.class) .retryWhen(customStrategy) .doOnError(ex -> log.debug("API invocation error: ", ex));
关键逻辑说明
429错误处理:
- 捕获
WebClientResponseException.TooManyRequests类型的异常 - 提取响应头
Retry-After,优先使用该值指定的延迟(支持秒数或日期格式) - 无有效
Retry-After头时,降级到指数退避逻辑
- 捕获
5XX错误处理:
- 捕获所有5XX状态码的
WebClientResponseException - 使用指数退避计算延迟:
初始延迟 * 2^重试次数
- 捕获所有5XX状态码的
终止条件:
- 无论哪种错误类型,达到
MAX_RETRY_ATTEMPTS后停止重试,抛出原始异常
- 无论哪种错误类型,达到
内容的提问来源于stack exchange,提问作者codeluv
相关产品推荐
相关产品推荐

