【问题标题】:Spring Boot Resilience4J Annotation Not Opening CircuitSpring Boot Resilience4J 注解未打开电路
【发布时间】:2020-02-11 10:53:01
【问题描述】:

我查看了网站上有关 Resilience4J 的问题,但没有得到任何答案。我正在尝试在我的 Spring Boot 2.x 项目中实现来自 Resilience4J 的 @CircuitBreaker 注释。断路器是围绕一个非常简单的功能实现的。但是,当我提供错误的 URL 时,无论我发送多少次请求,电路都不会打开。我什至将所有内容提取到一个独立的应用程序中并运行它 100 次并观察它只是无休止地失败。知道我做错了什么吗?

    @CircuitBreaker(name = "backendA")
    @Component
    public class ResilientClient {

        private HttpClient httpClient;

        private static final Logger log = LoggerFactory.getLogger(ResilientClient.class);

        public ResilientClient() {

            httpClient = HttpClient.newBuilder().build();
        }


        @Bulkhead(name = "backendA")
        public String processPostRequest(String body, String[] headers, String url) {

            HttpResponse<String> response = null;

            HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(url))
                .POST(HttpRequest.BodyPublishers.ofString(body))
                .headers(headers)
                .build();

            try {
                response =  httpClient.send(request, HttpResponse.BodyHandlers.ofString());
            } catch (IOException e) {
                throw new HttpServerErrorException(HttpStatus.INTERNAL_SERVER_ERROR, "This is a remote exception");
            } catch (InterruptedException e) {
                e.printStackTrace();
                log.error("Interrupted Exception: " + e.getLocalizedMessage(), e);

            }
            return response != null ? response.body() : null;
    };

// None of these functions ever get invoked

    private String fallback(Throwable e){
        log.info("generic throwable caught");
        return "generic result";
    }

    private String fallback(String param1, String[] headers, String url, Throwable e) {
        log.info("Fallback method invoked for Throwable: " + param1);
        return null;
    }

    private String fallback(String param1, String[] headers, String url, ConnectException e) {
        log.info("Fallback method invoked for ConnectException: " + param1);
        return null;
    }

}


配置文件直接取自 Github 示例

resilience4j.circuitbreaker:
  configs:
    default:
      registerHealthIndicator: false
      slidingWindowSize: 10
      minimumNumberOfCalls: 5
      permittedNumberOfCallsInHalfOpenState: 3
      automaticTransitionFromOpenToHalfOpenEnabled: true
      waitDurationInOpenState: 2s
      failureRateThreshold: 50
      eventConsumerBufferSize: 10
      recordExceptions:
        - org.springframework.web.client.HttpServerErrorException
        - java.io.IOException
      ignoreExceptions:
        - io.github.robwin.exception.BusinessException
    shared:
      registerHealthIndicator: true
      slidingWindowSize: 100
      permittedNumberOfCallsInHalfOpenState: 30
      waitDurationInOpenState: 1s
      failureRateThreshold: 50
      eventConsumerBufferSize: 10
      ignoreExceptions:
        - io.github.robwin.exception.BusinessException
  instances:
    backendA:
      baseConfig: default
    backendB:
      registerHealthIndicator: true
      slidingWindowSize: 10
      minimumNumberOfCalls: 10
      permittedNumberOfCallsInHalfOpenState: 3
      waitDurationInOpenState: 1s
      failureRateThreshold: 50
      eventConsumerBufferSize: 10
      recordFailurePredicate: io.github.robwin.exception.RecordFailurePredicate
resilience4j.retry:
  configs:
    default:
      maxRetryAttempts: 2
      waitDuration: 100
      retryExceptions:
        - org.springframework.web.client.HttpServerErrorException
        - java.io.IOException
      ignoreExceptions:
        - io.github.robwin.exception.BusinessException
  instances:
    backendA:
      maxRetryAttempts: 3
    backendB:
      maxRetryAttempts: 3
resilience4j.bulkhead:
  configs:
    default:
      maxConcurrentCalls: 100
  instances:
    backendA:
      maxConcurrentCalls: 10
    backendB:
      maxWaitDuration: 10ms
      maxConcurrentCalls: 20

