В предыдущем сообщении в блоге я рассказывал о мотивации такой библиотеки, как Netflix Hystrix . Здесь я расскажу о некоторых из самых простых способов начать использовать Hystrix и расскажу о более сложных случаях использования.
Привет, мир
Простой пример Hello World для «Команды Hystrix» следующий:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
|
import com.netflix.hystrix.HystrixCommand; import com.netflix.hystrix.HystrixCommandGroupKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class HelloWorldCommand extends HystrixCommand<String> { private static final Logger logger = LoggerFactory.getLogger(HelloWorldCommand. class ); private final String name; public HelloWorldCommand(String name) { super (HystrixCommandGroupKey.Factory.asKey( "default" )); this .name = name; } @Override protected String run() throws Exception { logger.info( "HelloWorld Command Invoked" ); return "Hello " + name; } } |
Метод run содержит любое зависимое действие, от которого мы хотим быть защищенным, которое в конечном итоге возвращает параметризованный тип — String в данном конкретном случае. Если вы поклонник библиотеки Netflix Rx-java , то другой способ создания команды Hystrix заключается в следующем:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
import com.netflix.hystrix.HystrixCommandGroupKey; import com.netflix.hystrix.HystrixObservableCommand; import rx.Observable; public class HelloWorldObservableCommand extends HystrixObservableCommand<String> { private String name; public HelloWorldObservableCommand(String name) { super (HystrixCommandGroupKey.Factory.asKey( "default" )); this .name = name; } @Override protected Observable<String> resumeWithFallback() { return Observable.just( "Returning a Fallback" ); } @Override protected Observable<String> construct() { return Observable.just( "Hello " + this .name); } } |
Здесь метод «конструкция» возвращает наблюдаемую Rx-Java .
Использование команды Hystrix
Теперь, когда у нас есть команда Hystrix, чтобы обернуть наш вызов, ее можно использовать разными способами, давайте начнем с самого простого синхронного вызова —
1
2
|
HelloWorldCommand helloWorldCommand = new HelloWorldCommand( "World" ); assertEquals( "Hello World" , helloWorldCommand.execute()); |
Или это можно сделать, чтобы вернуть будущее:
1
2
3
|
HelloWorldCommand helloWorldCommand = new HelloWorldCommand( "World" ); Future future = helloWorldCommand.queue(); assertEquals( "Hello World" , future.get()); |
Или, что еще лучше, можно сделать так, чтобы возвращалась наблюдаемая Rx-Java:
01
02
03
04
05
06
07
08
09
10
11
|
HelloWorldCommand helloWorldCommand = new HelloWorldCommand( "World" ); CountDownLatch l = new CountDownLatch( 1 ); Observable<String> obs = helloWorldCommand.observe(); obs.subscribe( s -> logger.info( "Received : " + s), t -> logger.error(t.getMessage(), t), () -> l.countDown() ); l.await( 5 , TimeUnit.SECONDS); |
Наблюдаемый вариант команды также работает в том же духе, однако мы должны противопоставить небольшую разницу в поведении:
1
2
3
|
HelloWorldObservableCommand helloWorldCommand = new HelloWorldObservableCommand( "World" ); logger.info( "Completed executing HelloWorld Command" ); Observable<String> obs = helloWorldCommand.observe(); |
Есть два способа получить Observable здесь: один такой же, как и выше, с помощью вызова «.observe ()», другой — следующим образом:
1
2
|
HelloWorldObservableCommand helloWorldCommand = new HelloWorldObservableCommand( "World" ); Observable<String> obs = helloWorldCommand.toObservable(); |
другое — использование вызова .toObservable ():
1
2
|
HelloWorldObservableCommand helloWorldCommand = new HelloWorldObservableCommand( "World" ); Observable<String> obs = helloWorldCommand.toObservable(); |
Разница заключается в том, что метод .observe () возвращает Hot Observable, который немедленно начинает выполнение метода «construct», тогда как вариант «.toObservable» возвращает Cold Observable и не будет вызывать метод «construct», если он не подписан на , скажем так:
1
2
3
|
CountDownLatch l = new CountDownLatch( 1 ); obs.subscribe(System.out::println, t -> l.countDown(), () -> l.countDown()); l.await(); |
У меня есть больше информации здесь.
Обратите внимание, что команда Hystrix не является синглтоном, типичный способ использования команды Hystrix — создать ее там, где она требуется, и утилизировать ее после выполнения.
Резервный и командный ключ группы
В конструкторе HelloWorldCommand я вызвал метод конструктора суперкласса со следующей сигнатурой:
1
2
3
4
|
public HelloWorldCommand(String name) { super (HystrixCommandGroupKey.Factory.asKey( "default" )); this .name = name; } |
Этот параметр задает ключ Hystrix «Command group», а также Command Key, который по умолчанию является простым именем класса, он контролирует множество наворотов поведения Hystrix, примером свойств является следующий, и я буду вернемся к специфике этих позже:
01
02
03
04
05
06
07
08
09
10
|
hystrix.command.HelloWorldCommand.metrics.rollingStats.timeInMilliseconds= 10000 hystrix.command.HelloWorldCommand.execution.isolation.strategy=THREAD hystrix.command.HelloWorldCommand.execution.isolation.thread.timeoutInMilliseconds= 1000 hystrix.command.HelloWorldCommand.execution.isolation.semaphore.maxConcurrentRequests= 10 hystrix.command.HelloWorldCommand.circuitBreaker.errorThresholdPercentage= 50 hystrix.command.HelloWorldCommand.circuitBreaker.requestVolumeThreshold= 20 hystrix.command.HelloWorldCommand.circuitBreaker.sleepWindowInMilliseconds= 5000 hystrix.threadpool. default .coreSize= 10 hystrix.threadpool. default .queueSizeRejectionThreshold= 5 |
Другим поведением, которое мы можем захотеть контролировать, является ответ в случае сбоя вызова зависимого сервиса, резервный метод обеспечивает такое поведение, поэтому рассмотрим случай, когда зависимый сервис всегда терпит неудачу:
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
|
import com.netflix.hystrix.HystrixCommand; import com.netflix.hystrix.HystrixCommandGroupKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class FallbackCommand extends HystrixCommand<String> { private static final String COMMAND_GROUP= "default" ; private static final Logger logger = LoggerFactory.getLogger(FallbackCommand. class ); public FallbackCommand() { super (HystrixCommandGroupKey.Factory.asKey(COMMAND_GROUP)); } @Override protected String run() throws Exception { throw new RuntimeException( "Always fail" ); } @Override protected String getFallback() { logger.info( "About to fallback" ); return "Falling back" ; } } |
Здесь зависимый сервисный вызов всегда терпит неудачу, и ответ, как показано в следующем тесте, всегда будет ответом от резервного метода:
1
2
|
FallbackCommand fallbackCommand = new FallbackCommand(); assertEquals( "Falling back" , fallbackCommand.execute()); |
Мониторинг
Прежде чем закончить основы, полезно продемонстрировать потрясающую функцию, которую Hystrix объединяет в терминах потока Hystrix и панели инструментов Hystrix. Давайте начнем с потоков Hystrix, если он обычно включен в качестве сервлета в веб-приложениях на основе Java, он предоставляет поток SSE статистики в реальном времени о поведении команд Hystrix, присутствующих в веб-приложении.
Поскольку моя демонстрация основана на приложении Karyon2 Rx-Netty , мою конфигурацию можно увидеть здесь . Однако информация из потока Hystrix является слишком сырой, и именно здесь встраивается удивительная панель инструментов Hystrix — она потребляет поток Hystrix и показывает агрегированную информацию в реальном времени о том, как выполняет каждая команда Hystrix и различные базовые пулы потоков. У меня есть пример проекта панели инструментов Hystrix, основанный на потрясающем проекте Spring-Cloud . Пример панели инструментов здесь:
Вывод
Это охватывает основы Hystrix, еще многое предстоит сделать, я завершу это в следующем посте с подробным описанием некоторых расширенных функций Hystrix.
Ссылка: | Нежное знакомство с Hystrix — Hello World от нашего партнера по JCG Биджу Кунджуммена в блоге all and sundry. |