MicroProfile 4 – Metrics

MicroProfile 4 ist releast (Aktuelle Version 4.1 vom Juli 2021). In einer lockeren Folge von Blog Posts möchte ich Änderungen zur Vorversion darstellen.

Die bisherigenen Posts:

Heute schauen wir auf MP Metrics. In https://javaeeblog.wordpress.com/2019/03/15/wie-laeufts-denn-so-monitoring-mit-microprofile-metrics/ finden sich die Grundlagen zu dieser Spezifikation. Gegenüber damals gibt es in der Version 3 u. a. diese Ergänzungen:

Neue Metrik @SimplyTimed

Wird eine Methode mit @Timed annotiert, werden die Aufrufe gezählt und diverse statistische Größen in Bezug auf die Durchlaufzeit der Methode gesammelt – bspw. Extremwerte und diverse Percentile. Sollte diese Vielfalt nicht nötig sein, kann man mit @SimplyTimed arbeiten. Die gesammelten Werte beschränken sich dann auf Aufrufanzahl, Gesamtzeit sowie minimale und maximale Durchlaufzeit:

curl http://localhost:8080/metrics/application -H 'accept: application/json'

{
  "de.gedoplan.showcase.metrics.TimedEndpoint.getHello": {
  "count": 7,
  "elapsedTime": 742,
  "maxTimeDuration": 218,
  "minTimeDuration": 68

Neue Metrik @ConcurrentGauge

Eine weitere Ergänzung erlaubt das Zählen gleichzeitiger Zugriffe auf eine Methode: Beim Eintritt in die Methode erhöht sich der Wert, beim Verlassen wird er wieder vermindert. Die Metrik-Ausgabe umfasst den aktuellen Zählerstand sowie bisheriges Minimum bzw. Maximum:

curl http://localhost:8080/metrics/application -H 'accept: application/json'

{
  "de.gedoplan.showcase.metrics.TimedEndpoint.getHello": {
  "current": 2,
  "min": 0,
  "max": 2

Weitere Informationen

Wir versorgen Sie gerne mit weiterem Input:

Dirk Weil, GEDOPLAN GmbH

MicroProfile 4: Health

MicroProfile 4 ist releast (Aktuelle Version 4.1 vom Juli 2021). In einer lockeren Folge von Blog Posts möchte ich die Änderungen zur Vorversion darstellen.

Der letzte Post befasste sich mit MP Config (https://javaeeblog.wordpress.com/2021/08/10/microprofile-4-config/).

Nun geht es um MP Health. Auch über diese Teilspezifikation habe ich hier bereits berichtet (https://javaeeblog.wordpress.com/2019/11/05/immer-noch-gesund-aenderungen-von-microprofile-health-2-0-vs-1-0/ und https://javaeeblog.wordpress.com/2019/01/27/alles-gesund-health-checking-mit-microprofile-health/). In der nun vorliegenden Version 3 gibt es u. a. die folgenden Änderungen:

Breaking Change in HealthCheckResponse

Die JSON-Form eines HealthCheckResponse enthält das Attribut status:

GET /health/live
200 OK
{
"status": "UP",

In der Klasse hieß die Property dagegen bisher state, was zu Problemen bei der Serialisierung geführt hat. Die Property wurde in status umbenannt, was aber auch bedeutet, dass die entsprechende Methode in HealthCheckResponseBuilder nun auch so heißt.

Property für „leere“ Readiness

Mit der Config Property mp.health.default.readiness.empty.response kann eingestellt werden, welchen Status der Server während des Hochfahrens der Anwendung für den Readiness-Check liefern soll, also in dem Zeitfenster, in dem die Anwendung zwar schon teilweise läuft, aber die darin enthaltenen Checks noch nicht deployt sind. Der Default-Wert ist DOWN.

Startup Checks

Mit der Version 3.1 kommen noch Startup Checks hinzu. Sie dienen der Unterstützung der Startup Probes in Kubernetes. Der Status wird über den Endpoint health/started veröffentlicht. Custom Checks können mit dem Qualifier @Startup programmiert werden (analog zu @Liveness und @Readiness) und es gibt auch hier eine Config Property für die Antwort während der Anwendungsinitialisierung: mp.health.default.startup.empty.response.

Weitere Informationen

Wir versorgen Sie gerne mit weiterem Input:

Dirk Weil, GEDOPLAN GmbH

MicroProfile 4: Config

Seit den vergangenen Posts zum Thema MicroProfile ist einiges passiert. MicroProfile 4 ist releast (Aktuelle Version 4.1 vom Juli 2021). Das umfasst auch MP Config 2.0 mit einigen Änderungen zu damals beschriebenen Version 1.4.

Die Grundlagen aus Welche Einstellung haben Sie? Anwendungskonfiguration mit MicroProfile Config sind natürlich immer noch gültig. Zwei neue Features möchte ich im Folgenden kurz beschreiben.

Configuration Profiles

In verschiedenen Stages haben Anwendungen i. A. unterschiedliche Einstellungen. MP Config bietet dazu Configuration Profiles an, von denen eines mit der Property mp.config.profile aktiviert werden kann. Die davon betroffenen Config Properties können entweder mit einem Präfix benannt werden (z. B. %dev.database) oder in separaten Properties-Dateien eingetragen werden (z. B. META-INF/microprofile-config-test.properties).

Nehmen wir an, wir hätten diese beiden Konfigurationsdateien:

# META-INF/microprofile-config.properties
database: postgres
%dev.database: h2
# META-INF/microprofile-config-test.properties
database: derby

Dazu denken wir uns noch eine Variable, die per Injektion den Konfigurationswert namens database zugewiesen bekommt:

@Inject
@ConfigProperty(name = "database")
String database;

Normalerweise wäre der Wert nun postgres, im Profil dev jedoch h2 und im Profil test derby. Das Profil kann bspw. beim Start der Anwendung durch Angabe der System Property mp.config.profile ausgewählt werden:

java -Dmp.config.profile=test -jar …

Property Expressions

In den Konfigurationdateien META-INF/microprofile-config[-profile].properties
dürfen nun Properties im Wert einer anderen Property verwendet werden, und zwar mehrfach, eingeschachtelt oder auch mit Default-Wert. Auch dazu ein kleines Beispiel:

META-INF/microprofile-config.properties
server.url=http://${server.host:example.org}:${server.port}/${server.endpoint}
server.port=8080
server.endpoint=${server.endpoint.path.${server.endpoint.name:foo}}
server.endpoint.path.foo=api/foo
server.endpoint.path.bar=api/bar

Unter der Annahme, dass keine der aufgeführten Properties durch andere Config Sources mit Werten belegt wird, hätte die Property server.url den Wert http://example.org:8080/api/foo.

Weitere Informationen

Wir versorgen Sie gerne mit weiterem Input:

Dirk Weil, GEDOPLAN GmbH

Runtime-Modelle für Java EE bzw. Jakarta EE

Beim ersten Release des damals noch J2EE genannten Standards war das Betriebsmodell ganz klar so ausgerichtet, dass ein oder einige wenige Application Server als Träger von Enterprise-Applikationen genutzt werden sollten. Wie eine Art Betriebssystem stellten die Server die Infrastruktur – Transaktionen, Persistenz, Kommunikation, Isolation etc. – zur Verfügung, die dann von Anwendungen nach dem Deployment genutzt werden konnte.

Aus organisatorischen Gründen wurden Server aber zunehmend mit nur einer Anwendung betrieben. Hardware-Virtualisierung und leichtgewichtige Server machten es möglich, entsprechend viele Instanzen effektiv betreiben zu können.

Wir können heute also davon ausgehen, dass Server und Anwendung nicht unbedingt scharf voneinander getrennt sind, sonder eher verschiedene Aspekte einer Einheit darstellen. Man hört häufig die Meinung, dass damit das Konzept des Application Servers unbrauchbar geworden ist und moderne Serveranwendungen nur mit dedizierten Frameworks wie Spring Boot aufgebaut werden können. Ich möchte im Folgenden darstellen, dass das so nicht stimmt und JEE-Anwendungen sehr wohl in modernen Betriebsumgebungen ihren Platz haben.

Klassisches Deployment

Das klassische Deployment-Format sind WAR-Files. Ursprünglich mal für Web-Anwendungen gedacht (daher das W) ist dies seit Java EE 6 das General Purpose Deployment Format. Ein WAR-File wird in einen dafür vorgesehen Deployment-Ordner gelegt oder über spezielle Werkzeuge im Server deployt.

Da ein WAR-File nur die kompilierten Anwendungsklassen und – falls nötig – Bibliotheken außerhalb des Standards – enthält, sind die Deployments vergleichsweise klein. Eine Demo-Anwendung, die Sie in https://github.com/GEDOPLAN/jee-runtimes-demo finden, bringt gerade einmal 12 kB als WAR-File auf die Waage. Dabei enthält die Anwendung einen zwar einfachen, aber technisch kompletten Stack aus REST-API, Injektion mittels CDI und Persistenz mit JPA.

Das WAR-File kann auf einen beliebigen Jakarta-EE-Server deployt werden, z. B. auf einen WildFly 24. Der belegt im Vergleich viel Platz auf der Festplatte: ca. 250 MB.

Angenehm ist die Trennung von Anwendung und Infrastruktur. Sämtliche Laufzeit-Container befinden sich im Server, die Anwendung koppelt sich daran nur schwach über Interfaces und Standardklassen. Die Konfiguration von Subsystemen wie Datenbanken (oder Messaging, Mail, …) geschieht im Server. Die Anwendung referenziert die Konfiguration über logische Namen ohne konkrete Kenntnisse von bspw. Passwörtern.

Deployment als Kommando-Parameter

Der klassische Weg umfasst zwei Schritte: Installation eines Servers und Deployment der Anwendung. Manchen ist das zu aufwändig: sie würden einen einfachen Start des Servers inklusive der Anwendung in nur einem Schritt bevorzugen. Das ist mit JEE-Produkten auch recht leicht möglich.

Payara Micro ist ein solches Produkt. Es stellt einen Server in Form eines JAR-Files dar. Es ist nur ca. 77 MB groß. Die zu deployende Anwendung wird beim Start einfach als Parameter mitgegeben:

java -jar payara-micro.jar jee-runtimes-demo.war

Ähnlich funktioniert WildFly Bootable JAR. Hier kann der Server-Anteil sogar an die Bedürfnisse der Anwendung angepasst werden (im Beispiel-Projekt JAX-RS+CDI+JPA). Auch hier wird das Deployment durch einen Startparameter erledigt:

java -jar wildfly-bootable.jar --deployment=jee-runtimes-demo.war

Darüber hinaus bietet das zum Build genutzte Plugin sogar einen Development Mode an. Wird die Anwendung mit

mvn wildfly-jar:dev-watch

gestartet, überwacht sie kontinuierlich die Sourcen und deployt Änderungen automatisch.

Quarkus

Die bisherigen Modi nutzen alle ein Standard-WAR-File als Deployment-Einheit. Es ist also – wenn man so will – auch für Payara Micro und WildFly Bootable JAR immer noch ein Server vorhanden, auf den eine Anwendung deployt wird.

Quarkus geht den Weg (wie auch Spring Boot), die Anwendung mit dem Server zu einer ausführbaren Einheit zu verbinden. Die benötigten Serverbestandteile wie REST-, CDI- oder JPA-Implementierung werden im Build als einfache Dependencies eingebunden. Heraus kommt ein JAR-File, das einfach auf der Kommandozeile gestartet werden kann:

java -jar quarkus-app/quarkus-run.jar

Das JAR-File enthält übrigens nicht den gesamten Code der Anwendung, sondern referenziert ihn in dedizierten Unterverzeichnissen für den eigentlichen Anwendungscode und die Infrastruktur. Diese Trennung macht es leichter, bspw. Docker-Images zu erstellen und dabei nicht die eher statische Infrastruktur-Schicht bei jedem Rebuild neu erstellen zu müssen.
Das gesamte Verzeichnis quarkus-app ist für unsere Demo-Anwendung nur ca. 31 MB groß, ist also im Vergleich mit den bisherigen Serverlösungen nochmals erheblich verkleinert.

Quarkus verlegt einige Dinge aus der Laufzeit in die Build-Zeit, wie z. B. Entscheidungen über die Zuordnung von CDI Beans zu Injektionszielen. Damit wird der im Standard beim Anwendungsstart laufende, auf Reflection basierende Container-Start erheblich beschleunigt.

Unsere Demo-Anwendung startet damit in ca. 1,5 s (im Vergleich zu 4,7 s für WildFly Bootable JAR). Wem das immer noch nicht schnell genug ist, kann die Anwendung auch noch in Native Code übersetzen lassen. Dann sind Startzeiten im Millisekunden-Bereich erreichbar.

Auch Quarkus hat einen Development Mode, der ähnlich funktioniert, wie es oben für WildFly Bootable JAR beschrieben ist. Start mit

mvn quarkus:dev

Fazit

Java EE und Jakarta EE sind weit weg von den zunächst schwergewichtigen Serverlösungen der Anfangszeit (seitdem sind ja auch gut 20 Jahre vergangen!).

Wer eine Deployment-Einheit in Form eines WAR-Files bevorzugt, kann sie in einen klassischen Server deployen oder aber mit nur einem Kommando Server und Anwendung zugleich starten.

Es ist jedoch auch möglich, bei unverändertem Anwendungscode eine Anwendung mit eingebetteten Serveranteilen zu bauen, die recht klein ist und äußerst schnell startet.

Wer also heute noch behauptet, Java EE sei für moderne Anwendungen ungeeignet, hat vermutlich einige Jahre der JEE-Entwicklung verschlafen.

Weitere Informationen

Wir versorgen Sie gerne mit weiterem Input:

  • Unser Expertenkreis Java ist eine regelmäßige Vortragsveranstaltung zu allem aus dem Java-Ökosystem. Melden Sie sich kostenfrei an unter https://gedoplan.de/java-events/.
  • In die Tiefe gehen wir in unseren Seminaren, z. B. Power Workshop Jakarta EE oder Microservices mit Quarkus – kompakt, zu finden auf https://gedoplan.de.
  • Zweimal im Jahr finden Sie in unserem Firmenmagazin GEDOPLAN aktuell Fachartikel zu den Themen, die uns bewegen. Fordern Sie die Print-Ausgabe an oder lesen Sie online unter https://gedoplan.de/publikationen/.

Dirk Weil, GEDOPLAN GmbH