JAX Blog

Cloud | Quarkus

Mit Quarkus Schallgeschwindigkeit erreichen

Oct 22, 2019

Anfang dieses Jahres ist Red Hat mit Quarkus in die Arena eingetreten, in der Spring Boot und Micronaut bereits darum kämpfen, das beliebteste Full-Stack Framework für die Erstellung von Microservice- und Serverless-Apps zu werden. Was genau unterscheidet Quarkus von seinen Mitbewerbern?

In diesem Artikel lernen Sie die Grundlagen zum Erstellen von Anwendungen mit Quarkus kennen, indem Sie Code aus Spring PetClinic konvertieren, um eine cloud-native Quarkus-Anwendung mit den besten Java-Bibliotheken und -Standards wie Hibernate Panache, RESTEasy und GraalVM zu erstellen.

Was ist Quarkus?

Container First und Cloud Native: Bei Quarkus handelt es sich um ein Kubernetes-natives Java Framework, das Java in der neuen Welt von Serverless-Apps, Microservices, Containern und Cloud zu einer führenden Plattform machen soll. Indem Quarkus Oracles GraalVM nutzt, um native Apps zu erstellen, kann es überaus schnelle Startzeiten in der Größenordnung von Millisekunden sowie eine geringe Speichernutzung für Apps erreichen. Diese Eigenschaften ermöglichen automatisches Scale up und Scale down für Microservices in Containern sowie Function-as-a-Service(FaaS-)-Apps.

Imperative und Reactive: Obwohl Java-Entwickler schnell ein Cloud-natives, ereignisgesteuertes, asynchrones und reaktives Modell einführen können, um Geschäftsanforderungen für die Erstellung von hochkonkurrierenden und reaktionsschnellen Anwendungen zu erfüllen, sind die meisten unter ihnen eher mit dem imperativen Programmiermodell vertraut und möchten diese Erfahrung nutzen, um eine neue Plattform wie Quarkus einzuführen. Quarkus unterstützt sowohl imperative als auch reaktive Programmierparadigmen für Microservices, indem MicroProfile 2.2, die Reactive-Streams-Operators-Spezifikation und sogar Reactive Messaging für die Interaktion mit Apache Kafka vollständig unterstützt werden.

Optimiert auf die Freude des Entwicklers

Die Vision hinter Quarkus strebt mehr als nur Produktivität an: Die Nutzung soll Spaß machen! Deshalb hat das Team dahinter viel Aufmerksamkeit darauf verwendet, dass Livecodierung, Extensions und Unified Configuration gut funktionieren.

  • Im Entwicklungsmodus, den Sie mit mvn compile quarkus: devstarten können, unterstützt Quarkus Livecodierung, indem geänderte Dateien transparent kompiliert werden, wenn eine HTTP-Anfrage eingeht.
  • Das Extension-System soll dazu beitragen, ein lebendiges Ökosystem rund um Quarkus zu schaffen. Extensions, die im Grunde nichts anderes als Projektabhängigkeiten sind, konfigurieren, booten und integrieren ein Framework oder eine Technologie in eine Quarkus-App. Dazu stellen sie GraalVM die richtigen Informationen zur Verfügung, damit Ihre App nativ kompiliert werden kann.
  • Eine einzige Konfigurationsdatei (application.properties) genügt, um Quarkus sowie alle Extensions zu konfigurieren. Um die Größe dieser Datei zu verringern, sollte jede Extension sinnvolle Standardkonfigurationseigenschaften bereitstellen.

Wie teste ich meine Apps mit Quarkus?

Bevor wir uns näher mit dem Code befassen, ist es sinnvoll, einen ersten Blick auf den Testansatz von Quarkus zu werfen. Mit Quarkus können Sie Tests in zwei verschiedenen Modi ausführen, nämlich JVM Mode und Native Mode.

Gemeinhin erweitern die Testklassen im Native Mode die Tests im JVM Mode und werden in einem Docker-Container unter Verwendung der von GraalVM erstellten Native App ausgeführt. Der Vorteil der Wiederverwendung derselben Testklasse für JVM- und native Tests besteht darin, dass wir direkt zu Beginn eines Projekts Tests schreiben können.

