Micronaut für Cloud-native JVM-Microservices

Seite 3: Features für Cloud-native Microservices

Inhaltsverzeichnis

Micronaut bietet alles, was Anwender heute von einem Cloud-nativen Microservice-Framework erwarten. Dazu gehören Features wie Dependency Injection, Convention over Configuration und Configuration Sharing, Service Discovery, Routing, Circuit Breaker, Tracing und vieles andere mehr. Die dafür notwendigen Bibliotheken sind über die Deklaration der Abhängigkeit als Feature in Micronaut-Anwendungen integrierbar.

Unterliegen Umgebungen einer geringen Anpassungsdynamik, können Entwickler Namensauflösungen zu Diensten statisch konfigurieren. In einer Infrastruktur, die aus vielen Diensten besteht, die man stets verändert und neu ausrollt, ist eine dynamische Auflösung von Dienstnamen zu spezifischen Netzwerkadressen notwendig. Die Service-to-Service Discovery ist mit gängigen Diensten wie Consul, Eureka, Kubernetes, Googles Cloud Plattform oder AWS Route 53 möglich. Consul beispielsweise ist ein verteiltes Service Mesh zum Verbinden, Sichern und Konfigurieren von Diensten. Micronaut bietet eine eigene Consul-Client-Implementierung, denn die Mehrheit der Consul- und Eureka-Clients sind blockierend. Zudem beinhalten sie eine Vielzahl externer Abhängigkeiten, die schließlich die auszuliefernden JAR-Dateien aufblähen. Der Discovery Client von Micronaut verwendet den nativen Micronaut HTTP-Client, der den Bedarf an externen Abhängigkeiten stark reduziert und eine reaktive API für die Nutzung beider Discovery-Server bereitstellt.

Resilienz bezeichnet die Widerstandsfähigkeit technischer Systeme. Sie hat als Ziel, bei einem Teilausfall standhaft zu bleiben und nicht vollständig zu versagen. Fallen etwa abhängige Microservices in einer Kommunikationskette aus, antworten nicht oder unerwartet spät, ist eine adäquate Reaktion notwendig. Verbindungsprobleme wie Zeitüberschreitungen durch hohe Last und die temporäre Nichterreichbarkeit angebundener Systeme sind Aspekte, die Entwickler in Microservices-Infrastrukturen immer berücksichtigen müssen. In der Praxis treten die Probleme häufig auf, weshalb man auf sie vorbereitet sein sollte. Mechanismen, um ihnen zu entgegnen, sind Wiederholungsversuche (Retries), Rückfallmechanismen (Fallbacks) und Schutzschalter (Circuit Breaker).

Hystrix ist eine Bibliothek für den Umgang mit Latenzzeiten und Fehlertoleranz, die Micronaut unterstützt und von Netflix entwickelt wurde, um Zugriffe auf entfernte Systeme, Dienste und Bibliotheken von Drittanbietern zu isolieren und damit kaskadierende Fehler zu vermeiden.

Ein erneuter Verbindungsaufbau zu einem abhängigen Dienst ist in Architekturen von Microservices manchmal sinnvoll. Mit der Annotation @Retryable kann man den Aufruf einer Operation mehrfach versuchen, bevor der Fehlerfall eintritt:

@Retryable(attempts="3", delay="1s")

In einigen Fällen ist die Verwendung des Circuit-Breaker-Musters eine bessere Wahl. Leistungsschalter finden sich im hauseigenen Stromkasten. Das Prinzip in der Softwareentwicklung ist ähnlich. Circuit Breaker lassen eine bestimmte Anzahl fehlerhafter Anforderungen zu, bis sie mit dem Öffnen eines "Schaltkreises" reagieren, der für einen definierten Zeitraum offen bleibt, bevor er zusätzliche Wiederholungsversuche zulässt. Die Annotation des Circuit Breaker ist eine Variation der Annotation @Retryable. Sie gibt an, wie lange der Kreis offen bleiben soll (standardmäßig 20 Sekunden).

Der Einsatz eines Circuit Breakers mit maximal fünf Wiederholungsversuchen im Abstand von fünf Millisekunden und 300 Millisekunden zum Reset ist mit Micronaut per gleichnamiger Annotation möglich:

@CircuitBreaker(attempts="5", delay="5ms", reset="300ms")

Der Fallback-Mechanismus ist eine dritte Möglichkeit, auf Ausnahmesituationen zu reagieren. Ein Fallback ermöglicht es, Standardwerte gemäß fachlicher Anforderungen in Ausnahmesituationen zurückzugeben. Über die Annotation @Fallback können Anwender eine Bean definieren, die automatisch als alternative Implementierung dient, wenn ein angebundener Service nicht antwortet. Eine Klasse oder eine Methode, die einen solchen Fallback nutzen soll, ist mit @Recoverable annotiert. Mehr ist nicht zu tun.

Ein solcher Fallback-Mechanismus kann ebenso zur Entwicklungszeit von Microservices nützlich sein. Bei einem korrekten Microservice-Systemschnitt ist es üblich, dass man für ein Feature an nur einem Microservice arbeitet. Ist das Feature jedoch abhängig von Daten anderer Dienste, ist es sinnvoll, dass sie nicht zur Entwicklungs- und Testzeit erreichbar sein müssen. In einem solchen Fall kann etwa eine Fallback-Implementierung das Verhalten des zur Laufzeit in Produktion angebundenen Services simulieren.