TLDR: MicroProfile Metriken sind toll und nützlich, hier die Doku.
Wer bereits in den Genuss gekommen ist eine Anwendung mit Quarkus zu entwickeln, weiß wahrscheinlich um die Vorzüge, die sich in Bezug auf das Sammeln von Metriken ergeben. Die MicroProfile-Spezifikation bietet dazu eine Fülle an Möglichkeiten, die ich hier kurz vorstellen möchte.
Natürlich deckt dieser Beitrag längst nicht alle Möglichkeiten und Aspekte bezüglich MicroProfile Metriken ab, und dient eher als Einstieg mit sinnvollen Beispielen. Der Inhalt bezieht sich auf die Verwendung mit Quarkus, sollte jedoch grundlegend auch für andere MicroProfile-Implementierungen gelten. Auf die Speicherung und Darstellung der Metriken mit beispielsweise Prometheus oder Grafana gehe ich hier nicht ein.
Update zu MicroProfile 3.3
Mittlerweile unterstützt Quarkus (seit Version 1.3) auch die MicroProfile 3.3-Spezifikation, die MicroProfile Metrics in Version 2.3 mitbringt.
Die Metriken im MicroProfile sind – wie viele Spezifikationen im MicroProfile – auf einfache Anwendungen ausgelegt. Wir unterteilen Metriken in drei Bereiche (Scopes):"Base"-,"Vendor"- und "Application"-Metriken.
"Basis"-Metriken müssen von jeder MicroProfile-Implementierung erstellt werden. "Vendor"-Metriken sind für zusätzliche Metriken der einzelnen Implementierungen gedacht und "Application"-Metriken schließlich für die Anwendung selbst.
Da ich hier auf die Möglichkeiten im Rahmen eines Quarkus-Projekts eingehen möchte, beschränke ich mich auf die"Application"-Metriken.
Weitere Informationen zur Spezifikation können (beispielsweise für die aktuelle von Quarkus unterstützte Version 2.3) hier eingesehen werden:
Siehe hierzu auch eine kurze Einführung mit Beispielen für die Verwendung in Quarkus:
Wir fangen direkt mit einem minimalen Code-Beispiel an. Dieses stellt eine REST Schnittstelle dar, die eine übergebene Liste aus Ganzzahlen sortiert:
Am einfachsten setzen wir Metriken per Annotation an die gewünschten Klassen, Methoden oder Felder. Schauen wir uns ein paar einfache Metriken an.
Wenn wir wissen wollen, wie häufig unsere Ressource aufgerufen wird, genügt Folgendes:
Diese und alle anderen Metriken können wir bequem per REST-Schnittstelle im OpenMetrics oder Json Format unter “/metrics/application” für alle Application-Metriken abfragen. In unserem Beispiel bekommen wir, sofern wir unsere Schnittstelle einmal aufgerufen haben, folgende Ausgabe im OpenMetrics Format:
Nicht so fancy, oder? Gut, neuer Versuch mit:
Damit erhalten wir Folgendes:
Wir können den Namen der Metrik festlegen(name) und bestimmen, dass dieser als absoluter Name(absolute) statt des vollen Klassen-Namens angezeigt wird.
Mit einem Timer bekommen wir - neben der Anzahl - auch Ausführungszeiten präsentiert:
Ein SimpleTimer ist ein vereinfachter Timer, der nur die Anzahl und die verstrichene Zeit berücksichtigt.
Das ist insbesondere dann sinnvoll, wenn die Metriken mit Prometheus ausgewertet werden sollen. Da Prometheus viele Werte ohnehin selbst bestimmt, müssen diese nicht über die Metrik zur Verfügung gestellt werden. Siehe auch die Spezifikation und diese Diskussion auf GitHub.
Ein Meter dagegen zeigt die Anzahl der Aufrufe in bestimmten Zeitabschnitten an:
Mit einem Gauge ist es möglich, Rückgabewerte von Methoden für die Erzeugung von Metriken zu verwenden. Der Wert selbst kann somit programmatisch und zur Laufzeit angepasst werden. Ein gutes Bespiel dazu findet sich im Quarkus-Tutorial.
Reicht das trotzdem nicht aus, so müssen eventuell Metriken dynamisch angelegt werden.
Mit den vorgestellten Metriken per Annotation kann man bereits viele Szenarien abdecken, die in einem Microservice relevant sind. Jedoch ist es manchmal erforderlich, zusätzlich Metriken dynamisch und programmatisch zu sammeln. Insbesondere Metriken für spezifische Werte der Businesslogik (wie Anzahlen von Objekten) oder Metriken, die erst zur Laufzeit dynamisch registriert werden, können somit flexibel eingesetzt werden.
Metriken werden in einer MetricRegistry gespeichert. Eine MetricRegistry verwaltet alle Metriken samt Metadaten, wie beispielsweise Name und Beschreibung. Die eindeutige Zuordnung geschieht über die Metric-ID, die aus dem Namen und allen Tags der Metrik besteht. Ferner existieren Registries für jeden der drei Bereiche Base, Vendor und Application. Später werden wir auf die Verwendung von Tags näher eingeben.
Die entsprechende Registry lässt sich komfortabel injecten:
Da die Application Registry der Standard ist, kann sie auch ohne @RegistryType injected werden.
Sobald man sich eine Registry eingebunden hat, lassen sich Metriken direkt in dieser registrieren. Für das hier verwendete Beispiel wollen wir neben einem annotierten Counter zusätzliche Counter für die Status Codes 200 und 400 einführen.
Dazu gibt man Folgendes an:
Diese Counter können nun im Code beliebig aufgerufen werden. In unserem Beispiel sieht das so aus:
Falls nur eine Zahl übergeben wird, soll ein HTTP Response Code 400 zurückgegeben werden. Außerdem wird die entsprechende Metrik hochgezählt. Entsprechend wird im Erfolgsfall die Metrik für den Response Code 200 hochgezählt.
Beim Aufruf der Schnittstelle, einmal mit einer Zahl und einmal mit drei Zahlen, ergibt sich folgendes Bild beim Abruf der Metriken:
Das ist nur ein Minimalbeispiel. Mithilfe eines Metadata-Objekts können weitere Daten wie Name und Beschreibung hinzugefügt werden. Siehe dazu auch die Spezifikation.
Wenn man eine neue Metrik ohnehin in die Application Registry aufnehmen möchte, kann man das ebenfalls bequemer per @Metric Annotation erledigen. Das möchte ich in unserem Beispiel anhand einer Metrik zeigen, die nur dynamisch verfügbar ist – dem Histogramm.
Wir registrieren unser Histogramm folgendermaßen:
Im Code verwenden wir es überdies, um die Anzahl der übergebenen Zahlen als Häufigkeiten zu messen:
Ein Aufruf mit viermal zwei Zahlen, dreimal drei Zahlen, zweimal vier Zahlen und einmal fünf Zahlen ergibt folgende Metriken:
Zusätzlich zum Namen der Metrik, lassen sich Tags als Key, Value Paare definieren. Damit können wir die Metrik besser beschreiben und abgrenzen. Tags können wir sowohl bei annotierten als auch bei dynamischen Metriken hinzufügen.
Hier eine annotierte Metrik mit Tags samt Abruf der Metrik-Schnittstelle:
Bei annotierten Metriken ist das Wiederverwenden unter gleichem Namen und Tags (Metrik-ID) standardmäßig nicht möglich, um schwer auffindbare Fehler zu vermeiden. Dennoch kann man dies explizit ermöglichen. Dafür muss man das Feld “reusable” auf true setzen. Bei dynamischen Metriken dagegen ist das Wiederverwenden standardmäßig aktiviert, um eine Metrik an unterschiedlichen Stellen im Code ansprechen zu können.
Zur Verwendung von Namen, Tags, Metadaten und Typen sei noch Folgendes gesagt:
Wenn man eine Metrik wiederverwenden will, dann müssen Name, Tags und Type (z.B. Counter) übereinstimmen. Mehr dazu hier.
Abgesehen von selbst erstellten Metriken gibt es auch noch solche, die erst mit der Nutzung anderer Teile des MicroProfiles gesetzt werden.
Ein Beispiel dafür ist die Fault Tolerance Spezifikation, die beim Einsatz von z.B. @Timeout oder @Retry automatisch entsprechende Metriken registriert. Durch dieses Zusammenspiel der einzelnen Spezifikationen kommt man in den Genuss von automatischen Metriken, ohne selbst welche anlegen zu müssen.
Ein anderesBeispiel kommt von Quarkus selbst. Dort kann man beispielsweise bei Bedarf Metriken aktivieren, wenn der eigene Service mit Datenbanken kommuniziert. Diese bekommt man also “frei Haus” geliefert. Das ist nicht nur praktisch, sondern erspart auch Implementierungsaufwand und sorgt zusätzlich dafür, dass sich nicht jeder Entwickler für die gleiche Metrik eine eigene Lösung ausdenken muss.
Mit der neuen MicroProfile Version 4.0 werden entsprechend auch die Metriken in einer neuen Version 3.0 zur Verfügung stehen.
Die Änderungen fallen dabei umfangreicher aus als zwischen 2.2 und 2.3. Abgesehen vonAnpassungen an Metriken wie SimpleTimer und Timer wurde beispielsweise die Wiederverwendbarkeit von Metriken ausgebaut. Nun geht das auch standardmäßig bei annotierten Metriken (außer Gauge). Weitere Änderungen lassen sich dem Changelog entnehmen.
Insgesamt bietet die Metrik-Spezifikation des MicroProfiles eine tolle und einfache Möglichkeit, den eigenen Microservice mit Metriken aufzuhübschen.
Bisher konnten wir diese schon in einigen Projekten in mehreren Microservices erfolgreich einsetzen.
Natürlich sind die vorgestellten Metriken und Möglichkeiten nur eine Auswahl. Die am Anfang erwähnte Spezifikation der Metriken samt Link gibt einen tieferen Einblick für Interessierte.
Ich hoffe, dieser Beitrag ist dabei für den ein oder anderen hilfreich! Und Respekt an alle, die bis hierhin durchgehalten haben 😉