Статьи

Защитите свое приложение с помощью Hystrix

68747470733a2f2f6e6574666c69782e6769746875622e636f6d2f487973747269782f696d616765732f687973747269782d6c6f676f2d7461676c696e652d3835302e706e67

В предыдущем посте http://www.javacodegeeks.com/2014/07/rxjava-java8-java-ee-7-arquillian-bliss.html мы говорили о микросервисах и о том, как их организовать с помощью Reactive Extensions с использованием (RxJava). Но что происходит, когда один или несколько сервисов выходят из строя, потому что они остановлены или выдают исключение? В распределенной системе, такой как архитектура микросервисов, нормально, что удаленная служба может дать сбой, поэтому связь между ними должна быть отказоустойчивой и правильно управлять задержкой при сетевых вызовах.

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

В распределенной архитектуре, такой как микросервисы, один сервис может потребовать использования других сервисов в качестве зависимостей для выполнения своей работы. Каждая точка в приложении, которое обращается по сети или в клиентскую библиотеку, которая потенциально может привести к сетевым запросам, является источником сбоя. Хуже, чем сбои, эти приложения также могут привести к увеличению задержек между службами. И это оставляет нас к другой большой проблеме: предположим, что вы разрабатываете сервис на Tomcat, который откроет два соединения с двумя сервисами, и если один из этих сервисов займет больше времени, чем ожидалось, для отправки ответа, вы будете тратить один поток на Пул Tomcat (из текущего запроса) ничего не делает, вместо того, чтобы ждать ответа. Если у вас нет сайта с высоким трафиком, это может быть приемлемо, но если у вас значительный объем трафика, все ресурсы могут стать насыщенными и блокировать весь сервер.

Схема из этого сценария представлена ​​в википедии Hystrix :

СОА-3-640

Способ избежать предыдущей проблемы — добавить слой потока, который изолирует каждую зависимость друг от друга. Таким образом, каждая зависимость (служба) может содержать пул потоков для выполнения этой службы. В Hystrix этот уровень реализуется объектом HystricxCommand , поэтому каждый вызов внешней службы переносится для выполнения в другом потоке.

Схема этого сценария представлена ​​в википедии Hystrix :

СОА-4-изоляция-640

Но также Hystrix предоставляет другие функции:

  • У каждого потока есть тайм-аут, поэтому вызов не может быть бесконечным в ожидании ответа.
  • Выполняйте откат, где это возможно, чтобы защитить пользователей от сбоев.
  • Измерение успеха, сбоев (исключений, выданных клиентом), тайм-аутов и отклонений потоков и позволяет проводить мониторинг.
  • Реализует схему автоматического выключателя, которая автоматически или вручную останавливает все запросы к внешней службе на период времени, если процент ошибок превышает пороговое значение.

Итак, давайте начнем с очень простого примера:

01
02
03
04
05
06
07
08
09
10
11
public class HelloWorldCommand extends HystrixCommand<String> {
 
    public HelloWorldCommand() {
        super(HystrixCommandGroupKey.Factory.asKey("HelloWorld"));
    }
 
    @Override
    protected String run() throws Exception {
        return "Hello World";
    }
}

И затем мы можем выполнить эту команду синхронно, используя метод execute.

1
new HelloWorldCommand().execute();

Хотя эта команда является синхронной, она выполняется в другом потоке. По умолчанию Hystrix создает пул потоков для каждой команды, определенной внутри того же HystrixCommandGroupKey . В нашем примере Hystrix создает пул потоков, связанный со всеми командами, сгруппированными в пул потоков HelloWorld . Затем для каждого выполнения один поток получают из пула для выполнения команды.

Но, конечно, мы можем выполнить команду асинхронно (которая идеально подходит для асинхронных спецификаций JAX-RS 2.0 или Servlet 3.0 ). Для этого просто запустите:

1
2
3
Future<String> helloWorldResult = new HelloWorldCommand().queue();
//some more work
Stirng message = helloWorldResult.get();

Фактически синхронные вызовы реализуются внутри Hystrix как return new HelloWorldCommand (). Queue (). Get (); внутренне.

