Quarkus: Der Blick über den Tellerrand

Seite 3: Unifies imperative and reactive: Ein einheitliches Programmiermodell

Inhaltsverzeichnis

In den vergangenen Jahren drehten sich viele Diskussionen um den Begriff der "reaktiven Programmierung". Doch bei der Frage, was darunter genau zu verstehen ist, gehen die Ansichten erheblich auseinander. Eine einfache Erklärung beschränkt sich darauf, dass HTTP-Endpunkte CompletionStage<> oder CompletableFuture<> von Java zurückgeben sollen, damit Services ihre Arbeit im Hintergrund verrichten können und keine Threads blockieren. Tatsächlich ist gerade "nichtblockierend" das wichtigste Merkmal (siehe Kasten "Reaktive Programmiermodelle").

Die Reactive-Streams-Initiative lebt seit JDK 9 im Paket java.util.concurrent.Flow, sie ist aber nach wie vor essenziell für JDK-8-basierte Programme. Businessanwendungen sollten in der Regel keine eigene Implementierung dieser Spezifikationen mitbringen, sondern eine der vorliegenden Implementierungen wählen. Reaktive Frameworks unterstützen in der Regel die Schnittstellen der reaktiven Streams und nutzen intern eine bestimmte Implementierung, auf die dann auch die eigentliche Anwendung zugreifen kann – so wie bei Quarkus umgesetzt. Während Spring Boot naheliegenderweise auf das Project Reactor setzt, vertraut das Quarkus-Team auf RxJava 2 sowie die MicroProfile-Implementierung Reactive Streams Operators.

An den Schnittstellen – sowohl zu HTTP als auch zu Messaging-Systemen – kommt dann in der Regel org.reactivestreams.Publisher zum Einsatz. Überträgt man <<hello-world>> in die reaktive Welt, wird in <<hello-world-reactive>> schnell klar, was unter "unification" gemeint ist:

package org.acme.quickstart;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import org.eclipse.microprofile.reactive.streams.operators.ReactiveStreams;
import org.reactivestreams.Publisher;

@Path("/hello")
public class GreetingResource {

@GET
@Produces(MediaType.SERVER_SENT_EVENTS)
public Publisher<String> hello() {
return ReactiveStreams.of("Hello, World!").buildRs();
}
}

In derselben JAX-RS-Infrastruktur auf demselben Webserver lässt sich gleichberechtigt ein Publisher nutzen – die notwendigen Kompilierungszeitabhängigkeiten vorausgesetzt.

Mehr Infos

Reaktive Programmiermodelle

Reaktive Programmiermodelle reagieren auf Bereitschaft und Verfügbarkeit von Ressourcen. Anwendungen können vorhandene Ressourcen effizienter nutzen, ohne über deren Belegung spekulieren zu müssen. Innerhalb einer Anwendung, die dem Paradigma der reaktiven Programmierung folgt, bestimmen asynchrone Datenflüsse die Programmierung. Die Anwendung reagiert auf Ereignisse im Datenstrom.

Sie können Anwendungsfälle für reaktive Programmierung zum Beispiel anhand von Infrastrukturanforderungen ableiten und sich dabei stärker an Skalierbarkeit und Stabilität orientieren als am absoluten Durchsatz. Microservice-Architekturen tendieren zu einem höheren Speicher- und CPU-Ressourcenbedarf als monolithische Systeme. Reaktive Anwendungen erlauben dem Laufzeitsystem, die vorhandenen Ressourcen effizienter zu nutzen. Inhaltlich sind ereignisgesteuerte Systeme ein Anwendungsfall.

Aus "Spring Boot 2 – Moderne Softwareentwicklung mit Spring 5" von Michael Simons und Mark Paluch

Damit die Motivation nicht leidet, wollen Entwickler bei der Arbeit Spaß haben. Zu den häufig genannten Dingen, die Spaß verhindern, zählen vor allem:

  • unbekanntes, zusätzliches Tooling
  • aufwendige Konfiguration
  • lange Turn-around-Zeiten (Code-Änderungen, Neuladen, Effekt bewundern)
  • lange Test- und Deploy-Zyklen

Quarkus zeigt sich im Hinblick auf diese Aspekte erfreulich ausgereift. Bereits out of the box entlastet Quarkus Entwickler bei vielen lästigen Aufgaben – insbesondere auch bei den Turn-around-Zeiten. So bietet beispielsweise das Maven-Plug-in einen Developer-Modus (mvn compile quarkus:dev), der das Quellverzeichnis (src) der Anwendung überwacht. Geht ein Request ein, beginnt unmittelbar das Kompilieren der geänderten Klassen und die Anwendung startet neu. Dieser Neustart der Anwendung (nicht der Umgebung) ist zudem schneller als der initiale Start. Ein Hot-Replace der Hello-World-Anwendung findet inklusive Kompilierung im Bruchteil einer Sekunde statt – Spaß ist also garantiert. Eine nähere Beschreibung der Funktionsweise des Starts folgt im Abschnitt "Wie werden die Ziele erreicht?"

Quarkus-Tests lassen sich mit der speziellen JUnit-5-Erweiterung @QuarkusTest aus dem Paket quarkus-junit5 erstellen. Diese steuert das Testframework. Aufgrund der schnellen Startzeiten empfehlen die Autoren des Frameworks Integrationstests der Endpunkte mit REST-Assured. Die Empfehlung ist nachvollziehbar und funktioniert – auch wenn üblicherweise Unit-Tests am Anfang stehen sollten. Innerhalb eines @QuarkusTest funktioniert CDI, Services lassen sich per @Inject in Tests injizieren und direkt testen.

Spring bot bereits einfachen Test-Support und war damit seiner Zeit voraus (vergleiche TDD mit Spring Boot 2). Für ein heutzutage neu veröffentlichtes Framework wie Quarkus sollte es daher selbstverständlich sein, einfache Möglichkeiten für Tests bereitzustellen.