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

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

1. обзор

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

Принцип аналогичен электронному:Hystrix наблюдает за методами на предмет неудачных обращений к связанным службам. Если есть такой сбой, он откроет схему и переадресует вызов резервному методу.

Библиотека будет терпеть сбои вплоть до порога. Помимо этого, он оставляет цепь открытой. Это означает, что он будет перенаправлять все последующие вызовы в резервный метод, чтобы предотвратить будущие сбои. This creates a time buffer for the related service to recover from its failing state.с

2. REST Продюсер

Чтобы создать сценарий, демонстрирующий шаблонCircuit Breaker, нам сначала понадобится сервис. Мы назовем его «REST Producer», потому что он предоставляет данные для «REST Consumer» с поддержкой Hystrix, которые мы создадим на следующем шаге.

Давайте создадим новый проектMaven, используя зависимостьspring-boot-starter-web:


    org.springframework.boot
    spring-boot-starter-web
    1.4.0.RELEASE

Сам проект намеренно прост. Он состоит из интерфейса контроллера с одним аннотированным методом GET@RequestMapping, возвращающим простоString,,@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, потому что порт должен оставаться зарезервированным для приложения, описанного в следующем шаге.

Кроме того, мы определяем имя приложения, чтобы иметь возможность искать "REST Producer" в клиентском приложении, которое мы представим позже. Давайте создадимapplication.properties со следующим содержанием:

server.port=9090
spring.application.name=rest-producer

Теперь мы можем протестировать наш "REST Producer" с помощью curl:

$> curl http://localhost:9090/greeting/Cid
Hello Cid!

3. REST Потребитель сHystrix

В нашем демонстрационном сценарии мы реализуем веб-приложение, которое использует службуREST из предыдущего шага, используяRestTemplate иHystrix. Для простоты назовем его «REST Consumer».

Следовательно, мы создаем новый проект Maven сhttps://search.maven.org/classic/#search%7Cgav%7C1%7Cg%3A%22org.springframework.cloud%22%20AND%20a%3A%22spring-cloud-starter-hystrix%22, spring-boot-starter-web и_spring-boot-starter-thymeleaf в качестве зависимостей:


    org.springframework.cloud
    spring-cloud-starter-hystrix
    1.1.5.RELEASE


    org.springframework.boot
    spring-boot-starter-web
    1.4.0.RELEASE


    org.springframework.boot
    spring-boot-starter-thymeleaf
    1.4.0.RELEASE

ЧтобыCircuit Breaker работал,Hystix просканирует аннотированные классы@Component or @Service на предмет аннотированных методов@HystixCommand, реализует для него прокси и будет отслеживать его вызовы.

Сначала мы создадим класс@Service, который будет внедрен в@Controller. Так как мы создаемweb-application using Thymeleaf,, нам также понадобится HTML-шаблон, который будет служить представлением.

Это будет наш вводимый@Service, реализующий@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!";
    }
}

RestConsumerApplication будет нашим основным классом приложения. Аннотация@EnableCircuitBreaker просканирует путь к классам на предмет любой совместимой реализацииCircuit Breaker.

Чтобы использоватьHystrix явно, вы должны аннотировать этот класс с помощью@EnableHystrix:

@SpringBootApplication
@EnableCircuitBreaker
public class RestConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(RestConsumerApplication.class, args);
    }
}

Мы настроим контроллер, используя нашGreetingService:

@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

Чтобы увидеть автоматический выключатель Hystix в действии, мы запускаем «REST Consumer» и указываем в браузере наhttp://localhost:8080/get-greeting/Cid. При нормальных обстоятельствах будет показано следующее:

Hello Cid!

Чтобы смоделировать сбой нашего «REST Producer», мы просто остановим его, и после того, как мы закончим обновление браузера, мы должны увидеть общее сообщение, возвращаемое из резервного метода в нашем@Service:

Hello User!

4. REST Consumer сHystrix иFeign

Теперь мы собираемся изменить проект из предыдущего шага, чтобы использоватьSpring Netflix Feign в качестве декларативного клиента REST вместо SpringRestTemplate.

Преимущество состоит в том, что позже мы сможем легко реорганизовать наш интерфейс Feign Client, чтобы использоватьSpring Netflix Eureka для обнаружения сервисов.

Чтобы начать новый проект, мы сделаем копию нашего «REST Consumer» и добавим «REST Producer» иspring-cloud-starter-feign в качестве зависимостей:


    com.example.spring.cloud
    spring-cloud-hystrix-rest-producer
    1.0.0-SNAPSHOT


    org.springframework.cloud
    spring-cloud-starter-feign
    1.1.5.RELEASE

Теперь мы можем использоватьGreetingController для расширения Feign Client. Мы реализуем резервный вариантHystrix как статический внутренний класс, аннотированный@Component.

В качестве альтернативы мы могли бы определить аннотированный метод@Bean, возвращающий экземпляр этого резервного класса.

