Ratpack com Hystrix
*1. Introdução *
Anteriormente, temos o link:/ratpack [mostrado] como criar um aplicativo reativo e de alto desempenho com o Ratpack.
Neste artigo, veremos como integrar o Netflix Hystrix a um aplicativo Ratpack.
O Netflix Hystrix ajuda a controlar interações entre serviços distribuídos, isolando pontos de acesso para interromper falhas em cascata e fornecendo opções de fallback para tolerância a falhas. Isso pode nos ajudar a criar um aplicativo mais resiliente. Veja nosso link:/introdução ao hystrix [introdução ao Hystrix] para uma rápida revisão.
E é assim que vamos usá-lo - aprimoraremos nosso aplicativo Ratpack com esses recursos úteis da Hystrix.
===* 2. Dependência Maven *
Para usar o Hystrix com o Ratpack, precisamos da dependência de ratpack-hystrix no projeto pom.xml:
<dependency>
<groupId>io.ratpack</groupId>
<artifactId>ratpack-hystrix</artifactId>
<version>1.4.6</version>
</dependency>
A versão mais recente do ratpack-hystrix pode ser encontrada em https://search.maven.org/classic/#search%7Cga%7C1%7Ca%3A%22ratpack-hystrix%22%20AND%20g%3A%22io.ratpack%22 [aqui]. O ratpack-hystrix inclui ratpack-core e hystrix-core.
Para usar os recursos reativos do Ratpack, também precisaríamos do ratpack-rx:
<dependency>
<groupId>io.ratpack</groupId>
<artifactId>ratpack-rx</artifactId>
<version>1.4.6</version>
</dependency>
A versão mais recente do ratpack-rx pode ser encontrada https://search.maven.org/classic/#search%7Cga%7C1%7Cg%3A%22io.ratpack%22%20AND%20a%3A%22ratpack-rx%22 [aqui].
===* 3. Servindo com o comando Hystrix *
Ao usar o Hystrix, os serviços subjacentes geralmente são agrupados em HystrixCommand ou HystrixObservableCommand. O Hystrix suporta a execução desses comandos de maneira síncrona, assíncrona e reativa. Entre estes, apenas reativo é não bloqueador e oficialmente recomendado.
*Nos exemplos a seguir, criaremos alguns pontos de extremidade que buscam um perfil na https://developer.github.com/v3/[Github REST API].*
3.1 Execução de comando reativo
Primeiro, vamos criar um serviço de back-end reativo com o Hystrix:
public class HystrixReactiveHttpCommand extends HystrixObservableCommand<String> {
//...
@Override
protected Observable<String> construct() {
return RxRatpack.observe(httpClient
.get(uri, r -> r.headers(h -> h.add("User-Agent", "Baeldung HttpClient")))
.map(res -> res.getBody().getText()));
}
@Override
protected Observable<String> resumeWithFallback() {
return Observable.just("eugenp's reactive fallback profile");
}
}
Aqui, um HttpClient é usado para fazer uma solicitação GET. O HystrixReactiveHttpCommand pode funcionar como um manipulador reativo:
chain.get("rx", ctx ->
new HystrixReactiveHttpCommand(
ctx.get(HttpClient.class), eugenGithubProfileUri, timeout)
.toObservable()
.subscribe(ctx::render));
O terminal pode ser verificado com o seguinte teste:
@Test
public void whenFetchReactive_thenGotEugenProfile() {
assertThat(appUnderTest.getHttpClient().getText("rx"),
containsString("www..com"));
}
3.2 Execução de comando assíncrono
Uma execução assíncrona de HystrixCommand enfileira o comando no conjunto de encadeamentos e retorna um Future:
chain.get("async", ctx -> ctx.render(
new HystrixAsyncHttpCommand(eugenGithubProfileUri, timeout)
.queue()
.get()));
O HystrixAsyncHttpCommand se parece com:
public class HystrixAsyncHttpCommand extends HystrixCommand<String> {
//...
@Override
protected String run() throws Exception {
return EntityUtils.toString(HttpClientBuilder.create()
.setDefaultRequestConfig(requestConfig)
.setDefaultHeaders(Collections.singleton(
new BasicHeader("User-Agent", "Baeldung Blocking HttpClient")))
.build().execute(new HttpGet(uri)).getEntity());
}
@Override
protected String getFallback() {
return "eugenp's async fallback profile";
}
}
Aqui, usamos um HttpClient em vez de um não bloqueador, porque queremos que o Hystrix controle o tempo limite de execução do comando real, para que não precisemos lidar com isso sozinhos ao obter uma resposta do Future. Isso também permite que o Hystrix faça backup ou armazene em cache nossa solicitação.
A execução assíncrona também produz o resultado esperado:
@Test
public void whenFetchAsync_thenGotEugenProfile() {
assertThat(appUnderTest.getHttpClient().getText("async"),
containsString("www..com"));
}
3.3 Execução de comando síncrona
Uma execução síncrona executa o comando diretamente no encadeamento atual:
chain.get("sync", ctx -> ctx.render(
new HystrixSyncHttpCommand(eugenGithubProfileUri, timeout).execute()));
A implementação de HystrixSyncHttpCommand é quase idêntica a HystrixAsyncHttpCommand, exceto que fornecemos um resultado de retorno diferente. Quando não recua, ele se comporta da mesma forma que a execução reativa e assíncrona:
@Test
public void whenFetchSync_thenGotEugenProfile() {
assertThat(appUnderTest.getHttpClient().getText("sync"),
containsString("www..com"));
}
4. Métricas
Registrando o link:/ratpack-google-guice [módulo Guice] - HystrixModule no registro Ratpack, podemos transmitir o escopo da solicitação métricas e exponha os fluxos de eventos por meio de um terminal GET:
serverSpec.registry(
Guice.registry(spec -> spec.module(new HystrixModule().sse())))
.handlers(c -> c.get("hystrix", new HystrixMetricsEventStreamHandler()));
O https://ratpack.io/manual/current/api/ratpack/hystrix/HystrixMetricsEventStreamHandler.html [HystrixMetricsEventStreamHandler] _ ajuda a transmitir as métricas do Hystrix no formato _text/event-stream, para que possamos monitorar as métricas em Hystrix Dashboard.
Podemos configurar um standalone dashboard Hystrix e adicionar nosso fluxo de eventos Hystrix à lista de monitores para ver como o aplicativo Ratpack executa:
link:/wp-content/uploads/2017/08/Snip20170815_1.png [imagem:/wp-content/uploads/2017/08/Snip20170815_1-300x143.png [imagem, largura = 692, altura = 330]]
Após várias solicitações ao aplicativo Ratpack, podemos ver os comandos relacionados ao Hystrix no painel.
4.1 Sob o capô
No HystrixModule, um Hystrix Concurrency Strategy está registrado no Hystrix em https://github.com/Netflix/Hystrix/wiki/Plugins [HystrixPlugin] para gerenciar o contexto da solicitação com o registro Ratpack. Isso remove a necessidade de inicializar o contexto de solicitação do Hystrix antes de cada solicitação começar.
public class HystrixModule extends ConfigurableModule<HystrixModule.Config> {
//...
@Override
protected void configure() {
try {
HystrixPlugins.getInstance().registerConcurrencyStrategy(
new HystrixRegistryBackedConcurrencyStrategy());
} catch (IllegalStateException e) {
//...
}
}
//...
}
5. Conclusão
Neste rápido artigo, mostramos como o Hystrix pode ser integrado ao Ratpack e como enviar as métricas de nosso aplicativo Ratpack para o Hystrix Dashboard para uma melhor visualização do desempenho do aplicativo.
Como sempre, a implementação completa pode ser encontrada em o projeto Github.