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

Spring 6声明式客户端CircuitBreaker无Fallback响应问题排查

Spring Boot声明式客户端集成CircuitBreaker未触发Fallback的问题

已完成的配置与代码

1. 添加Maven依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-circuitbreaker-reactor-resilience4j</artifactId>
</dependency>

2. application.properties配置

resilience4j.circuitbreaker.configs.default.registerHealthIndicator=true
resilience4j.circuitbreaker.configs.default.slidingWindowSize=10
resilience4j.circuitbreaker.configs.default.minimumNumberOfCalls=5
resilience4j.circuitbreaker.configs.default.permittedNumberOfCallsInHalfOpenState=3
resilience4j.circuitbreaker.configs.default.automaticTransitionFromOpenToHalfOpenEnabled=true
resilience4j.circuitbreaker.configs.default.waitDurationInOpenState=5s
resilience4j.circuitbreaker.configs.default.failureRateThreshold=50
resilience4j.circuitbreaker.configs.default.eventConsumerBufferSize=10
resilience4j.circuitbreaker.configs.default.recordExceptions[0]=org.springframework.web.reactive.function.client.WebClientRequestException
resilience4j.circuitbreaker.configs.default.recordExceptions[1]=java.net.ConnectException
resilience4j.circuitbreaker.configs.default.recordExceptions[2]=java.io.IOException

resilience4j.timelimiter.configs.default.timeoutDuration=5s
resilience4j.timelimiter.configs.default.cancelRunningFuture=true

management.health.circuitbreakers.enabled=true

3. 创建ReactiveResilience4JCircuitBreakerFactory Bean

@Bean
Customizer<ReactiveResilience4JCircuitBreakerFactory> defaultCustomizer() {
    return factory ->
            factory.configureDefault(
                    id ->
                            new Resilience4JConfigBuilder(id)
                                    .circuitBreakerConfig(CircuitBreakerConfig.ofDefaults())
                                    .timeLimiterConfig(TimeLimiterConfig.ofDefaults())
                                    .build());
}

4. 为HttpServiceProxyFactory添加CircuitBreaker

@Bean
public HttpServiceProxyFactory httpServiceProxyFactory(
        WebClient.Builder builder,
        ReactiveResilience4JCircuitBreakerFactory resilience4JCircuitBreakerFactory) {
    CircuitBreaker circuitBreaker =
            resilience4JCircuitBreakerFactory
                    .getCircuitBreakerRegistry()
                    .circuitBreaker("default");

    WebClient webClient =
            builder.baseUrl(applicationProperties.getInventoryServiceUrl())
                    .filter(
                            (request, next) ->
                                    next.exchange(request)
                                            .transform(
                                                    CircuitBreakerOperator.of(circuitBreaker)))
                    .defaultHeaders(
                            httpHeaders -> {
                                    httpHeaders.setContentType(MediaType.APPLICATION_JSON);
                                    httpHeaders.setAccept(List.of(MediaType.APPLICATION_JSON));
                            })
                    .build();
    return HttpServiceProxyFactory.builder(WebClientAdapter.forClient(webClient)).build();
}

问题描述

问题1:第三方服务宕机时未触发Fallback响应

按上述配置集成后,当第三方服务宕机时,没有返回任何fallback响应。

问题2:使用@CircuitBreaker注解仍抛出异常

尝试用@CircuitBreaker标记方法并编写fallback方法,但调用时依然抛出异常,代码如下:

@CircuitBreaker(
    name = "getInventoryByProductCode",
    fallbackMethod = "getInventoryByProductCodeFallBack")
private InventoryDto getInventoryByProductCode(String code) {
    return inventoryServiceProxy.getInventoryByProductCode(code);
}

private InventoryDto getInventoryByProductCodeFallBack(String code, Exception e) {
    log.error("Exception occurred while fetching product details", e);
    return new InventoryDto(code, 0);
}

解决方案

针对问题1:WebClient过滤器方式未触发Fallback的修复

  1. 添加Fallback逻辑到CircuitBreakerOperator
    现有配置只初始化了断路器,未指定降级处理逻辑,需要扩展CircuitBreakerOperator的配置:
    .filter((request, next) ->
        next.exchange(request)
            .transform(CircuitBreakerOperator.of(
                circuitBreaker,
                throwable -> {
                    // 构造符合业务格式的fallback响应
                    String productCode = request.pathVariables().get("code");
                    return Mono.just(ClientResponse.create(HttpStatus.OK)
                        .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                        .body("{\"productCode\":\"" + productCode + "\",\"quantity\":0}")
                        .build());
                }
            ))
    )
    
  2. 补充异常记录配置
    服务宕机可能抛出WebClientResponseException(如5xx状态码),需要将其加入异常记录列表:
    resilience4j.circuitbreaker.configs.default.recordExceptions[3]=org.springframework.web.reactive.function.client.WebClientResponseException
    
  3. 满足断路器触发阈值
    当前配置minimumNumberOfCalls=5,测试时需要调用至少5次,断路器才会开始计算失败率并触发降级。

针对问题2:@CircuitBreaker注解失效的修复

  1. 修改方法访问权限为public
    Resilience4J的注解无法作用于private方法,必须将目标方法和fallback方法改为public:
    @CircuitBreaker(
        name = "getInventoryByProductCode",
        fallbackMethod = "getInventoryByProductCodeFallBack")
    public InventoryDto getInventoryByProductCode(String code) {
        return inventoryServiceProxy.getInventoryByProductCode(code);
    }
    
    public InventoryDto getInventoryByProductCodeFallBack(String code, Exception e) {
        log.error("Exception occurred while fetching product details", e);
        return new InventoryDto(code, 0);
    }
    
  2. 确保类被Spring容器管理
    包含该方法的类必须添加@Service或@Component注解,让Spring生成代理类实现切面逻辑。
  3. 校验fallback方法签名
    Fallback方法的参数需与原方法完全匹配,最后追加Exception类型参数,参数顺序和类型不能出错。
  4. 确认注解配置有效性
    若针对getInventoryByProductCode未单独配置断路器,会使用default配置,确保default配置中的异常记录、阈值等符合预期。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 23:05:18