Свойство name@FeignClient является обязательным. Он используется для поиска приложения либо путем обнаружения службы черезEureka Client, либо через URL-адрес, если это свойство задано. Чтобы узнать больше об использовании Spring Netflix Eureka для обнаружения сервисов, посмотритеat this article:

@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!";
        }
    }
}

ВRestConsumerFeignApplication мы поместим дополнительную аннотацию, чтобы включить интеграциюFeign, фактически@EnableFeignClients, в основной класс приложения:

@SpringBootApplication
@EnableCircuitBreaker
@EnableFeignClients
public class RestConsumerFeignApplication {

    public static void main(String[] args) {
        SpringApplication.run(RestConsumerFeignApplication.class, args);
    }
}

Мы собираемся модифицировать контроллер, чтобы использовать автоматически подключенный клиент 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

Наконец, мы протестируем этотFeign «REST Consumer» с включенной функциейREST Consumer, как в предыдущем разделе. Ожидаемый результат должен быть таким же.

5. Откат кеширования сHystrix

Теперь мы собираемся добавить Hystrix в наш проектSpring Cloud. В этом облачном проекте у нас есть сервис оценки, который общается с базой данных и получает рейтинги книг.

Предположим, что наша база данных является востребованным ресурсом, и задержка ответа может меняться по времени или может быть недоступна по времени. Мы обработаем этот сценарий, когдаHystrixCircuit-Breaker откатится к кешу для данных.

5.1. Настройка и настройка

Добавим зависимостьspring-cloud-starter-hystrix к нашему рейтинговому модулю:


    org.springframework.cloud
    spring-cloud-starter-hystrix

Когда рейтинги вставляются / обновляются / удаляются в базе данных, мы реплицируем их в кеш Redis с помощьюRepository. Если вы хотите узнать больше о Redis, проверьтеthis article.

Давайте обновимRatingService, чтобы обернуть методы запросов к базе данных в команде Hystrix с помощью@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);
}

Обратите внимание, что резервный метод должен иметь одинаковую сигнатуру упакованного метода и должен находиться в том же классе. Теперь, когдаfindRatingById выходит из строя или задерживается больше, чем заданный порог,Hystrix откатывается кfindCachedRatingById.

Поскольку возможностиHystrix прозрачно вводятся как рекомендацияAOP, мы должны скорректировать порядок, в котором рекомендации складываются, на случай, если у нас есть другие советы, например, транзакционные советы Spring. Здесь мы скорректировали рекомендацию AOP транзакции Spring, чтобы она имела более низкий приоритет, чем рекомендацияHystrix AOP:

@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’s транзакцииAOP, чтобы она имела более низкий приоритет, чем рекомендацияHystrix AOP.

5.2. Тестирование Hystrix Fallback

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

Давайте скопируемH2 library (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

Мы можем запустить наши службы, как указано в наших предыдущихarticle из серии Spring-Cloud, и протестировать рейтинги каждой книги, отключив внешний экземпляр H2, который мы запускаем.

Мы могли видеть, что, когда база данныхH2 недоступна,Hystrix автоматически возвращается кRedis, чтобы прочитать оценки каждой книги. Исходный код, демонстрирующий этот вариант использования, можно найти вhere.

6. Использование областей видимости

Обычно аннотированный метод@HytrixCommand выполняется в контексте пула потоков. Но иногда он должен работать в локальной области, например,@SessionScope или@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 нашего "REST Consumer":


    org.springframework.cloud
    spring-cloud-starter-hystrix-dashboard
    1.1.5.RELEASE


    org.springframework.boot
    spring-boot-starter-actuator
    1.4.0.RELEASE

Первый необходимо включить, аннотируя@Configuration с помощью@EnableHystrixDashboard, а второй автоматически включает необходимые метрики в нашем веб-приложении.

После перезапуска приложения мы укажем в браузереhttp://localhost:8080/hystrix, введем URL-адрес метрики «hystrix.stream» и начнем мониторинг.

Наконец, мы должны увидеть что-то вроде этого:

image

Мониторинг «hystrix.stream» - это нормально, но если вам придется смотреть несколько приложений с поддержкойHystrix, это станет неудобным. Для этой целиSpring Cloud предоставляет инструмент под названиемTurbine, который может агрегировать потоки для представления в одномHystrix Dashboard.

НастройкаTurbine выходит за рамки этой статьи, но здесь следует упомянуть такую ​​возможность. Таким образом, можно также собирать эти потоки через обмен сообщениями, используяTurbine Stream.

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

Как мы уже видели, теперь мы можем реализовать шаблонCircuit Breaker, используяSpring Netflix Hystrix вместе сSpring RestTemplate илиSpring Netflix Feign.

Это означает, что мы можем использовать сервисы с включенным запасным вариантом, используя «статические» или, точнее, «стандартные» данные, и мы можем отслеживать использование этих данных.

Как обычно, вы найдете источники наGitHub.