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的修复
- 添加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()); } )) ) - 补充异常记录配置
服务宕机可能抛出WebClientResponseException(如5xx状态码),需要将其加入异常记录列表:resilience4j.circuitbreaker.configs.default.recordExceptions[3]=org.springframework.web.reactive.function.client.WebClientResponseException - 满足断路器触发阈值
当前配置minimumNumberOfCalls=5,测试时需要调用至少5次,断路器才会开始计算失败率并触发降级。
针对问题2:@CircuitBreaker注解失效的修复
- 修改方法访问权限为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); } - 确保类被Spring容器管理
包含该方法的类必须添加@Service或@Component注解,让Spring生成代理类实现切面逻辑。 - 校验fallback方法签名
Fallback方法的参数需与原方法完全匹配,最后追加Exception类型参数,参数顺序和类型不能出错。 - 确认注解配置有效性
若针对getInventoryByProductCode未单独配置断路器,会使用default配置,确保default配置中的异常记录、阈值等符合预期。
内容的提问来源于stack exchange,提问作者rajadilipkolli
相关产品推荐
相关产品推荐

