Spring Boot Resilience4J Аннотация не разомкнутая цепь - PullRequest
1 голос
/ 14 октября 2019

Я проверил на сайте вопросы о Resilience4J, но мне не повезло с их ответами. Я пытаюсь реализовать аннотацию @CircuitBreaker от Resilience4J в моем проекте Spring Boot 2.x. Автоматический выключатель реализован вокруг довольно простой функции. Однако, когда я предоставляю неверный 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

Я не уверен, что мне не хватает. Любая помощь будет принята с благодарностью.

1 Ответ

1 голос
/ 16 октября 2019

Не думаю, что это сработает. Во-первых, вы создаете экземпляр ResilientClient как new ResilientClient(). Вы должны использовать созданный Бин, а не создавать его сами. В аннотации @CircuitBreaker используется spring-aop. Таким образом, вам придется запускать свой класс как SpringBootApplicaiton.

Во-вторых, вы записываете только HttpServerErrorException и IOException как ошибки. Таким образом, автоматический выключатель рассматривает все другие исключения (кроме упомянутых выше и их дочерних элементов) как успешные.

...