Статьи

Нежное Введение в Hystrix

В последние несколько дней я изучал библиотеку Netflix Hystrix и оценил возможности этой превосходной библиотеки.

Цитировать с сайта Hystrix:

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

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

Непредсказуемый сервис

Рассмотрим нечетную службу, которая принимает сообщение json следующей структуры и возвращает подтверждение:

1
2
3
4
5
6
{
    "id":"1",
    "payload": "Sample Payload",
    "throw_exception":false,
    "delay_by": 0
}

Служба принимает полезную нагрузку, но дополнительно принимает два поля — delay_by, которое заставляет службу подтверждать ответ после задержки в миллисекундах, и поле «throw_exceptions», которое приведет к исключению после указанной задержки!

Вот пример ответа:

1
2
3
4
5
{
 "id":"1",
 "received":"Sample Payload",
 "payload":"Reply Message"
}

Если вы продолжаете , вот мой репозиторий github с этим примером , я использовал Netflix Karyon 2 для этого примера, и код, который обрабатывает запрос, можно выразить очень кратко следующим образом — посмотрите, как размещается библиотека rx-java для хорошего использования здесь:

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.governator.annotations.Configuration;
import rx.Observable;
import service1.domain.Message;
import service1.domain.MessageAcknowledgement;
 
import java.util.concurrent.TimeUnit;
 
public class MessageHandlerServiceImpl implements MessageHandlerService {
 
    @Configuration("reply.message")
    private String replyMessage;
 
    public Observable<MessageAcknowledgement> handleMessage(Message message) {
        logger.info("About to Acknowledge");
        return Observable.timer(message.getDelayBy(), TimeUnit.MILLISECONDS)
                .map(l -> message.isThrowException())
                .map(throwException -> {
                    if (throwException) {
                        throw new RuntimeException("Throwing an exception!");
                    }
                    return new MessageAcknowledgement(message.getId(), message.getPayload(), replyMessage);
                });
    }
 
 
}

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

Клиент Сервиса

Теперь на клиенте к этой услуге. Я использую Netflix Feign, чтобы сделать этот вызов, еще одна потрясающая библиотека, все, что для этого требуется, — это Java-интерфейс, аннотированный следующим образом:

01
02
03
04
05
06
07
08
09
10
package aggregate.service;
 
import aggregate.domain.Message;
import aggregate.domain.MessageAcknowledgement;
import feign.RequestLine;
 
public interface RemoteCallService {
    @RequestLine("POST /message")
    MessageAcknowledgement handleMessage(Message message);
}

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

1
2
3
4
RemoteCallService remoteCallService = Feign.builder()
        .encoder(new JacksonEncoder())
        .decoder(new JacksonDecoder())
        .target(RemoteCallService.class, "http://127.0.0.1:8889");

У меня есть несколько конечных точек, которые делегируют вызовы этому удаленному клиенту, все они представляют шаблон URL по этим линиям — http: // localhost: 8888 / noHystrix? Message = Hello & delay_by = 0 & throw_exception = false , этот первый пример является примером, где конечная точка не использует Hystrix.

Нет Hystrix Case

В качестве первого примера рассмотрим вызовы удаленной службы без Hystrix, если бы я попытался позвонить по адресу http: // localhost: 8888 / noHystrix? Message = Hello & delay_by = 5000 & throw_exception = false или сказать http: // localhost: 8888 / noHystrix? message = Hello & delay_by = 5000 & throw_exception = true , в обоих случаях пользовательский запрос к конечным точкам будет просто зависать на 5 секунд, прежде чем ответить.

Здесь должно быть сразу несколько вещей:

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

Очевидно, что существует необходимость в чем-то вроде Hystrix, который решает все эти проблемы.

Обертывание команды Hystrix Удаленные вызовы

Я провел небольшой тест нагрузки, используя нагрузку 50 пользователей в предыдущем случае, и получил следующий результат:

01
02
03
04
05
06
07
08
09
10
================================================================================
---- Global Information --------------------------------------------------------
> request count                                         50 (OK=50     KO=0     )
> min response time                                   5007 (OK=5007   KO=-     )
> max response time                                  34088 (OK=34088  KO=-     )
> mean response time                                 17797 (OK=17797  KO=-     )
> std deviation                                       8760 (OK=8760   KO=-     )
> response time 50th percentile                      19532 (OK=19532  KO=-     )
> response time 75th percentile                      24386 (OK=24386  KO=-     )
> mean requests/sec                                  1.425 (OK=1.425  KO=-     )

По существу, 5-секундная задержка от службы приводит к времени 75-го процентиля в 25 секунд! Теперь рассмотрим тот же тест с командой Hystrix, заключающей в себе служебные вызовы:

01
02
03
04
05
06
07
08
09
10
================================================================================
---- Global Information --------------------------------------------------------
> request count                                         50 (OK=50     KO=0     )
> min response time                                      1 (OK=1      KO=-     )
> max response time                                   1014 (OK=1014   KO=-     )
> mean response time                                    22 (OK=22     KO=-     )
> std deviation                                        141 (OK=141    KO=-     )
> response time 50th percentile                          2 (OK=2      KO=-     )
> response time 75th percentile                          2 (OK=2      KO=-     )
> mean requests/sec                                 48.123 (OK=48.123 KO=-     )

Как ни странно, время 75-го процентиля сейчас составляет 2 миллисекунды !, как это возможно, и ответ становится очевидным при использовании превосходных инструментов, которые предоставляет Hystrix, вот представление панели инструментов Hystrix для этого теста:

Hystrix-RemoteFailed

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

Вывод

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

Ссылка: Нежное знакомство с Hystrix от нашего партнера по JCG Биджу Кунджуммена в блоге all and sundry.