Es hat sich als nützlich erwiesen, mit HTTPie die Integrität neuer REST Services zu überprüfen, obwohl Sie auch curloder wgetverwenden können, wenn Sie sich damit besser auskennen. HTTPie (http) ist ein mächtiges Kommandozeilenprogramm mit JSON-Unterstützung, Plug-ins und vielem mehr.

Erste Schritte mit Quarkus

Zum Zeitpunkt der Niederschrift dieses Artikels (August 2019) ist die neueste Version von Quarkus Version 0.21.1. Aus dem Versionsnummernschema können Sie ableiten, dass Quarkus derzeit als Beta eingestuft wird. Es ist wichtig zu wissen, dass zum jetzigen Zeitpunkt jede neue Quarkus-Version wahrscheinlich ihre Abhängigkeiten und Bibliotheken auf die neuesten Versionen aktualisieren wird. Daher habe ich für diesen Artikel die neuesten verfügbaren Versionen von Java, Maven und GraalVM verwendet: GraalVM Community Edition Version 19.2.0, AdoptOpenJDK 8u222 und Maven 3.6.1.

Erstellen einer Quarkus-App aus der Spring-PetClinic-Demo-App

Um ein Gefühl für Quarkus zu entwickeln, konvertieren wir den Code aus der Spring-PetClinic-Demo-App, um eine Cloud-native Quarkus-App zu erstellen. Beginnen wir mit der Ausführung der Spring-PetClinic-App:

git clone https://github.com/spring-projects/spring-petclinic.git
cd spring-petclinic

mvn clean package
mvn spring-boot:run

Abschließend stellen wir sicher, dass die Spring-PetClinic-App ordnungsgemäß funktioniert, indem wir entweder http://localhost:8080in unserem bevorzugten Browser öffnen oder den REST Service mit HTTPie anrufen: http :8080/vets.

Wie zu erwarten war, wird eine JSON-ähnliche Liste aller Tierärzte in der PetClinic-App zurückgegeben. Nun erstellen wir diesen REST Service in Quarkus neu.

Ähnlich wie bei Spring Boot und Micronaut können wir das Quarkus-Maven-Plug-in verwenden, um unsere erste „Hallo-Welt“-App mit Quarkus zu erstellen und auszuführen, wie in Listing 1 gezeigt.

Listing 1

mvn io.quarkus:quarkus-maven-plugin:0.21.1:create \
  -DprojectGroupId=com.github.acme \
  -DprojectArtifactId=quarkus-petclinic \
  -DclassName="com.github.acme.quarkus.petclinic.web.resource.VetResource" \
  -Dpath="/vets"

cd quarkus-petclinic
./mvnw compile quarkus:dev

Wenn wir den resultierenden Dienst testen, indem wir http :8080/vetsaufrufen, wird Hallo im Klartext zurückgegeben. Nicht wirklich spannend, aber was kann man von einer „Hallo-Welt“-App noch erwarten? Wir fahren fort, indem wir das Domänenmodell hinzufügen.

Implementierung des Domänenmodels

JPA, der De-facto-Standard im Object Relational Mapping, wird in Quarkus mit Hibernate ORM vollständig unterstützt. Um Hibernate zu konfigurieren, benötigen wir auch eine Datenquelle, um die Verbindungen zu einer Datenbank herzustellen. In Quarkus ist Agroal die Standardimplementierung für Datenquellen und Connection Pools. Wir können Unterstützung für Hibernate und Agroal hinzufügen, indem wir ihre Quarkus-Erweiterungen zu unserem Projekt hinzufügen:

./mvnw quarkus:add-extension -Dextensions="agroal, hibernate-orm, jdbc-h2, jdbc-mariadb"

Da diese Aktion die Maven-pom.xml-Datei ändert, müssen wir den quarkus:dev-Build stoppen. Gleichzeitig können wir das Modell (Entity Classes) aus der Spring PetClinic in unsere neu erstellte Quarkus PetClinic kopieren.

Da die Domänenklassen Spring-spezifischen Code verwenden, müssen wir etwas mehr als die Import- und Paketnamen korrigieren. Glücklicherweise können wir von Lambdamethoden in Java 8 profitieren, was diese Änderungen fast trivial macht.

