«1. Обзор

В этом руководстве мы представим проект Spring Cloud Circuit Breaker и узнаем, как мы можем его использовать.

Во-первых, мы посмотрим, что предлагает Spring Cloud Circuit Breaker в дополнение к существующим реализациям прерывателя цепи. Далее мы узнаем, как использовать механизм автоматической настройки Spring Boot для интеграции одного или нескольких автоматических выключателей в наше приложение.

Обратите внимание, что мы получили дополнительную информацию о том, что такое автоматический выключатель и как он работает, в статьях «Введение в Hystrix», «Spring Cloud Netflix Hystrix» и «Руководство по Resilience4j».

2. Spring Cloud Circuit Breaker

До недавнего времени Spring Cloud предоставлял нам только один способ добавления автоматических выключателей в наши приложения. Это произошло благодаря использованию Netflix Hystrix в рамках проекта Spring Cloud Netflix.

Проект Spring Cloud Netflix на самом деле представляет собой просто библиотеку-оболочку на основе аннотаций вокруг Hystrix. Следовательно, эти две библиотеки тесно связаны. Это означает, что мы не можем переключиться на другую реализацию автоматического выключателя без изменения приложения.

Проект Spring Cloud Circuit Breaker решает эту проблему. Он обеспечивает уровень абстракции для различных реализаций автоматических выключателей. Это подключаемая архитектура. Таким образом, мы можем кодировать предоставленную абстракцию/интерфейс и переключаться на другую реализацию в зависимости от наших потребностей.

В наших примерах мы сосредоточимся только на реализации Resilience4J. Однако эти методы можно использовать и для других плагинов.

3. Автоматическая конфигурация

Чтобы использовать определенные реализации автоматического выключателя в нашем приложении, нам нужно добавить соответствующий стартер Spring. В нашем случае воспользуемся spring-cloud-starter-circuitbreaker-resilence4j:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
    <version>1.0.2.RELEASE</version>
</dependency>

Механизм автоконфигурации настраивает необходимые bean-компоненты прерывателя цепи, если видит один из стартеров в пути к классам.

Если бы мы хотели отключить автоматическую настройку Resilience4J, мы могли бы установить для свойства spring.cloud.circuitbreaker.resilience4j.enabled значение false.

4. Пример простого прерывателя цепи

Давайте создадим веб-приложение, используя Spring Boot, чтобы мы могли изучить, как работает библиотека Spring Cloud Circuit Breaker.

Мы создадим простой веб-сервис, возвращающий список альбомов. Предположим, необработанный список предоставлен сторонней службой. Для простоты мы будем использовать внешний фиктивный API, предоставленный Jsonplaceholder, для получения списка:

https://jsonplaceholder.typicode.com/albums

4.1. Создание автоматического выключателя

Давайте создадим наш первый автоматический выключатель. Мы начнем с внедрения экземпляра компонента CircuitBreakerFactory:

@Service
public class AlbumService {
    
    @Autowired
    private CircuitBreakerFactory circuitBreakerFactory;

    //... 

}

Теперь мы можем легко создать автоматический выключатель, используя метод CircuitBreakerFactory#create. В качестве аргумента принимает идентификатор автоматического выключателя:

CircuitBreaker circuitBreaker = circuitBreakerFactory.create("circuitbreaker");

4.2. Обернуть задачу в автоматический выключатель

Чтобы обернуть и запустить задачу, защищенную автоматическим выключателем, нам нужно вызвать метод запуска, который принимает поставщика в качестве аргумента.

public String getAlbumList() {
    CircuitBreaker circuitBreaker = circuitBreakerFactory.create("circuitbreaker");
    String url = "https://jsonplaceholder.typicode.com/albums";

    return circuitBreaker.run(() -> restTemplate.getForObject(url, String.class));
}

Автоматический выключатель запускает наш метод за нас и обеспечивает отказоустойчивость.

Иногда нашей внешней службе может потребоваться слишком много времени, чтобы ответить, вызвать неожиданное исключение или внешнюю службу или хост не существует. В этом случае мы можем предоставить запасной вариант в качестве второго аргумента для метода run:

public String getAlbumList() {
    CircuitBreaker circuitBreaker = circuitBreakerFactory.create("circuitbreaker");
    String url = "http://localhost:1234/not-real";
    
    return circuitBreaker.run(() -> restTemplate.getForObject(url, String.class), 
      throwable -> getDefaultAlbumList());
}

Лямбда для резервного варианта получает Throwable в качестве входных данных, описывающих ошибку. Это означает, что мы можем предоставить вызывающему объекту разные результаты отката в зависимости от типа исключения, вызвавшего откат.

В этом случае мы не будем учитывать исключение. Мы просто вернем кешированный список альбомов.

Если внешний вызов завершается исключением и резервный вариант не предоставляется, Spring создает исключение NoFallbackAvailableException.

4.3. Создание контроллера

Теперь давайте закончим наш пример и создадим простой контроллер, который вызывает методы службы и представляет результаты через браузер:

@RestController
public class Controller {

    @Autowired
    private Service service;

    @GetMapping("/albums")
    public String albums() {
        return service.getAlbumList();
    }

}

Наконец, давайте вызовем службу REST и посмотрим на результаты: ~~ ~

[GET] http://localhost:8080/albums

5. Глобальная пользовательская конфигурация

«Обычно конфигурации по умолчанию недостаточно. По этой причине нам необходимо создавать автоматические выключатели с индивидуальными конфигурациями, основанными на наших вариантах использования.

Чтобы переопределить конфигурацию по умолчанию, нам нужно указать наши собственные bean-компоненты и свойства в классе @Configuration.

Здесь мы собираемся определить глобальную конфигурацию для всех автоматических выключателей. По этой причине нам необходимо определить bean-компонент Customizer\u003cCircuitBreakerFactory\u003e. Итак, давайте воспользуемся реализацией Resilience4JCircuitBreakerFactory.

Сначала мы определим классы конфигурации прерывателя цепи и ограничителя времени в соответствии с руководством Resilience4j:

CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
  .failureRateThreshold(50)
  .waitDurationInOpenState(Duration.ofMillis(1000))
  .slidingWindowSize(2)
  .build();
TimeLimiterConfig timeLimiterConfig = TimeLimiterConfig.custom()
  .timeoutDuration(Duration.ofSeconds(4))
  .build();

Затем давайте встроим конфигурацию в bean-компонент Customizer с помощью метода Resilience4JCircuitBreakerFactory.configureDefault:

@Configuration
public class Resilience4JConfiguration {
    @Bean
    public Customizer<Resilience4JCircuitBreakerFactory> globalCustomConfiguration() {
        
        // the circuitBreakerConfig and timeLimiterConfig objects

        return factory -> factory.configureDefault(id -> new Resilience4JConfigBuilder(id)
          .timeLimiterConfig(timeLimiterConfig)
          .circuitBreakerConfig(circuitBreakerConfig)
          .build());
    } 
}

~ ~~ 6. Конкретная пользовательская конфигурация

Конечно, в нашем приложении может быть несколько автоматических выключателей. Поэтому в некоторых случаях нам нужна определенная конфигурация для каждого автоматического выключателя.

Точно так же мы можем определить один или несколько bean-компонентов Customizer. Затем мы можем предоставить разные конфигурации для каждого из них, используя метод Resilience4JCircuitBreakerFactory.configure:

@Bean
public Customizer<Resilience4JCircuitBreakerFactory> specificCustomConfiguration1() {

    // the circuitBreakerConfig and timeLimiterConfig objects

    return factory -> factory.configure(builder -> builder.circuitBreakerConfig(circuitBreakerConfig)
      .timeLimiterConfig(timeLimiterConfig).build(), "circuitBreaker");
}

Здесь мы предоставляем второй параметр, идентификатор настраиваемого автоматического выключателя.

Мы также можем настроить несколько автоматических выключателей с одинаковой конфигурацией, предоставив список идентификаторов автоматических выключателей одному и тому же методу:

@Bean
public Customizer<Resilience4JCircuitBreakerFactory> specificCustomConfiguration2() {

    // the circuitBreakerConfig and timeLimiterConfig objects

    return factory -> factory.configure(builder -> builder.circuitBreakerConfig(circuitBreakerConfig)
      .timeLimiterConfig(timeLimiterConfig).build(),
        "circuitBreaker1", "circuitBreaker2", "circuitBreaker3");
}

7. Альтернативные реализации

Мы видели, как использовать Resilience4j реализация для создания одного или нескольких автоматических выключателей с помощью Spring Cloud Circuit Breaker.

Однако есть и другие реализации, поддерживаемые Spring Cloud Circuit Breaker, которые мы можем использовать в нашем приложении:

    Hystrix Sentinel Spring Retry

Стоит отметить, что мы можем смешивать и сочетать различные реализации прерывателя цепи в нашем приложении. Мы не ограничиваемся одной библиотекой.

Вышеуказанные библиотеки имеют больше возможностей, чем мы рассмотрели здесь. Однако Spring Cloud Circuit Breaker является абстракцией только над частью автоматического выключателя. Например, Resilience4j также предоставляет другие модули, такие как RateLimiter, Bulkhead, Retry, в дополнение к модулям CircuitBreaker и TimeLimiter, использованным в этой статье.

8. Заключение

В этой статье мы обнаружили проект Spring Cloud Circuit Breaker.

Во-первых, мы узнали, что такое Spring Cloud Circuit Breaker и как он позволяет нам добавлять автоматические выключатели в наше приложение.

Затем мы использовали механизм автоматической настройки Spring Boot, чтобы показать, как определять и интегрировать автоматические выключатели. Кроме того, мы продемонстрировали, как Spring Cloud Circuit Breaker работает через простой сервис REST.

Наконец-то мы научились настраивать все автоматические выключатели как вместе, так и по отдельности.

Как всегда, исходный код этого руководства доступен на GitHub.