resilience4j.thread-pool-bulkhead:
  configs:
    default:
      maxThreadPoolSize: 4
      coreThreadPoolSize: 2
      queueCapacity: 2
  instances:
    backendA:
      baseConfig: default
    backendB:
      maxThreadPoolSize: 1
      coreThreadPoolSize: 1
      queueCapacity: 1

resilience4j.ratelimiter:
  configs:
    default:
      registerHealthIndicator: false
      limitForPeriod: 10
      limitRefreshPeriod: 1s
      timeoutDuration: 0
      eventConsumerBufferSize: 100
  instances:
    backendA:
      baseConfig: default
    backendB:
      limitForPeriod: 6
      limitRefreshPeriod: 500ms
      timeoutDuration: 3s

尝试测试的代码

SpringBootApplication
public class CircuitsApplication {

    private static final Logger logger = LoggerFactory.getLogger(CircuitsApplication.class);

    static ResilientClient resilientClient = new ResilientClient();

    public static void main(String[] args) {
        //SpringApplication.run(CircuitsApplication.class, args);
        for (int i = 0; i < 100; i++){
            try {
                String body = "body content";
                String[] headers = new String[]{"header", "value"};
                String url = "http://a.bad.url";
                String result = resilientClient.processPostRequest(body, headers, url);
                logger.info(result);
            } catch (Exception ex){
                logger.info("Error caught in main loop");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}

我尝试将 Circuitbreaker 注释添加到方法本身。我尝试过创建供应商并对其进行装饰。我试过添加隔板,移除隔板。我尝试添加具有不同签名的其他后备方法。我尝试过使用和不使用@Component

我最终在日志中得到的只有 100 次:

14:33:10.348 [main] INFO c.t.circuits.CircuitsApplication - Error caught in main loop

我不确定我错过了什么。任何帮助将不胜感激。

【问题讨论】:

  • 你能解决这个问题吗?

标签: java spring-boot circuit-breaker resilience4j


【解决方案1】:

我认为这行不通。首先,您将ResilientClient 实例化为new ResilientClient()。您必须使用创建的 Bean 而不是自己实例化它。 @CircuitBreaker 注解使用 spring-aop。所以你必须将你的类作为 SpringBootApplicaiton 运行。

其次,您仅将HttpServerErrorExceptionIOException 记录为失败。因此断路器将所有其他异常(除了上面提到的异常及其子异常)视为成功。

【讨论】:

  • 要清楚,上面的代码是我将代码从我的 Spring Boot 应用程序中提取出来的。 :) 现在,客户端是使用 (@Autowired ResilientClient resilientClient) 从构造函数中的服务层实例化的。在 Spring Boot 2 中使用 Resilience4J 的几个项目未包含在文档中,包括需要让 Spring 自动装配 bean 或设置一个配置类和文档中的示例 Java 代码有一个未定义的变量
  • 如果您想重试所有异常,请尝试将java.lang.Throwable 添加到resilience4j.circuitbreaker.configs.default. recordExceptions。您可以将忽略的异常添加到resilience4j.circuitbreaker.configs.default. ignoreExceptions
  • 默认情况下,所有异常都算作失败。您不必将 Throwable 添加到 recordExceptions。您只需将例外添加到 ignoreExpections 列表中。
  • 是的,如果您不配置 recordExceptions 他们会这样做。但是,如果您在 recordExceptions 配置中放置异常列表,它们不会。 recordExceptions:记录为失败从而增加失败率的异常列表。任何匹配或从列表之一继承的异常都算作失败,除非通过 ignoreExceptions 明确忽略。如果您指定异常列表,则所有其他异常都算成功,除非它们被 ignoreExceptions 明确忽略。
  • 我知道。我写的。 :) 如果您没有指定应记录的异常列表。除非明确忽略,否则所有异常都会被记录。
猜你喜欢
  • 2020-11-02
  • 2022-08-14
  • 1970-01-01
  • 2021-01-19
  • 2020-09-19
  • 2023-03-09
  • 2021-09-25
  • 2020-01-27
  • 2020-01-26
相关资源
最近更新 更多