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

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());
}

原因分析

  1. 异常类型不匹配:回退方法参数声明为RuntimeException,但ConnectException属于IOException的子类(非RuntimeException),不在捕获范围内;实际WebClient抛出的是WebClientRequestException(包装了ConnectException),同样不属于RuntimeException,因此无法触发回退。
  2. 初始调用阈值限制:配置中minimum-number-of-calls=5,意味着断路器需要累计至少5次调用后才会开始计算失败率,前5次调用即使全部失败,也不会触发断路器逻辑和回退方法。
  3. 异常传递中断:如果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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 17:15:31