Ratpack com Hystrix

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>

===* 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.