You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于错误类型配置不同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));

关键逻辑说明

  1. 429错误处理:

    • 捕获WebClientResponseException.TooManyRequests类型的异常
    • 提取响应头Retry-After,优先使用该值指定的延迟(支持秒数或日期格式)
    • 无有效Retry-After头时,降级到指数退避逻辑
  2. 5XX错误处理:

    • 捕获所有5XX状态码的WebClientResponseException
    • 使用指数退避计算延迟:初始延迟 * 2^重试次数
  3. 终止条件:

    • 无论哪种错误类型,达到MAX_RETRY_ATTEMPTS后停止重试,抛出原始异常

内容的提问来源于stack exchange,提问作者codeluv

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 06:35:24