Статьи

Создание собственных приложений для облачной среды Spring — часть 5

В следующем разделе учебника мы обсудим очень важную тему, когда речь идет о приложениях микросервисов, автоматических выключателях.

circuit_breaker

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

Как владелец сервиса, мы хотим оградить себя от проблем в наших зависимых сервисах, так как мы это делаем? Одним из решений является использование шаблона автоматического выключателя, который был представлен Майклом Найгардом в его книге « Освободи его» .

скачать

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

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

Добавление автоматических выключателей в наш код

В нашем приложении есть 2 места, где один сервис вызывает другой сервис. Первое, что должно прийти в голову, — это то, где мы используем прокси Zuul (из части 2  этого урока). Прокси Zuul используется в нашем веб-приложении для передачи наших вызовов JavaScript из нашего веб-приложения на другие микросервисы. К счастью, Spring Cloud автоматически защищает все эти звонки с помощью автоматических выключателей, поэтому мы ничего не должны делать:)

Другой удаленный вызов, который мы делаем в нашем приложении, включает API, который мы добавили для мобильных клиентов в последнем сообщении в блоге . В этом API мы делаем запрос в службу Участников из нашей службы Races. Если по какой-либо причине наша служба Участников не работает или не отвечает достаточно быстро, наша Служба Гонок пострадает. Вместо того чтобы прерывать работу нашего сервиса Races из-за проблем со службой участников, мы можем защитить удаленный вызов с помощью автоматического выключателя и фактически вернуть что-то нашим клиентам, даже если есть проблема со службой участников. То, чем мы отвечаем, может быть не идеальным или иметь всю информацию, которая нужна нашим клиентам, но это лучше, чем сбой вызова или тайм-аут. Давайте посмотрим, как мы используем Hystrix в нашем сервисе Races.

Чтобы использовать Hystrix, нам нужно добавить зависимость Hystrix в наш сервис Races. Откройте файл POM для службы Races и добавьте следующую зависимость в раздел зависимостей.

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>

Теперь, когда у нас есть зависимость, мы можем начать использовать Hystrix. В нашем последнем сообщении в блоге мы создали клиента Feign для удаленных вызовов службы «Участники» и добавили новый REST API в / members, который использовал клиент Feign для получения информации об участнике и добавления ее к данным гонки. Нам нужно защитить этот вызов от возможных сбоев, поэтому очевидным решением будет добавить автоматический выключатель вокруг REST API. К сожалению, сейчас мы не можем обернуть контроллер REST в автоматический выключатель (см. Эту проблему GitHub ). Из-за этого ограничения нам нужно будет разбить вызов клиента Feign на его собственный Бин. Откройте OcrRacesApplication.java и обновите OcrRacesApplication.Класс и добавить новый компонент с именем ParticipanBean .

@SpringBootApplication
@RestController
@EnableEurekaClient 
@EnableFeignClients
@EnableCircuitBreaker
public class OcrRacesApplication implements CommandLineRunner {
    
    private static List<Race> races = new ArrayList<Race>();
    @Autowired
    private ParticipantsBean participantsBean;

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

    @Override
    public void run(String... arg0) throws Exception {
        races.add(new Race("Spartan Beast", "123", "MA", "Boston"));
        races.add(new Race("Tough Mudder RI", "456", "RI", "Providence"));
    }
    
    @RequestMapping("/")
    public List<Race> getRaces() {
        return races;
    }
    
    @RequestMapping("/participants")
    public List<RaceWithParticipants> getRacesWithParticipants() {
        List<RaceWithParticipants> returnRaces = new ArrayList<RaceWithParticipants>();
        for(Race r : races) {
            returnRaces.add(new RaceWithParticipants(r, participantsBean.getParticipants(r.getId())));
        }
        return returnRaces;
    }
}  

 @Component
class ParticipantsBean {
    @Autowired
    private ParticipantsClient participantsClient;
    
    @HystrixCommand(fallbackMethod = "defaultParticipants")
    public List<Participant> getParticipants(String raceId) {
        return participantsClient.getParticipants(raceId);
    }
    
    public List<Participant> defaultParticipants(String raceId) {
        return new ArrayList<Participant>();
    }
}

