Ratpack mit Hystrix

Ratpack mit Hystrix

1. Einführung

Bisher haben wirshown, wie man mit Ratpack eine leistungsstarke und reaktive Anwendung erstellt.

In diesem Artikel erfahren Sie, wie Sie Netflix Hystrix in eine Ratpack-Anwendung integrieren.

Netflix Hystrix hilft bei der Steuerung der Interaktionen zwischen verteilten Diensten, indem Zugriffspunkte isoliert werden, um kaskadierende Fehler zu stoppen und Ausweichoptionen für die Fehlertoleranz bereitzustellen. Dies kann uns helfen, eine stabilere Anwendung zu erstellen. Eine kurze Übersicht finden Sie in unserenintroduction to Hystrix.

So werden wir es verwenden - wir werden unsere Ratpack-Anwendung mit diesen nützlichen Funktionen von Hystrix erweitern.

2. Maven-Abhängigkeit

Um Hystrix mit Ratpack verwenden zu können, benötigen wir die Ratpack-Hystrix-Abhängigkeit im Projektpom.xml:


    io.ratpack
    ratpack-hystrix
    1.4.6

Die neueste Version von Ratpack-Hystrix isthere. Die Rattenpack-Hystrix enthältratpack-core undhystrix-core.

Um die reaktiven Funktionen von Ratpack nutzen zu können, benötigen wir außerdem ratpack-rx:


    io.ratpack
    ratpack-rx
    1.4.6

Die neueste Version von ratpack-rx isthere.

3. Servieren mit Hystrix Command

Bei Verwendung von Hystrix werden zugrunde liegende Services normalerweise inHystrixCommand oderHystrixObservableCommand eingeschlossen. Hystrix unterstützt die Ausführung dieser Befehle auf synchrone, asynchrone und reaktive Weise. Unter diesen ist nur reaktiv nicht blockierend und wird offiziell empfohlen.

In den folgenden Beispielen werden einige Endpunkte erstellt, die ein Profil vonGithub REST API abrufen.

3.1. Reaktive Befehlsausführung

Lassen Sie uns zunächst einen reaktiven Backend-Service mit Hystrix erstellen:

public class HystrixReactiveHttpCommand extends HystrixObservableCommand {

    //...

    @Override
    protected Observable construct() {
        return RxRatpack.observe(httpClient
          .get(uri, r -> r.headers(h -> h.add("User-Agent", "example HttpClient")))
          .map(res -> res.getBody().getText()));
    }

    @Override
    protected Observable resumeWithFallback() {
        return Observable.just("eugenp's reactive fallback profile");
    }
}

Hier wird ein Ratpack-reaktivesHttpClient verwendet, um eine GET-Anfrage zu stellen. DieHystrixReactiveHttpCommand können als reaktiver Handler ausgeführt werden:

chain.get("rx", ctx ->
  new HystrixReactiveHttpCommand(
    ctx.get(HttpClient.class), eugenGithubProfileUri, timeout)
    .toObservable()
    .subscribe(ctx::render));

Der Endpunkt kann mit dem folgenden Test überprüft werden:

@Test
public void whenFetchReactive_thenGotEugenProfile() {
    assertThat(appUnderTest.getHttpClient().getText("rx"),
      containsString("www.example.com"));
}

3.2. Asynchrone Befehlsausführung

Eine asynchrone Ausführung vonHystrixCommand stellt den Befehl im Thread-Pool in die Warteschlange und gibtFuture zurück:

chain.get("async", ctx -> ctx.render(
  new HystrixAsyncHttpCommand(eugenGithubProfileUri, timeout)
    .queue()
    .get()));

DasHystrixAsyncHttpCommand sieht aus wie:

public class HystrixAsyncHttpCommand extends HystrixCommand {

    //...

    @Override
    protected String run() throws Exception {
        return EntityUtils.toString(HttpClientBuilder.create()
          .setDefaultRequestConfig(requestConfig)
          .setDefaultHeaders(Collections.singleton(
            new BasicHeader("User-Agent", "example Blocking HttpClient")))
          .build().execute(new HttpGet(uri)).getEntity());
    }

    @Override
    protected String getFallback() {
        return "eugenp's async fallback profile";
    }

}

Hier verwenden wir ein blockierendesHttpClient anstelle eines nicht blockierenden, da Hystrix das Ausführungszeitlimit des eigentlichen Befehls steuern soll, damit wir es nicht alleine behandeln müssen, wenn wir eine Antwort vonFutureerhalten ) s. Dies ermöglicht es Hystrix auch, unsere Anfrage zurückzusetzen oder zwischenzuspeichern.

Die asynchrone Ausführung liefert auch das erwartete Ergebnis:

@Test
public void whenFetchAsync_thenGotEugenProfile() {
    assertThat(appUnderTest.getHttpClient().getText("async"),
      containsString("www.example.com"));
}

3.3. Synchrone Befehlsausführung

Eine synchrone Ausführung führt den Befehl direkt im aktuellen Thread aus:

chain.get("sync", ctx -> ctx.render(
  new HystrixSyncHttpCommand(eugenGithubProfileUri, timeout).execute()));

Die Implementierung vonHystrixSyncHttpCommand ist fast identisch mitHystrixAsyncHttpCommand, außer dass wir ein anderes Fallback-Ergebnis erzielen. Wenn es nicht zurückfällt, verhält es sich genauso wie eine reaktive und asynchrone Ausführung:

@Test
public void whenFetchSync_thenGotEugenProfile() {
    assertThat(appUnderTest.getHttpClient().getText("sync"),
      containsString("www.example.com"));
}

4. Metriken

Durch Registrieren vonGuice module -HystrixModule in der Ratpack-Registrierung können wir die Metriken mit Anforderungsbereich streamen und die Ereignisströme über einen Endpunkt vonGETverfügbar machen:

serverSpec.registry(
  Guice.registry(spec -> spec.module(new HystrixModule().sse())))
  .handlers(c -> c.get("hystrix", new HystrixMetricsEventStreamHandler()));

MitHystrixMetricsEventStreamHandler können Hystrix-Metriken imtext/event-stream-Format gestreamt werden, sodass wir die Metriken inHystrix Dashboard überwachen können.

Wir können einstandalone Hystrix dashboard einrichten und unseren Hystrix-Ereignisstrom zur Monitorliste hinzufügen, um zu sehen, wie unsere Ratpack-Anwendung funktioniert:

image

Nach mehreren Anfragen an unsere Ratpack-Anwendung sehen wir die Hystrix-bezogenen Befehle im Dashboard.

4.1. Unter der Haube

InHystrixModule wird einHystrix Concurrency Strategy überHystrixPlugin bei Hystrix registriert, um den Anforderungskontext mit der Ratpack-Registrierung zu verwalten. Dadurch entfällt die Notwendigkeit, den Hystrix-Anforderungskontext vor Beginn jeder Anforderung zu initialisieren.

public class HystrixModule extends ConfigurableModule {

    //...

    @Override
    protected void configure() {
      try {
        HystrixPlugins.getInstance().registerConcurrencyStrategy(
          new HystrixRegistryBackedConcurrencyStrategy());
      } catch (IllegalStateException e) {
        //...
      }
    }

    //...

}

5. Fazit

In diesem kurzen Artikel haben wir gezeigt, wie Hystrix in Ratpack integriert werden kann und wie Metriken unserer Ratpack-Anwendung in das Hystrix Dashboard übertragen werden, um eine bessere Übersicht über die Anwendungsleistung zu erhalten.

Wie immer kann die vollständige Implementierung überthe Github project gefunden werden.