Мы видели, что мы можем выполнять команду синхронно и асинхронно, но есть третий метод — реактивное выполнение с использованием RxJava (вы можете узнать больше о RxJava в моем предыдущем посте http://www.javacodegeeks.com/2014/07/ rxjava-java8-java-ee-7-arquillian-bliss.html ).

Для этого вам просто нужно вызвать метод наблюдения:

1
2
3
4
Observable<String> obs = new HelloWorldCommand().observe();
obs.subscribe((v) -> {
    System.out.println("onNext: " + v);
}

Но иногда что-то может пойти не так, и выполнение команды может вызвать исключение. Все исключения, выбрасываемые из метода run (), за исключением HystrixBadRequestException, считаются как сбои и запускают getFallback () и логику автоматического выключателя (в дальнейшем будет рассказано о автоматическом выключателе). Любое бизнес-исключение, которое вы не хотите рассматривать как сбой службы (например, недопустимые аргументы), должно быть включено в HystrixBadRequestException .

Но что происходит со сбоями в обслуживании, что Hystrix может сделать для нас? Таким образом, Hystrix может предложить две вещи:

  1. Способ сделать что-то в случае сбоя службы. Этот метод может возвращать пустое значение по умолчанию или значение-заглушку или, например, может вызывать другую службу, которая может выполнить ту же логику, что и неисправная.
  2. Какая-то логика для автоматического размыкания и замыкания цепи.

Отступать

Метод, который вызывается при возникновении исключения (за исключением HystrixBadRequestException ), называется getFallback () . Вы можете переопределить этот метод и предоставить собственную реализацию.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
public class HelloWorldCommand extends HystrixCommand<String> {
 
    public HelloWorldCommand() {
        super(HystrixCommandGroupKey.Factory.asKey("HelloWorld"));
    }
 
    @Override
    protected String getFallback() {
        return "Good Bye";
    }
 
    @Override
    protected String run() throws Exception {
        //return "Hello World";
        throw new IllegalArgumentException();
    }
}

Выключатель

Circuit break — это программный шаблон для обнаружения сбоев и предотвращения постоянного получения одной и той же ошибки. Но также, если служба удаленная, вы можете выдать ошибку, не дожидаясь истечения времени ожидания соединения TCP.

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

Поэтому было бы очень полезно, если бы система могла обнаружить сбой службы и избежать того, чтобы клиенты делали больше запросов до некоторого периода времени. И это то, что делает автоматический выключатель. Для каждого выполнения проверяйте, является ли цепь открытой (сработавшей), что означает, что произошла ошибка, и запрос не будет отправлен в службу, и будет выполнена логика отката. Но если цепь замкнута, то запрос обрабатывается и может работать.

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

На следующей диаграмме с сайта Hystrix вы можете увидеть взаимодействие между Hystrix и схемой.

Hystrix-блок-схема оригинал

Теперь, когда мы ознакомились с основами Hystrix , давайте посмотрим, как писать тесты, чтобы проверить, что Hystrix работает должным образом.

Последняя вещь перед тестом. В Hystrix есть специальный класс, называемый HystrixRequestContext . Этот класс содержит состояние и управляет жизненным циклом запроса. Вам нужно инициализировать этот класс, если, например, вы хотите, чтобы Hystrix управлял результатами кэширования или для целей ведения журнала. Обычно этот класс инициализируется непосредственно перед запуском бизнес-логики (например, в фильтре сервлетов ) и завершается после обработки запроса.

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
public class HelloWorldCommand extends HystrixCommand<String> {
 
    public HelloWorldCommand() {
        super(HystrixCommandGroupKey.Factory.asKey("HelloWorld"));
    }
 
    @Override
    protected String getFallback() {
        return "Good Bye";
    }
 
    @Override
    protected String run() throws Exception {
        return "Hello World";
    }
}

И тест. Имейте в виду, что я добавил в тест много утверждений для академических целей.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Test
public void should_execute_fallback_method_when_circuit_is_open() {
   
  //Initialize HystrixRequestContext to be able to get some metrics
  HystrixRequestContext context = HystrixRequestContext.initializeContext();
  HystrixCommandMetrics creditCardMetrics = HystrixCommandMetrics.getInstance(HystrixCommandKey.Factory.asKey(HelloWorldRestCommand.class.getSimpleName()));
   
  //We use Archaius to set the circuit as closed.
  ConfigurationManager.getConfigInstance().setProperty("hystrix.command.default.circuitBreaker.forceOpen", false);
   
  String successMessage = new HelloWorldRestCommand().execute();
  assertThat(successMessage, is("Hello World"));
   
  //We use Archaius to open the circuit
  ConfigurationManager.getConfigInstance().setProperty("hystrix.command.default.circuitBreaker.forceOpen", true);
   
  String failMessage = new HelloWorldRestCommand().execute();
  assertThat(failMessage, is("Good Bye"));
   
  //Prints Request => HelloWorldRestCommand[SUCCESS][19ms], HelloWorldRestCommand[SHORT_CIRCUITED, FALLBACK_SUCCESS][0ms]
  System.out.println("Request => " + HystrixRequestLog.getCurrentRequest().getExecutedCommandsAsString());
   
  assertThat(creditCardMetrics.getHealthCounts().getTotalRequests(), is(2));
  assertThat(creditCardMetrics.getHealthCounts().getErrorCount(), is(1));
 
}

Это очень простой пример, потому что метод execute и резервный метод довольно прост, но если вы считаете, что метод execute может содержать сложную логику, а резервный метод также может быть таким же сложным (например, при получении данных с другого сервера создайте некоторый тип заглушки). данные,…), затем написание интеграционных или функциональных тестов, которые проверяют весь этот поток, который он начинает иметь смысл. Имейте в виду, что иногда ваша резервная логика может зависеть от предыдущих вызовов от текущего пользователя или других пользователей.

Hystrix также предлагает другие функции, такие как кеширование результатов, поэтому любая команда, уже выполненная в том же HystrixRequestContext, может возвращать результат кэширования ( https://github.com/Netflix/Hystrix/wiki/How-To-Use#Caching ). Другая особенность, которую он предлагает, — это разрушение. Это позволяет автоматизировать пакетирование запросов в одном экземпляре HystrixCommand . Он может использовать размер и время пакета в качестве триггеров для выполнения пакета.

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

Мы продолжаем учиться,

Алекс.


Спой нам песню, ты пианист, Спой нам песню сегодня вечером, Ну, мы все в настроении для мелодии, И у тебя с нами все в порядке (Piano Man — Billy Joel)

Музыка: https://www.youtube.com/watch?v=gxEPV4kolz0

Ссылка: Защитите свою заявку с помощью Hystrix от нашего партнера по JCG Алекса Сото в блоге One Jar To Rule All .