Microservice-Entwicklung mit Java EE – eine Einführung in Eclipse MicroProfile

Seite 3: Integration mit MicroProfile Config und Metrics

Inhaltsverzeichnis

Wenn die fehlertolerante Verwendung einer Methode oder CDI Bean benötigt wird, muss diese meist auch mit Metriken überwacht und für verschiedene Ausführungsumgebungen konfiguriert werden. Daher integriert die MicroProfile-Fault-Tolerance-Spezifikation die bereits beschriebenen Config- und Metrics-Spezifikationen.

Dadurch lassen sich alle Parameter der zuvor gezeigten InterceptorBindings durch externe Konfigurationen überschreiben, die dem folgenden Namensschema entsprechen: <classname>/<methodname>/<annotation>/<parameter>. Im vorherigen Beispiel ließe sich also der maxRetries-Parameter der @Retry-Annotation mit dem Konfigurationsparameter org.example.MyBean/buildName/Retry/maxRetries an die Anforderungen der jeweiligen Ausführungsumgebung anpassen.

Des Weiteren werden automatisch Metriken für jede mit @Retry, @Timeout, @CircuitBreaker, @Bulkhead oder @Fallback annotierte Methode und CDI Bean erfasst. Damit lassen sich beispielsweise die Anzahl der erfolgreichen und fehlgeschlagenen Aufrufe, die Anzahl der ausgeführten Retries, die Menge der eingetretenen Timeouts und die Anzahl der vom Bulkhead abgelehnten Aufrufe messen. Somit stehen automatisch viele Informationen zur Verfügung, die Aufschluss über den aktuellen Zustand des Microservice-Systems liefern.

Eine weitere Herausforderung in verteilten Systemen ist die Authentifizierung zwischen verschiedenen Services. Das gilt vor allem in Microservice-Architekturen, in denen zustandslose Services über eine REST-API aufgerufen werden. Wenn sich in so einem System Services gegenseitig aufrufen, müssen diese den aktuellen Authentifizierungszustand vom aufrufenden an den aufgerufenen Service weitergeben.

Die MicroProfile-JWT-Authentication-Spezifikation verwendet dazu das OpenID-Connect-Protokoll auf Basis von JSON Web Tokens (JWT). Die JWT-Authentication-Implementierung extrahiert und verifiziert die Tokens automatisch. Der anschließend auf Basis des Tokens erzeugt Java EE Security Context lässt sich innerhalb von JAX-RS-Ressourcen verwenden. Des Weiteren kann mittels CDI Injection auf den Token und die damit verbundenen Claims zugegriffen werden.

Mehr Informationen über die genauen Bestandteile des Tokens und die verschiedenen Zugriffsmöglichkeiten hat Lars Röwekamp in seinem Artikel MicroProfile unter der Lupe, Teil 3: JWT Security beschrieben.

Die Open-API-Spezifikation definiert einen Standard zur Dokumentation von REST-APIs. Die MicroProfile-OpenAPI-Spezifikation setzt auf diesen Standard auf und spezifiziert eine Reihe von Annotationen, mit denen sich Open-API-Dokumente erstellen lassen. Dazu wurde ein Großteil der Annotation der häufig eingesetzten Swagger-Core-Bibliothek übernommen. Viele Entwickler sind daher bereits mit den Annotationen und Konzepten vertraut – was die Verwendung von MicroProfile OpenAPI erleichtert.

Der folgende Codeausschnitt zeigt ein Beispiel, in dem ein REST Endpoint mit Hilfe verschiedener OpenAPI-Annotationen dokumentiert wird. Die @Operation-Annotation definiert eine Beschreibung der GET-Methode und die beiden @APIResponse-Annotationen beschreiben die zu erwartenden Antworten für den Fall, dass eine Order mit der angegebenen ID gefunden wurde, und für den Fall, in dem keine Order mit einer übereinstimmenden ID vorhanden ist.

@GET
@Path("/{orderId}")
@Operation(summary = "Get an order by its ID")
@APIResponse(description = "The order",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = Order.class))),
@APIResponse(responseCode = "404", description = "Order not found")
public Response getOrderById(
@Parameter(description = "The ID of the order that needs to be loaded", required = true) @PathParam("orderId") String orderId)
{...}

Auf diese Weise lassen sich alle REST-Endpoints im Quellcode beschreiben. Darauf aufbauend wird dann eine Dokumentation generiert, die den Anforderungen der Open API Spezifikation entspricht.

/order/{orderId}:
get:
summary: Get an order by its ID
operationId: getOrderById
parameters:
- name: orderId
in: path
description: 'The ID of the order that needs to be loaded '
required: true
schema:
type: string
responses:
default:
description: The order
content:
application/json:
schema:
$ref: '#/components/schemas/Order'
404:
description: Order not found

Die bisher besprochenen MicroProfile-Spezifikationen haben gezeigt, dass das Java-EE-Programmiermodell gezielt um Funktionen erweitert wird, die die Implementierung und den Betrieb verteilter Anwendungen erleichtern. Das gilt auch für die OpenTracing-Spezifikation.

Durch die Aufteilung des Systems auf mehrere, unabhängige Services wird es im Fehlerfall deutlich schwieriger den Verlauf eines einzelnen Aufrufs und die dabei aufgetretene Situation nachzuvollziehen. Um dieses Problem zu lösen, stehen inzwischen verschiedene Tracing-Anwendungen zur Verfügung, die das OpenTracing-Protokoll implementieren. Sie ermöglichen es, den Verlauf eines Aufrufs innerhalb eines Systems zu überwachen. Dazu ist es erforderlich, dass alle Services Informationen über jeden Aufruf auf Basis des OpenTracing-Protokolls bereitstellen. Innerhalb der Tracing-Anwendung lassen sich diese Informationen sammeln und für die Auswertung bereitstellen.

Die MicroProfile-OpenTracing-Spezifikation erleichtert die Integration eines Microservice in eine auf Basis des OpenTracing-Protokolls überwachte Umgebung. Dazu definiert die Spezifikation einen Modus, der keinerlei Anpassung des Quellcodes erfordert und automatisch die benötigten Tracing-Informationen über einen Serviceaufruf bereitstellt. Zusätzlich lässt sich eine Klasse oder Methode mit der @Traced-Annotation annotieren, um die beim Tracing verwendeten Informationen anzupassen.