Nachdem die Kompilierungsfehler behoben wurden, schlägt die Ausführung von mvn compile quarkus:devleider noch fehl, wie in Listing 2 zu sehen.

Listing 2

  ERROR [io.qua.dev.DevModeMain] Failed to start quarkus: 
java.lang.RuntimeException: io.quarkus.builder.BuildException: Build failure: Build failed due to errors
     [error]: Build step 
io.quarkus.hibernate.orm.deployment.HibernateOrmProcessor#build threw an exception: 
io.quarkus.deployment.configuration.ConfigurationError: Hibernate extension cannot
 guess the dialect as no JDBC driver is specified by 'quarkus.datasource.driver'

Betrachtet man die Fehlermeldung, so ist der Grund, warum Quarkus nicht startet, offensichtlich. Wir haben noch keine Datenbank konfiguriert.

Konfiguration der Datenbanken in Quarkus

Derzeit bietet Quarkus Erweiterungen für die folgenden Datenbanken an:

  • H2
  • PostgreSQL
  • MariaDB (und MySQL)
  • Microsoft SQL Server

Um die Konsistenz mit Spring PetClinic zu gewährleisten, benötigen wir H2-In-Memory-Datenbanken für die Entwicklung und MariaDB (MySQL) für die Produktion. Ähnlich wie bei Spring und Micronaut können Sie Quarkus-Konfigurationsprofile verwenden, um zwischen verschiedenen Laufzeitumgebungen zu unterscheiden. Voreingestellt versteht Quarkus Entwickler(dev)- und Produktionsprofile (prof). Im Moment müssen wir in der Konfigurationsdatei zumindest H2 konfigurieren (Listing 3).

Listing 3: Kofigurationsdatei: „src/main/resources/application.properties“

%dev.quarkus.datasource.url=jdbc:h2:mem:default
%dev.quarkus.datasource.driver=org.h2.Driver
%dev.quarkus.datasource.username=username-default
%dev.quarkus.datasource.min-size=3
%dev.quarkus.datasource.max-size=13
%dev.quarkus.hibernate-orm.database.generation=drop-and-create
%dev.quarkus.hibernate-orm.sql-load-script=db/hsqldb/data-and-schema.sql

%prod.quarkus.hibernate-orm.database.generation=none
%prod.quarkus.datasource.driver=org.mariadb.jdbc.Driver

Jetzt müssen wir nur noch die SQL-Datei für HSQLDB aus der Spring PetClinic in unsere neue Quarkus PetClinic kopieren. Wenn wir jetzt versuchen, unsere Quarkus-PetClinic-App erneut mit mvn compile quarkus:devauszuführen, sollte sie fehlerfrei starten. Beim Testen sehen wir, dass der Aufruf von http :8080/vetsimmer noch Hallo im Klartext zurückgibt. Wir beheben das, indem wir die Klasse VetResource implementieren.

Implementierung der VetResource-Klasse

Um die Klasse VetResource ähnlich wie Spring PetClinic zu implementieren, werden wir das Repository-Pattern verwenden und die Klasse VetRepository mit der Quarkus-Erweiterung Hibernate Panache erneut implementieren. Bei Hibernate Panache handelt es sich um eine Quarkus-Erweiterung, die das Schreiben von Entitäten zugleich simpel und unterhaltsam machen soll. Sie müssen Ihre Entitäten lediglich die Klasse PanacheEntity erweitern lassen, die Spalten von privaten Feldern in öffentliche Felder ändern und können dann alle Getter und Setter, automatisch generierte IDs usw. entfernen.

Außerdem muss unsere Quarkus-App natürlich auch noch in der Lage sein, JSON zu konsumieren und zu produzieren. Dazu stehen zwei Erweiterungen zur Verfügung, die die JSON-Bindung unterstützen: Jackson und RESTEasy. Da RESTEasy die ausgereifteste zu sein scheint, werden wir die RESTEasy-JSON-Bindungserweiterung verwenden.

