Spring Boot集成Resilience4j断路器时回退方法未触发问题
Resilience4j断路器未触发回退方法的问题排查
问题描述
配置Resilience4j断路器后,当依赖的catalogService宕机时,notificationService抛出java.net.ConnectException: Connection refused: no further information异常,但@CircuitBreaker注解指定的回退方法未被触发。
相关配置与代码
application.properties
## circuit breaker management.endpoints.web.exposure.include=* management.endpoint.health.show-details=always management.health.circuitbreakers.enabled=true resilience4j.circuitbreaker.instances.reservation.failure-rate-threshold=50 resilience4j.circuitbreaker.instances.reservation.minimum-number-of-calls=5 resilience4j.circuitbreaker.instances.reservation.automatic-transition-from-open-to-half-open-enabled=true resilience4j.circuitbreaker.instances.reservation.wait-duration-in-open-state=5s resilience4j.circuitbreaker.instances.reservation.permitted-number-of-calls-in-half-open-state=3 resilience4j.circuitbreaker.instances.reservation.sliding-window-size=10 resilience4j.circuitbreaker.instances.reservation.sliding-window-type=count_based resilience4j.circuitbreaker.configs.default.registerHealthIndicator=true
NotificationController.java
@RestController @RequestMapping("/api/notification") public class NotificationApiController { @Autowired private NotificationService notificationService; @PostMapping @ResponseStatus(HttpStatus.OK) @CircuitBreaker(name = "reservation", fallbackMethod = "fallbackMethod") public void createNotificationsAboutUpcomingDeadlines() { notificationService.createNotificationsAboutUpcomingDeadlines(); } public void fallbackMethod(RuntimeException runtimeException) { System.out.println("Not working"); } }
NotificationService调用片段
private List<BookDto> queryCatalogServiceForItemsDetails(List<ReservationDto> reservationItemDtoList){ List<Integer> bookIsbnList = reservationItemDtoList.stream() .flatMap(reservation -> reservation.getReservationItemList().stream()) .map(ReservationItemDto::getBookIsbn) .collect(Collectors.toList()); Object[] x = webClientBuilder.build().get() .uri(String.format("http://catalogService/api/catalog?ids=%s", bookIsbnList.stream() .map(Object::toString) .collect(Collectors.joining(", ")))) .retrieve() .bodyToMono(Object[].class) .block(); return Arrays.stream(x) .map(object -> mapper.convertValue(object, BookDto.class)) .collect(Collectors.toList()); }
原因分析
- 异常类型不匹配:回退方法参数声明为
RuntimeException,但ConnectException属于IOException的子类(非RuntimeException),不在捕获范围内;实际WebClient抛出的是WebClientRequestException(包装了ConnectException),同样不属于RuntimeException,因此无法触发回退。 - 初始调用阈值限制:配置中
minimum-number-of-calls=5,意味着断路器需要累计至少5次调用后才会开始计算失败率,前5次调用即使全部失败,也不会触发断路器逻辑和回退方法。 - 异常传递中断:如果NotificationService的方法内部存在未显式抛出的异常捕获逻辑,会导致异常无法传递到Controller层的@CircuitBreaker注解方法,断路器无法感知失败。
解决方案
1. 修正回退方法的异常参数类型
将回退方法的参数改为Throwable(捕获所有异常)或具体的WebClientRequestException,确保覆盖连接异常的类型:
public void fallbackMethod(Throwable throwable) { System.out.println("触发回退:连接失败,原因:" + throwable.getMessage()); }
2. 调整断路器初始调用阈值(测试/快速验证用)
如果需要在少量失败后立即触发回退,可以降低minimum-number-of-calls的值,比如设置为1:
resilience4j.circuitbreaker.instances.reservation.minimum-number-of-calls=1
3. 确保异常完整传递到注解方法层
检查NotificationService的所有方法,确保没有在内部捕获异常后未重新抛出的情况。比如如果queryCatalogServiceForItemsDetails方法有try-catch块,需要将异常抛出,让@CircuitBreaker注解能感知到失败。
内容的提问来源于stack exchange,提问作者StuckyBoy
相关产品推荐
相关产品推荐

