1. Обзор
В этом уроке мы рассмотрим Spring Cloud Netflix Hystrix – библиотеку отказоустойчивости. Мы используем библиотеку и реализуем корпоративный шаблон автоматического выключателя, который описывает стратегию защиты от каскадирования отказов на разных уровнях приложения.
Принцип аналогичен электронике: Hystrix наблюдает за методами неудачных вызовов связанных служб. Если произойдет такой сбой, он разомкнет цепь и переадресует вызов резервному методу.
Библиотека будет терпеть сбои вплоть до порога. Кроме того, он оставляет цепь открытой. Это означает, что он будет перенаправлять все последующие вызовы резервному методу, чтобы предотвратить будущие сбои. Это создает буфер времени для восстановления связанной службы из состояния сбоя.
2. Производитель ОТДЫХА
Чтобы создать сценарий, который демонстрирует схему автоматического выключателя, нам сначала нужна услуга. Мы назовем его “REST Producer”, поскольку он предоставляет данные для “REST Consumer” с поддержкой Hystrix, который мы создадим на следующем шаге.
Давайте создадим новый проект Maven с помощью зависимости spring-boot-starter-web :
org.springframework.boot spring-boot-starter-web 2.2.6.RELEASE
Сам проект намеренно упрощен. Он состоит из интерфейса контроллера с одним @RequestMapping аннотированным методом GET, возвращающим просто строку, a @RestController реализующим этот интерфейс и @SpringBootApplication .
Начнем с интерфейса:
public interface GreetingController { @GetMapping("/greeting/{username}") String greeting(@PathVariable("username") String username); }
И реализация:
@RestController public class GreetingControllerImpl implements GreetingController { @Override public String greeting(@PathVariable("username") String username) { return String.format("Hello %s!\n", username); } }
Далее мы запишем основной класс приложения:
@SpringBootApplication public class RestProducerApplication { public static void main(String[] args) { SpringApplication.run(RestProducerApplication.class, args); } }
Чтобы завершить этот раздел, единственное, что осталось сделать, это настроить порт приложения, на котором мы будем слушать. Мы не будем использовать порт по умолчанию 8080, потому что порт должен оставаться зарезервированным для приложения, описанного в следующем шаге.
Кроме того, мы определяем имя приложения, чтобы иметь возможность искать нашего производителя из клиентского приложения, которое мы представим позже.
Давайте тогда определим порт 9090 и имя rest-producer в нашем application.properties файле:
server.port=9090 spring.application.name=rest-producer
Теперь мы можем протестировать нашего производителя с помощью cURL:
$> curl http://localhost:9090/greeting/Cid Hello Cid!
3. ОТДЫХ Потребителя С Помощью Hystrix
Для нашего демонстрационного сценария мы будем реализовывать веб-приложение, которое использует службу REST из предыдущего шага, используя RestTemplate и Hystrix . Для простоты назовем его “Потребителем ОТДЫХА”.
Следовательно, мы создаем новый проект Maven с spring-cloud-starter- hystrix , spring-boot-starter-web и spring-boot-starter-thymeleaf в качестве зависимостей:
org.springframework.cloud spring-cloud-starter-hystrix 1.4.7.RELEASE org.springframework.boot spring-boot-starter-web 2.2.6.RELEASE org.springframework.boot spring-boot-starter-thymeleaf 2.2.6.RELEASE
Чтобы автоматический выключатель работал, Hystrix будет сканировать @Component или @Service аннотированные классы для @Hystrixcommand аннотированных методов, реализовывать прокси-сервер для него и отслеживать его вызовы.
Сначала мы создадим класс @Service , который будет введен в @Controller . Поскольку мы создаем веб-приложение с помощью Thymeleaf, нам также нужен HTML-шаблон для представления.
Это будет наш инъекционный @Сервис реализация @HystrixCommand с соответствующим резервным методом. Этот запасной вариант должен использовать ту же подпись, что и оригинал:
@Service public class GreetingService { @HystrixCommand(fallbackMethod = "defaultGreeting") public String getGreeting(String username) { return new RestTemplate() .getForObject("http://localhost:9090/greeting/{username}", String.class, username); } private String defaultGreeting(String username) { return "Hello User!"; } }
Rest Consumer Application будет нашим основным классом приложений. Аннотация @EnableCircuitBreaker будет сканировать путь к классу на наличие любой совместимой реализации автоматического выключателя.
Чтобы использовать Hystrix явно, мы должны аннотировать этот класс с помощью @EnableHystrix :
@SpringBootApplication @EnableCircuitBreaker public class RestConsumerApplication { public static void main(String[] args) { SpringApplication.run(RestConsumerApplication.class, args); } }
Мы настроим контроллер с помощью нашего Приветственного сервиса :
@Controller public class GreetingController { @Autowired private GreetingService greetingService; @GetMapping("/get-greeting/{username}") public String getGreeting(Model model, @PathVariable("username") String username) { model.addAttribute("greeting", greetingService.getGreeting(username)); return "greeting-view"; } }
А вот и HTML шаблон:
Greetings from Hystrix
Чтобы убедиться, что приложение прослушивает определенный порт, мы помещаем следующее в файл application.properties :
server.port=8080
Чтобы увидеть автоматический выключатель Hystrix в действии, мы запускаем нашего потребителя и указываем вашему браузеру на http://localhost:8080/get-greeting/Cid . При нормальных обстоятельствах будет показано следующее:
Hello Cid!
Чтобы имитировать сбой нашего производителя, мы просто остановим его, и после того, как мы закончим обновление браузера, мы должны увидеть общее сообщение, возвращенное из резервного метода в нашем @Service :
Hello User!
4. ОТДЫХ Потребителя С Истрикой и Притворством
Теперь мы изменим проект с предыдущего шага, чтобы использовать Spring Netflix Feign в качестве декларативного клиента REST вместо Spring RestTemplate .
Преимущество заключается в том, что позже мы сможем легко реорганизовать ваш симулированный клиентский интерфейс, чтобы использовать Spring Netflix Eureka для обнаружения служб.
Чтобы начать новый проект, мы сделаем копию нашего потребителя и добавим нашего производителя и spring-cloud-starter-feign в качестве зависимостей:
com.baeldung.spring.cloud spring-cloud-hystrix-rest-producer 1.0.0-SNAPSHOT org.springframework.cloud spring-cloud-starter-feign 1.1.5.RELEASE
Теперь мы можем использовать наш GreetingController для расширения симулированного клиента. Мы реализуем Hystrix fallback как статический внутренний класс, аннотированный @Component .
В качестве альтернативы мы могли бы определить аннотированный метод @Bean , возвращающий экземпляр этого резервного класса.
Свойство name объекта @FeignClient является обязательным. Он используется для поиска приложения либо путем обнаружения службы через клиент Eureka, либо по URL-адресу, если это свойство задано:
@FeignClient( name = "rest-producer" url = "http://localhost:9090", fallback = GreetingClient.GreetingClientFallback.class ) public interface GreetingClient extends GreetingController { @Component public static class GreetingClientFallback implements GreetingController { @Override public String greeting(@PathVariable("username") String username) { return "Hello User!"; } } }
Подробнее об использовании Spring Netflix Eureka для обнаружения сервисов читайте в этой статье .
В приложении Rest Consumer Feign мы поместим дополнительную аннотацию для включения интеграции Feign, фактически @EnableFeignClients , в основной класс приложения:
@SpringBootApplication @EnableCircuitBreaker @EnableFeignClients public class RestConsumerFeignApplication { public static void main(String[] args) { SpringApplication.run(RestConsumerFeignApplication.class, args); } }
Мы собираемся изменить контроллер, чтобы использовать клиент autowired Feign , а не ранее введенный @Service , чтобы получить наше приветствие:
@Controller public class GreetingController { @Autowired private GreetingClient greetingClient; @GetMapping("/get-greeting/{username}") public String getGreeting(Model model, @PathVariable("username") String username) { model.addAttribute("greeting", greetingClient.greeting(username)); return "greeting-view"; } }
Чтобы отличить этот пример от предыдущего, мы изменим порт прослушивания приложения в application.properties :
server.port=8082
Наконец, мы протестируем этого потребителя с поддержкой симуляции, как и в предыдущем разделе. Ожидаемый результат должен быть таким же.
5. Резервный Кэш С Помощью Hystrix
Теперь мы собираемся добавить Hystrix в наш проект Spring Cloud. В этом облачном проекте у нас есть рейтинговая служба, которая общается с базой данных и получает рейтинги книг.
Предположим, что наша база данных является ресурсом по требованию, и ее задержка отклика может изменяться во времени или может быть недоступна во времени. Мы справимся с этим сценарием, когда автоматический выключатель Hystrix вернется в кэш для данных.
5.1. Настройка и настройка
Давайте добавим spring-cloud-starter-hystrix зависимость к нашему модулю рейтинга:
org.springframework.cloud spring-cloud-starter-hystrix
Когда рейтинги будут вставлены/обновлены/удалены в базу данных, мы реплицируем их в кэш Redis с помощью репозитория . Чтобы узнать больше о Redis, проверьте эту статью.
Давайте обновим Рейтинговую службу , чтобы обернуть методы запроса базы данных в Hystrixcommand с помощью @HystrixCommand и настроить ее с помощью резервного копирования для чтения из Redis:
@HystrixCommand( commandKey = "ratingsByIdFromDB", fallbackMethod = "findCachedRatingById", ignoreExceptions = { RatingNotFoundException.class }) public Rating findRatingById(Long ratingId) { return Optional.ofNullable(ratingRepository.findOne(ratingId)) .orElseThrow(() -> new RatingNotFoundException("Rating not found. ID: " + ratingId)); } public Rating findCachedRatingById(Long ratingId) { return cacheRepository.findCachedRatingById(ratingId); }
Обратите внимание, что резервный метод должен иметь ту же сигнатуру обернутого метода и должен находиться в том же классе. Теперь, когда find Rating By Id терпит неудачу или задерживается больше заданного порога, Hystrix возвращается к findCachedRatingById.
Поскольку возможности Hystrix прозрачно вводятся в виде рекомендаций AOP, мы должны скорректировать порядок, в котором эти советы складываются, на случай, если у нас есть другие советы, такие как транзакционные советы Spring. Здесь мы скорректировали совет АОП транзакции Spring так, чтобы он имел более низкий приоритет, чем совет АОП Hystrix:
@EnableHystrix @EnableTransactionManagement( order=Ordered.LOWEST_PRECEDENCE, mode=AdviceMode.ASPECTJ) public class RatingServiceApplication { @Bean @Primary @Order(value=Ordered.HIGHEST_PRECEDENCE) public HystrixCommandAspect hystrixAspect() { return new HystrixCommandAspect(); } // other beans, configurations }
Здесь мы скорректировали совет АОП транзакции Spring так, чтобы он имел более низкий приоритет, чем совет АОП Hystrix.
5.2. Тестирование резервного варианта Hystrix
Теперь, когда мы настроили схему, мы можем протестировать ее, приведя в действие базу данных H2, с которой взаимодействует наш репозиторий. Но сначала давайте запустим экземпляр H2 как внешний процесс, а не как встроенную базу данных.
Давайте скопируем библиотеку H2 ( h2-1.4.193.jar ) в известный каталог и запустите сервер H2:
>java -cp h2-1.4.193.jar org.h2.tools.Server -tcp TCP server running at tcp://192.168.99.1:9092 (only local connections)
Теперь давайте обновим URL-адрес источника данных нашего модуля в rating-service.properties , чтобы указать на этот сервер H2:
spring.datasource.url = jdbc:h2:tcp://localhost/~/ratings
Мы можем запустить наши сервисы, как описано в нашей предыдущей статье из серии Spring Cloud, и протестировать рейтинги каждой книги, сбив внешний экземпляр H2, который мы запускаем.
Мы могли видеть, что когда база данных H2 недоступна, Hystrix автоматически возвращается к Redis, чтобы прочитать рейтинги для каждой книги. Исходный код, демонстрирующий этот вариант использования, можно найти здесь .
6. Использование областей видимости
Обычно аннотированный метод @Hystrixcommand выполняется в контексте пула потоков. Но иногда он должен быть запущен в локальной области, например, a @sessionScope или a @RequestScope . Это можно сделать, задав аргументы аннотации команды:
@HystrixCommand(fallbackMethod = "getSomeDefault", commandProperties = { @HystrixProperty(name = "execution.isolation.strategy", value = "SEMAPHORE") })
7. Панель управления Hystrix
Приятной дополнительной функцией Hystrix является возможность отслеживать ее состояние на приборной панели.
Чтобы включить его, мы поместим spring-cloud-starter-hystrix-dashboard и spring-boot-starter-actuator в pom.xml нашего потребителя:
org.springframework.cloud spring-cloud-starter-hystrix-dashboard 1.4.7.RELEASE org.springframework.boot spring-boot-starter-actuator 2.2.6.RELEASE
Первый должен быть включен с помощью аннотирования @Configuration с помощью @EnableHystrixDashboard , а второй автоматически включает необходимые метрики в нашем веб-приложении.
После перезапуска приложения мы направим браузер на http://localhost:8080/hystrix , введите URL-адрес метрики потока Hystrix и начните мониторинг.
Наконец, мы должны увидеть что-то вроде этого:
Мониторинг потока Hystrix – это нечто прекрасное, но если нам придется наблюдать за несколькими приложениями с поддержкой Hystrix, это станет неудобным. Для этой цели Spring Cloud предоставляет инструмент под названием Turbine, который может агрегировать потоки для представления в одной панели мониторинга Hystrix.
Настройка турбины выходит за рамки этой статьи, но здесь следует упомянуть о такой возможности. Таким образом, можно также собирать эти потоки через обмен сообщениями, используя Turbine stream.
8. Заключение
Как мы уже видели, теперь мы можем реализовать шаблон автоматического выключателя с помощью Spring Netflix Hystrix вместе с Spring RestTemplate или Spring Netflix Feign.
Это означает, что мы можем использовать службы с включенным резервным резервом, используя данные по умолчанию, и мы можем контролировать использование этих данных.
Как обычно, мы можем найти исходники на GitHub .