Die beiden Abhängigkeiten können wir in einer einzigen Anweisung hinzuzufügen: ./mvnw quarkus:add-extension -Dextensions=”hibernate-orm-panache, resteasy-jsonb”. Standardmäßig wird eine listAll()-Methode schon von der PanacheRepository-Klasse bereitgestellt. Wir werden auch die Methode find by last name in unsere VetRepository-Klasse aufnehmen und dabei die von der PanacheRepository-Superklasse bereitgestellte find()-Methode nutzen (Listing 4).

Listing 4: „VetRepository“

@ApplicationScoped
public class VetRepository implements PanacheRepository<Vet> {
  public Vet findByName(String name) {
    return find("lastName", name).firstResult();
  }
}

Jetzt können wir die VetResource-Klasse wie in Listing 5 implementieren.

Listing 5: „VetResource“

@Path("/vets")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class VetResource {
  @Inject
  VetRepository vetRepository;

  @GET
  public List<Vet> list() {
    return vetRepository.listAll();
  }

  @GET
  @Path("/name/{name}")
  public Vet findByName(@PathParam("name") String name) {
    return vetRepository.findByName(name);
  }
}

Wenn wir jetzt versuchen, unsere Quarkus-PetClinic-App erneut mit mvn compile quarkus:dev auszuführen und den Aufruf von http :8080/vets zu testen, erhalten wir die gleiche JSON-Antwort wie mit der Spring-PetClinic-App!

Erstellen einer nativen App

Eine der besten Sachen an Quarkus ist die hervorragende Unterstützung für die Erstellung nativer Images. Um sie einzurichten, müssen wir die Datei application.properties mit einem Produktionsprofil (prod) aktualisieren, um MariaDB zu unterstützen (ähnlich wie bei Spring PetClinic). Vorausgesetzt, dass GraalVM bereits installiert ist, können wir die native App auf folgende Weise testen und ausführen:

gu install native-image
./mvnw clean package -Pnative
./target/quarkus-petclinic-1.0-SNAPSHOT-runner

Wenn ich auf meinem Computer (MacBook Pro 2017) das Timing von Spring Boot mit Quarkus-Dev- und Quarkus-Native-Modus vergleiche, erhalte ich die in Tabelle 1 aufgeführten Ergebnisse.

Anlaufen time http :8080/vets
Spring Boot 5.654 s – real 0m0.270s
– user 0m0.215s
– sys 0m0.045s
Quarkus Dev 3.430 s – real 0m0.278s
– user 0m0.218s
– sys 0m0.047s
Quarkus Native 0.019 s – real 0m0.265s
– user 0m0.210s
– sys 0m0.045s
Tabelle: Vergleich von Spring Boot mit Quarkus-Dev-Modus und Quarkus-Native-Modus

Wir sehen, dass der Start unserer nativen Quarkus-App viel schneller läuft als Spring Boot und Quarkus-Dev-Modus. In diesem Fall dauert die Bearbeitung der Anfrage in allen Varianten ungefähr gleich lang.

Zusammenfassung

Wie wir gesehen haben, ist die Konvertierung vorhandener JSON REST Services von Spring Boot zu Quarkus ein Kinderspiel. Wir erhalten eine Anwendung mit weniger Code, die viel schneller startet, und mit dem zusätzlichen Bonus einer nativ aktivierten, ausführbaren Kubernetes-Datei!
Der Quellcode ist bei GitHub verfügbar. Dort finden Sie auch Testklassen, mit denen Sie die Quarkus-App sowohl im JVM- als auch im nativen Modus testen können.

Alle News der Java-Welt:

Behind the Tracks

Agile, People & Culture
Teamwork & Methoden

Clouds & Kubernetes
Alles rund um Cloud

Core Java & Languages
Ausblicke & Best Practices

Data & Machine Learning
Speicherung, Processing & mehr

DevOps & CI/CD
Deployment, Docker & mehr

Microservices
Strukturen & Frameworks

Performance & Security
Sichere Webanwendungen

Serverside Java
Spring, JDK & mehr

Software-Architektur
Best Practices

Web & JavaScript
JS & Webtechnologien

Digital Transformation & Innovation
Technologien & Vorgehensweisen

Domain-driven Design
Grundlagen und Ausblick

Spring Ecosystem
Wissen in Spring-Technologien

Web-APIs
API-Technologie, Design und Management