附录 D:弹性模式速查

响应式弹性模式速查:重试 retryWhen 指数退避配置、熔断 CircuitBreakerOperator 参数、限流 RateLimiterOperator 令牌桶设置、降级 onErrorResume 模式、以及各模式的组合顺序与最佳实践。

重试速查

.retryWhen(Retry.backoff(3, Duration.ofMillis(100))
    .maxBackoff(Duration.ofSeconds(2))
    .jitter(0.5)
    .filter(err -> err instanceof WebClientRequestException
        || (err instanceof WebClientResponseException wce
            && wce.getStatusCode().is5xxServerError())))

Retry.backoff(3, ...) 是「重试 3 次」,加上首次调用共 4 次。WebClient 的 4xx 不应该重试——WebClientResponseException 覆盖所有非 2xx 响应,要再按 is5xxServerError() 过滤;连接失败、DNS 失败等则是 WebClientRequestException。

参数含义
3最多重试次数
Duration.ofMillis(100)初始间隔
maxBackoff最大间隔
jitter抖动比例
filter只对特定错误重试

熔断速查

CircuitBreakerConfig config = CircuitBreakerConfig.custom()
    .failureRateThreshold(50)
    .waitDurationInOpenState(Duration.ofSeconds(30))
    .permittedNumberOfCallsInHalfOpenState(10)
    .slidingWindowSize(100)
    .build();

CircuitBreaker breaker = CircuitBreaker.of("api", config);

// 使用
mono.transformDeferred(CircuitBreakerOperator.of(breaker));

限流速查

RateLimiterConfig config = RateLimiterConfig.custom()
    .limitForPeriod(1000)
    .limitRefreshPeriod(Duration.ofSeconds(1))
    .timeoutDuration(Duration.ofMillis(10))
    .build();

RateLimiter limiter = RateLimiter.of("api", config);

// 使用
mono.transformDeferred(RateLimiterOperator.of(limiter));

降级速查

.onErrorResume(WebClientRequestException.class, err ->
    Mono.just(defaultValue))
.onErrorResume(err ->
    Mono.just(fallbackValue))
.onErrorReturn("static-fallback");

注意 java.util.concurrent.TimeoutException、Reactor Netty 的 ReadTimeoutException、WebClientRequestException 是不同异常;只写 TimeoutException.class 经常匹配不到真正的超时。先用日志确认异常类型,再写 onErrorResume。

组合顺序

请求
    → 限流(最外层,超量请求不进入重试和熔断统计)
        → 重试(中间层,每次重试都经过熔断器)
            → 熔断(最内层,保护下游并快速失败)
                → 业务逻辑
webClient.get().uri(url)
    .retrieve()
    .bodyToMono(String.class)
    .transformDeferred(CircuitBreakerOperator.of(breaker))       // 1 最内层
    .retryWhen(Retry.backoff(2, Duration.ofMillis(100))
        .filter(err -> !(err instanceof CallNotPermittedException)))  // 2
    .transformDeferred(RateLimiterOperator.of(rateLimiter))      // 3 最外层
    .onErrorResume(err -> Mono.just(fallback));                  // 4

Reactor 里「写在后面的操作符是外层的」,所以最外层限流要写在最后。重试必须排除 CallNotPermittedException,否则熔断器打开后重试还会反复发起注定被拒的调用。

参数对照表

场景重试熔断限流降级
内部服务调用2–3 次50% / 30s1000/s缓存
外部第三方 API1–2 次50% / 60s100/s失败
写操作(关键)0–1 次80% / 10s100/s不降级
读操作(非关键)3 次50% / 30s5000/s空值

小结

弹性模式不是越多越好。每个模式都会增加复杂度和延迟,只在有明确故障场景时使用。重试 + 熔断 + 限流能覆盖大部分同步调用场景,但写操作的重试必须先保证幂等,否则重试只会制造重复数据。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「java」更多文章

  1. 15.3 全书总结与学习路径回顾
  2. 15.2 响应式流处理的演进趋势
  3. 15.1 引擎选型指南