Мы уже обсудили, что такое Circuit Breaker, почему он важен для микросервисов, и как использовать библиотеку Resilience4j для его реализации. В этой лекции мы перейдём от теории к практике и разберём, как интегрировать Circuit Breaker в реальное приложение, а также настроим его работу.
Постановка задачи
Представьте, что у нас есть микросервис order-service, который обращается к другому микросервису inventory-service для проверки доступности товаров на складе. Иногда inventory-service может испытывать проблемы: быть недоступным или отвечать слишком долго. Наша цель — настроить Circuit Breaker в order-service, чтобы он корректно обрабатывал такие ситуации.
Подготовка проекта
Мы будем работать со Spring Boot-приложением. Если вы ещё не создали проект, воспользуйтесь Spring Initializr, чтобы сгенерировать заготовку. Убедитесь, что проект использует Java 17 (или выше), а также добавьте зависимости:
- Spring Web для обработки запросов.
- Resilience4j для нашего Circuit Breaker.
- Spring Boot Actuator для мониторинга (пригодится позже).
Пример настройки зависимости в pom.xml для Maven:
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Resilience4j -->
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot2</artifactId>
</dependency>
<!-- Spring Boot Actuator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
Если вы используете Gradle — вот как выглядят нужные зависомости:
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'io.github.resilience4j:resilience4j-spring-boot2'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
}
После добавления зависимостей не забудьте обновить проект!
Реализация Circuit Breaker
Шаг 1: Создание REST-клиента
Для взаимодействия с inventory-service создадим RestTemplate. Вот пример создания REST-клиента в @Configuration-классе:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
Шаг 2: Сервис для взаимодействия с inventory-service
Теперь добавим сервис, который использует RestTemplate для отправки HTTP-запросов:
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
public class InventoryService {
private final RestTemplate restTemplate;
public InventoryService(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
public String checkInventory(String productId) {
String inventoryUrl = "http://inventory-service/api/inventory/" + productId;
return restTemplate.getForObject(inventoryUrl, String.class);
}
}
Шаг 3: Добавление Circuit Breaker
Теперь мы добавим Circuit Breaker в наш InventoryService с использованием Resilience4j. Spring Boot позволяет сделать это с минимальными усилиями благодаря аннотации @CircuitBreaker.
Вот обновлённый код:
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
public class InventoryService {
private final RestTemplate restTemplate;
public InventoryService(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
@CircuitBreaker(name = "inventoryService", fallbackMethod = "fallbackCheckInventory")
public String checkInventory(String productId) {
String inventoryUrl = "http://inventory-service/api/inventory/" + productId;
return restTemplate.getForObject(inventoryUrl, String.class);
}
// Метод для обработки отказов
public String fallbackCheckInventory(String productId, Throwable throwable) {
return "Inventory service is currently unavailable. Please try again later.";
}
}
Шаг 4: Настройка параметров Circuit Breaker
Для настройки Resilience4j мы используем application.yml или application.properties. Создайте соответствующую конфигурацию:
resilience4j:
circuitbreaker:
instances:
inventoryService:
failureRateThreshold: 50
waitDurationInOpenState: 10s
minimumNumberOfCalls: 5
slidingWindowSize: 10
Эти параметры означают:
failureRateThreshold: порог отказов (в процентах), после которого Circuit Breaker перейдёт в состояние Open.waitDurationInOpenState: время, которое Circuit Breaker находится в состоянии Open, перед попыткой снова перейти в Half-Open.minimumNumberOfCalls: минимальное количество вызовов для вычисления отказов.slidingWindowSize: размер окна для подсчёта метрик.
Тестирование Circuit Breaker
Шаг 1: Простой контроллер
Для тестирования нашей реализации добавим контроллер:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class OrderController {
private final InventoryService inventoryService;
public OrderController(InventoryService inventoryService) {
this.inventoryService = inventoryService;
}
@GetMapping("/order")
public String placeOrder(@RequestParam String productId) {
return inventoryService.checkInventory(productId);
}
}
Шаг 2: Проверка работы
- Запустите приложение.
- Вызовите API
/order?productId=123через Postman или CURL. - Имитируйте отказ
inventory-service(например, укажите несуществующий URL). Проверьте, что Circuit Breaker переходит в состояние Open и выдаёт ответ из метода Fallback.
Анализ и оптимизация
Чтобы убедиться, что Circuit Breaker работает так, как мы ожидаем, можно подключить мониторинг через Actuator. Добавьте в application.yml:
management:
endpoints:
web:
exposure:
include: resilience4j.circuitbreakers
Теперь откройте URL /actuator/resilience4j/circuitbreakers и увидите текущие состояния Circuit Breaker.
Заключительный штрих
Ура! Мы настроили Circuit Breaker для нашего микросервиса, интегрировали его с Resilience4j и протестировали его работу. Благодаря этому наша система стала более отказоустойчивой и готовой к реальным нагрузкам. В следующей лекции мы рассмотрим, как добавить Retry и Timeout для ещё большей стабильности. А пока можете гордиться своей первой "переключалкой цепей"!