В классе OcrRacesApplication мы добавили аннотацию @EnableCircuitBreake r для включения автоматических выключателей в нашем приложении. Следующее изменение в этом классе — в нашем API / Участники, где мы теперь вызываем наш новый компонент вместо клиента Feign напрямую. В новом bean-компоненте мы просто заключаем вызов клиента Feign в метод getParticipants . Это метод, который мы включаем в автоматический выключатель, так как он использует удаленный сервис. Мы включаем функциональность автоматического выключателя, используя аннотацию @HystrixCommand для этого метода. В аннотации мы указываем резервный метод для вызова, если цепь разомкнута. Если цепь разомкнута, мы вызываем метод нашего компонента, называемыйdefaultParticipants, который просто возвращает пустой список. Вы можете делать все, что захотите, в своем методе резервирования, и в целом это будет более изощренным, чем возвращение пустого списка, но для этого примера достаточно пустого списка. В производственном приложении, возможно, наши сервисы Races будут кэшировать данные участников, поэтому нам есть что вернуть, если цепь когда-либо будет открыта.

Это все, что нам нужно сделать, теперь наш удаленный вызов службы «Участники» защищен автоматическим выключателем.

Hystrix Dashboard

Приятно иметь автоматические выключатели в наших сервисах, но как мы узнаем состояние цепей? К счастью, Netflix и Spring Cloud предоставляют веб-приложение под названием  Hystrix Dashboard,  которое предоставляет нам необходимую нам информацию. Эта панель дает разработчикам и операциям понимание различной статистики о цепях в их приложениях, таких как успешность и частота отказов. В дополнение к панели инструментов Hystrix, Netflix и Spring Cloud также предлагают еще один инструмент под названием Turbine . Turbine помогает объединять различные потоки данных Hystrix в один поток, поэтому вам не нужно постоянно переключать потоки на панели мониторинга для просмотра данных из разных экземпляров службы.

Чтобы воспользоваться этими инструментами в нашем приложении, давайте добавим новый сервис в наше приложение для их размещения. Перейдите на start.spring.io и создайте новый проект на основе следующего изображения.

 

Снимок экрана 2015-10-07 в 9.16.21

 

Убедитесь, что вы добавили панели управления Hystrix Dashboard и Turbine. Заполнив форму, нажмите «Создать проект», чтобы загрузить архив и импортировать проект в рабочее пространство. Чтобы включить панель инструментов Hystrix, нам нужно добавить одну аннотацию в com.ryanjbaxter.spring.cloud.ocr.hystrix.HystrixDashboardApplication . Откройте этот файл класса и добавьте  @EnableHystrixDashboard в файл класса.

package com.ryanjbaxter.spring.cloud.ocr.hystrix;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
import org.springframework.cloud.netflix.turbine.EnableTurbine;

