Рубрики
Без рубрики

Руководство по Spring Cloud Netflix – Hystrix

В статье показано, как настроить резервную версию логики приложения с помощью Spring Cloud Hystrix.

Автор оригинала: baeldung.

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 .