JavaRush /Курсы /Модуль 5. Spring /Лекция 277: Реализация Circuit Breakers для защиты сервис...

Лекция 277: Реализация Circuit Breakers для защиты сервисов

Модуль 5. Spring
28 уровень , 6 лекция
Открыта

Circuit Breaker можно представить как три состояния, через которые он проходит в процессе работы:

  1. Closed (замкнутое состояние): Все запросы пропускаются. Если начинают происходить ошибки, Circuit Breaker переходит в состояние Open.
  2. Open (разомкнутое состояние): Запросы блокируются. Это состояние активируется, когда система обнаруживает, что зависимость не работает должным образом.
  3. Half-Open (полуоткрытое состояние): Открывается небольшой «лаз», через который пропускаются несколько запросов, чтобы проверить, восстановилась ли зависимость. Если запросы успешны, Circuit Breaker возвращается в состояние Closed. Если же запросы снова проваливаются — состояние возвращается в Open.

Вот простая визуализация:


Ошибки                  Восстановление
Closed  --------------------->  Open  --------------------->  Half-Open
           (Таймер активен)            (Проверочные запросы)
                     <-----------------------------------
                     Успехи

Инструмент для реализации: Resilience4j

В Java-приложениях реализация Circuit Breaker упрощается за счёт использования библиотеки Resilience4j. Она модульна, лёгкая, поддерживает Spring Boot и позволяет настраивать Circuit Breaker на уровне конфигурации.

Начнём с подключения зависимости Resilience4j в наш pom.xml:


<dependency>
    <groupId>io.github.resilience4j</groupId>
    <artifactId>resilience4j-spring-boot2</artifactId>
    <version>2.0.2</version>
</dependency>

Если вы используете Gradle:

implementation 'io.github.resilience4j:resilience4j-spring-boot2:2.0.2'

Настройка Circuit Breaker

Resilience4j позволяет настроить Circuit Breaker как через Java-код, так и через файл конфигурации. Мы рассмотрим оба подхода.

Добавим настройки для Circuit Breaker'а в файл application.yml:


resilience4j.circuitbreaker:
  instances:
    myServiceBreaker:
      slidingWindowSize: 10            # Количество запросов в окне
      failureRateThreshold: 50         # Порог ошибок (в процентах)
      waitDurationInOpenState: 10s     # Время ожидания в состоянии Open
      permittedNumberOfCallsInHalfOpenState: 3  # Проверочные запросы
      minimumNumberOfCalls: 5          # Минимум запросов перед активацией
      automaticTransitionFromOpenToHalfOpenEnabled: true

Теперь добавим Circuit Breaker к методу, который обращается к другому сервису. Например, у нас есть микросервис, вызывающий внешний API:


@Service
public class MyService {

    private final RestTemplate restTemplate;

    public MyService(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    @CircuitBreaker(name = "myServiceBreaker", fallbackMethod = "fallbackResponse")
    public String callExternalService() {
        String url = "http://external-service/api/data";
        return restTemplate.getForObject(url, String.class);
    }

    public String fallbackResponse(Throwable t) {
        // Возвращаем простое сообщение при отказе
        return "Сервис временно недоступен. Пожалуйста, попробуйте позже.";
    }
}
  • @CircuitBreaker: Указывает, какой Circuit Breaker использовать (name).
  • fallbackMethod: Указывает метод, который будет вызван при сбое (в данном случае fallbackResponse).
ОПАСНОСТИ
Не забывайте, что сигнатура вашего fallbackMethod должна соответствовать сигнатуре основного метода, за исключением добавления Throwable в конце. В противном случае Spring просто не сможет вызвать его.

Реализация Circuit Breaker с Resilience4j

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

Подготовка продюсера и консьюмера Kafka

1. Продюсер:


@Service
public class EventProducer {

    private final KafkaTemplate<String, String> kafkaTemplate;

    public EventProducer(KafkaTemplate<String, String> kafkaTemplate) {
        this.kafkaTemplate = kafkaTemplate;
    }

    @CircuitBreaker(name = "kafkaBreaker", fallbackMethod = "fallbackSend")
    public void sendMessage(String topic, String message) {
        kafkaTemplate.send(topic, message);
    }

    public void fallbackSend(String topic, String message, Throwable t) {
        System.out.println("Не удалось отправить сообщение в Kafka. Сообщение будет отправлено позже.");
    }
}

2. Консьюмер:


@Component
public class EventConsumer {

    @KafkaListener(topics = "example-topic", groupId = "example-group")
    public void consume(String message) {
        System.out.println("Получено сообщение: " + message);
    }
}

Чтобы протестировать Circuit Breaker, мы можем симулировать сбой продюсера Kafka. Например, можно остановить Kafka и проверить, как система реагирует на сбой.

  1. При нормальной работе вы увидите сообщения от продюсера и консьюмера:
    
    Отправка сообщения: "Привет, Kafka!"
    Получено сообщение: "Привет, Kafka!"
    
  2. При сбое Kafka:
    
    Не удалось отправить сообщение в Kafka. Сообщение будет отправлено позже.
    

Политики отработки отказов

Resilience4j также поддерживает автоматическое повторение запросов (Retry), время ожидания (Timeout) и функционал Fallback. Эти механизмы можно комбинировать для улучшения отказоустойчивости.

Добавим настройку Retry для продюсера Kafka:


resilience4j.retry:
  instances:
    kafkaRetry:
      maxAttempts: 5
      waitDuration: 500ms

И обновим аннотацию:


@Retry(name = "kafkaRetry")
@CircuitBreaker(name = "kafkaBreaker", fallbackMethod = "fallbackSend")
public void sendMessage(String topic, String message) {
    kafkaTemplate.send(topic, message);
}

Теперь система будет пытаться отправить сообщение до 5 раз перед вызовом fallbackSend.


Тестируем поведение Circuit Breaker

Используйте тестовые окружения, чтобы проверить поведение Circuit Breaker под нагрузкой и в реальных условиях отказов. Например, вы можете:

  1. Отключить сервис-зависимость (в нашем случае, Kafka).
  2. Убедиться, что Circuit Breaker входит в состояние Open.
  3. Проверить, что вызовы начинают возвращать данные из fallbackMethod.

Circuit Breaker в реальной жизни

Circuit Breaker используется практически во всех реальных микросервисных системах. Например:

  • В Amazon Circuit Breaker защищает микросервисы от перегрузок при пиковом спросе.
  • Netflix применяет Circuit Breaker для своего потокового API, чтобы предотвратить каскадные отказы.
  • В финансовых системах Circuit Breaker критически важен, чтобы избежать сбоев обработки транзакций.

Поздравляем! Теперь вы готовы защитить свои микросервисы с помощью Circuit Breaker.

Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