@SpringBootApplication
@EnableHystrixDashboard 
public class HystrixDashboardApplication {

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

Единственное, что мы должны сделать сейчас, это немного настроить. Мы хотим изменить порт, на котором будут работать службы Hystrix Dashboard и Turbine, поэтому перейдите в src / main / resources в новом проекте и переименуйте application.properties в application.yml . Затем добавьте следующие свойства в файл YAML.

server:
  port: 8383

Запустите приложение, которое будет работать на порту 8383, и перейдите по адресу http: // localhost: 8383 / hystrix . Вы должны увидеть страницу, которая выглядит следующим образом.Снимок экрана 2015-10-07 в 9.28.44

Обязательное поле в этой форме — это URL-адрес потока Hystrix или Turbine. Мы еще не настроили Turbine, поэтому давайте попробуем поток Hystrix. Запустите все остальные службы для приложения (Eureka, Races, Участники и Интернет) и подождите, пока все зарегистрируется в Eureka.

Как только все будет зарегистрировано, перейдите в веб-приложение по адресу http: // localhost: 8080 и нажмите на гонку, чтобы просмотреть участников. Этот шаг необходим для того, чтобы увидеть любую интересную статистику, касающуюся автоматических выключателей в Цууле. Теперь вернитесь на панель инструментов Hystrix, введите URL-адрес http: // localhost: 8080 / hystrix.stream  и нажмите Monitor Stream. В результате приборная панель должна выглядеть примерно так, как на скриншоте ниже.

Снимок экрана 2015-10-07 в 9.55.14 утра

Вы заметите, что у нас есть 2 автоматических выключателя, один для запросов вызова прокси-сервера к сервису Races, а другой для запросов вызова прокси-сервера к сервису участников. Если вы начнете обновлять веб-приложение, вы заметите изменение панели мониторинга, поскольку оно отслеживает запросы через автоматические выключатели. Однако, как правило, вы не можете сделать очень хорошую работу, имитируя нагрузку на службу, обновляя страницу вручную в браузере. Один инструмент, который может лучше имитировать нагрузку на наши сервисы, называется  Siege . Вы можете установить Siege через ваш любимый менеджер пакетов ( Homebrew , Yum, Apt-Get и т. Д.). После установки он довольно прост в использовании . Например, чтобы подключиться к сервису Races через наш прокси Zuul, вам просто нужно сделать

$ siege http://localhost:8080/races

Как только вы это сделаете, взгляните на информационную панель Hystrix, и вы увидите еще более интересные данные на информационной панели.

Снимок экрана 2015-10-07 в 10.05.21

 

Информацию о том, что означают все цифры на панели инструментов, можно найти на вики-странице Hystrix на GitHub.

Как насчет мониторинга автоматического выключателя, который мы добавили в сервис Races? Во-первых, давайте удостоверимся, что мы попали в API, чтобы у нас были некоторые данные. Перейдите на http: // localhost: 8282 / участники . Затем вернитесь на целевую страницу панели инструментов Hystrix (http: // localhost: 8383 / hystrix ), введите URL-адрес http: // localhost: 8282 / hystrix.stream и нажмите Monitor Stream. Когда вы делаете это, вы должны получить ошибку в панели управления, почему?

Снимок экрана 2015-10-07 в 10.38.23

Это связано с тем, что функциональность потока еще не была включена в приложении (Zuul автоматически включает ее, поэтому она работала «из коробки» в нашем веб-сервисе). Чтобы добавить Hystrix поток в приложение , нам нужно добавить Spring Загрузочный Привод стартера службы Races (как указано в Spring Cloud Netflix документации ). Откройте файл POM для службы Races и добавьте следующую зависимость в раздел зависимостей.

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

Сохраните POM и перезапустите сервис Races. Опять ударил / участников API в вашем браузере, перейдя HTTP: // Localhost: 8282 / участников . Теперь вернитесь на целевую страницу панели инструментов Hystrix, введите http: // localhost: 8282 / hystrix.stream и нажмите Monitor Stream. Теперь вы должны увидеть информацию о нашем методе getParticipants, защищенном нашим автоматическим выключателем.

Снимок экрана 2015-10-07 в 10.53.26

Опять же, если вы установите API под seige, вы начнете видеть более интересные данные. Но что происходит в случае неудачи? Если вы закрыли службу участников, просто прекратив ее запуск, а затем нажали на API или поставили API на seige, вы должны увидеть, что цепь разомкнута, и число сбоев в панели мониторинга возрастает.

Снимок экрана 2015-10-07 в 13.06.21

На приведенном выше скриншоте мы видим, что количество запросов начало сбой (фиолетовое число), и наша частота ошибок начинает расти, однако цепь все еще закрыта. На скриншоте ниже схема наконец открылась из-за количества неудачных запросов

 

Снимок экрана 2015-10-07 в 10.57.39 утра

Число в синем (548) — это количество вызовов, которые были закорочены или перешли на наш резервный метод, определенный в нашей @HystrixCommand . Поскольку схема разомкнута, если мы нажмем на API в браузере, мы увидим, что возвращаются пустые списки для данных участников, так как это поведение мы определили в нашем резервном методе. Попробуйте , перейдите по адресу http: // localhost: 8282 / участники . Обратите внимание, что возвращаемые данные будут выглядеть так

[
   {
      "name":"Spartan Beast",
      "id":"123",
      "state":"MA",
      "city":"Boston",
      "participants":[

      ]
   },
   {
      "name":"Tough Mudder RI",
      "id":"456",
      "state":"RI",
      "city":"Providence",
      "participants":[

      ]
   }
]

Нет данных об участниках, как ожидалось.

Теперь, если мы запустим сервис участников, резервная копия должна закрыться, и наши запросы снова будут успешными. Но как схема узнает, что сервис восстановлен? Периодически схема пропускает пару запросов, чтобы увидеть, успешно они или нет. Обратите внимание на 2 ошибки (красным) на скриншоте ниже. Когда эти запросы начнут выполняться успешно, цепь будет закрыта, и запросы будут пропущены.

Снимок экрана 2015-10-07 в 1.10.50 PM

Теперь, когда сервис восстановлен, все возвращается в нормальное состояние, и мы видим, что цепь замкнута.

Снимок экрана 2015-10-07 в 13:7.17 PM

 

Использование турбины

Это все хорошо, но переключение между различными потоками Hystrix может быть проблематичным, было бы неплохо иметь возможность видеть потоки на основе идентификатора службы. Вот тут-то и появляется Turbine. Когда мы создавали наш проект Hystrix Dashboard на start.spring.io, мы добавили зависимость Turbine в наш проект, чтобы у нас были все зависимости, которые нам нужны уже в нашем POM. Чтобы включить Turbine, нам нужно добавить одну аннотацию к  com.ryanjbaxter.spring.cloud.ocr.hystrix.HystrixDashboardApplication . Откройте файл класса и добавьте  @EnableTurbine

package com.ryanjbaxter.spring.cloud.ocr.hystrix;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
import org.springframework.cloud.netflix.turbine.EnableTurbine;

@SpringBootApplication
@EnableHystrixDashboard
@EnableTurbine
public class HystrixDashboardApplication {

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

Теперь нам нужно сообщить Turbine, для каких сервисов мы собираем информацию. Откройте src / main / resources / application.yml и добавьте следующие свойства конфигурации.

server:
  port: 8383
  
turbine:
  appConfig: web,races
  aggregator:
    clusterConfig: WEB,RACES

Свойство turbine.appConfig указывает имена служб, которые вы хотите объединить. Эти значения должны соответствовать идентификаторам сервисов, которые мы уже настроили с помощью Eureka. Параметр turbine.aggregator.clusterConfig — это имена кластеров, которые также должны совпадать с идентификаторами служб, с той лишь разницей, что они ДОЛЖНЫ быть прописными.

После добавления новой аннотации и дополнительной конфигурации перезапустите приложение Hystrix Dashboard. Как только приложение вернется в прежнее состояние, снова перейдите на панель мониторинга Hystrix ( http: // localhost: 8383 / hystrix ). Теперь вместо ввода URL-адресов в отдельные потоки наших сервисов Hystrix, давайте использовать Turbine. Введите URL-адрес http: // localhost: 8383 / turbine.stream? Cluster = WEB и нажмите Мониторинг потока. Это должно включить все автоматические выключатели в веб-сервисе (тот, который использует наш прокси Zuul). Вы должны увидеть автоматические выключатели для маршрутов участников и гонок, отображаемые на панели инструментов, как если бы вы отслеживали Hystrix Stream отдельной службы.

Что если мы хотим посмотреть на автоматические выключатели для сервиса Races? Все, что нам нужно сделать, это настроить параметр запроса кластера в URL турбины, чтобы он указывал на кластер Races. Вернитесь на целевую страницу Hystrix, нажав кнопку «Назад» в своем браузере или вернувшись по адресу http: // localhost: 8383 / hystrix . Теперь введите тот же URL Turbine, но с параметром запроса Races, http: // localhost: 8383 / turbine.stream? Cluster = RACES, Опять же, то, что вы увидите, должно быть таким же, как если бы мы указывали на поток Races Hystrix. Очевидным преимуществом здесь является то, что мы можем отслеживать потоки Hystrix на основе идентификатора службы, а не URL. Другое преимущество, которое менее очевидно, заключается в том, что если бы мы масштабировали эти службы по горизонтали, мы бы увидели статистику по всем экземплярам этой службы, а не только по отдельной услуге. Это будет более полезно, когда наше приложение работает в облаке … не волнуйтесь, мы дойдем до конца:)

Как вы можете видеть, автоматические выключатели играют очень важную роль в приложении для микросервисов, и, как обычно, Spring Cloud позволяет не только использовать их в вашем приложении, но и отслеживать их состояние в вашем приложении.