Micronaut, MicroProfile oder Spring Boot/Cloud? Microservices Frameworks im Vergleich

Von: Thomas Bayer
Datum: 7. Januar 2020

Welches Framework eignet sich am besten für die Entwicklung und den Betrieb von Microservices mit Java? Die Frage nach der Auswahl des passendsten Frameworks beantwortet dieser Artikel anhand von Kriterien wie Ressourcenverbrauch, Funktionsumfang und Reife. Betrachtet werden die drei populärsten Microservices Frameworks für die Java Plattform.

Für den Vergleich wurde eine einfache, aus zwei Microservices bestehende Anwendung erstellt. Die Microservices machen von Features wie Loadbalancing , Retries und ServiceDiscovery Gebrauch. Die Microservices wurden auf jede der drei Plattformen portiert, gemessen und verglichen. Das Ergebnis fasst dieser Artikel zusammen.

Im ersten Teil dieses Artikels werden die einzelnen Frameworks kurz beschrieben. Im zweiten Teil werden einzelne Kriterien wie z.B. Funktionsumfang untersucht.

Teil 1 - Die Frameworks

Um den Rahmen dieses Artikels nicht zu sprengen werden im Folgenden nur spezifische Eigenschaften des jeweiligen Frameworks beschrieben, welche das jeweilige Produkt von den anderen unterscheiden. Detailliertere Informationen zu den einzelnen Frameworks sind in den folgenden Artikeln zu finden:

  • Microservices mit Spring Boot & Spring Cloud
  • Microservices mit dem Eclipse MicroProfile

In diesem Artikel wurden die folgenden Versionen betrachtet:

Framework

Version

Micronaut

1.2.8

MicroProfile

3.2 und Quarkus 1.1.0 Final

Spring Boot

Spring Boot 2.2.2 mit Spring Cloud Hoxton

1. Micronaut

Entwickelt wird Micronaut bei objectcomputing von den Entwicklern des Grails Framework. Die Erfahrungen, die mit Grails gesammelt wurden, sind in die Architektur des Micronaut Frameworks eingeflossen. Ebenso wie Grails ist Micronaut ein Fullstack Framework, das aber auf Microservices und nicht auf Web Anwendungen ausgelegt ist.

Micronaut verwendet dynamische Features wie Proxys und Reflection sparsam, um die Startup-Zeiten und den Footprint der Anwendungen zu optimieren.

Die meisten Guides und Beispiele sind beim Micronaut mehrsprachig in den Sprachen Groovy, Java und Kotlin. Wer gerne mit Kotlin Microservices entwickeln möchte, sollte Micronaut in die engere Wahl ziehen.

Vorteile:

  • Mächtiger Code Generator für die Kommandozeile
  • Gute Unterstützung für Kotlin
  • Kompakt und mächtig
  • Unterstützung für native Java Images

Nachteile:

  • Kein Standard
  • Geringe Verbreitung

2. Eclipse MicroProfile

Während Micronaut und Spring Boot konkrete Projekte sind, ist das MicroProfile ein Standard, für den mehrere Produkte verschiedener Hersteller zur Verfügung stehen.

Das MicroProfile ergänzt die Standards der Java Enterprise Edition um weitere Funktionen, die besonders für Microservices interessant sind. Wie der Name bereits andeutet ist das MicroProfile ein Profil, also eine Sammlung von Standards, die von den Implementierungen zur Verfügung gestellt werden. Wird gegen das Profil entwickelt, kann man sich darauf verlassen, dass die Laufzeitumgebungen alle benötigten Bibliotheken und Funktionen bereits enthalten. Das ermöglicht die Erstellung kleinerer Anwendungen, da diese die Bibliotheken nicht mitliefern müssen.

Auf dem Markt gibt es eine ganze Reihe von MicroProfile Implementierungen. Der Anwender ist nicht auf einen Hersteller angewiesen. Die Tabelle unten listet einige Open Source Implementierungen des MicroProfiles auf.

Projekt

Hersteller

Lizenz

Github Sterne, Stand 20.12.2019

Helidon

Oracle

Apache License 2

1.700

Quarkus

JBoss / Red Hat / IBM

Apache License 2

3.500

OpenLiberty

IBM

Eclipse Public License 1.0

660

KumuluzEE

Sunesis Ltd. aus Slowenien

MIT

249

Thorntail

JBoss / Red Hat / IBM

Apache License 2

381

Payara

Payara Foundation

Common Development and Distribution Licence

730

Jemo

Eclipse Foundation

Eclipse Public License 2.0

2

Für den Vergleich und die Messungen in diesem Artikel, wurde Quarkus von Red Hat verwendet. Zum jetzigen Zeitpunkt ist Quarkus nach github-Sternen am weitesten verbreitet und kann unserer Meinung nach gut die Vision hinter dem MicroProfile verkaufen.

Vorteile:

  • Das MicroProfile ist ein Standard
  • Es gibt einen Wettbewerb zwischen den Server Herstellern.
  • Hat von den Nachteilen von Spring gelernt und versucht diese zu vermeiden.
  • Bietet Unterstützung für native Java Images

Nachteile:

  • Der Standardisierungsprozess bremst Innovationen
  • Die Standards sind noch nicht vollständig. Beispielsweise fehlt die Service Discovery .
  • Die (noch) geringe Verbreitung

3. Spring Boot & Spring Cloud

Das auf dem Spring Framework basierende Spring Boot ist das populärste der hier vorgestellten Frameworks. Mit Spring Boot lassen sich komfortabel Anwendungen realisieren, die für den produktiven Betrieb kritische Eigenschaften wie Healthchecks oder vielfältige Konfigurationsmöglichkeiten bieten. Spring Clou d ist eine Sammlung von Modulen, die Spring Boot um Funktionen speziell für die Cloud erweitert. Zu diesen Funktionen gehören u.a. Service Discovery , Distributed Tracing oder ein API Gateway. Neben Spring Cloud gibt es noch Spring Cloud Netflix , welches Open Source Projekte von Netflix in Spring Boot Anwendungen nutzbar macht. In diesem Artikel wird die Kombination aus:

  • Spring Boot
  • Spring Cloud
  • Spring Cloud Netflix

als ein einziges Framework betrachtet und der Vereinfachung halber mit Spring Boot bezeichnet.

Wer das Spring Framework kennt und einsetzt, ist geneigt, für die Microservices Spring Boot zu verwenden, da das darunterliegende Framework bereits bekannt ist. Micronaut und MicroProfile sind von Spring stark beeinflusst, so dass Spring kundige Entwickler auch mit diesen Frameworks leicht zurechtkommen sollten.

Vorteile:

  • Große Verbreitung, Community und Installationsbasis
  • Größter Funktionsumfang
  • Umfangreiche Dokumentation ( Referenz, Bücher, Stackoverflow)

Nachteile:

  • Komplexität des Ökosystems
  • Kein Standard
  • Nicht alle Funktionen arbeiten fehlerfrei zusammen oder die Konfiguration muss zeitaufwendig angepasst werden. Beispielsweise funktionierte in bestimmten Versionen nicht der gleichzeitige Einsatz von Service Discovery und Cloud Config .

4. Weitere Frameworks

Neben den oben beschriebenen Frameworks gibt es weitere mit denen sich Microservices für die Java Plattform realisieren lassen. Diese wurden im Artikel nicht betrachtet, da sie entweder Bestandteil der obigen Frameworks sind oder sie unterstützen die Cloud nicht in dem Maße, wie die hier vorgestellten.

Teil 2 – Vergleich

In diesem Teil des Artikels werden verschiedene Aspekte der Frameworks beschrieben und verglichen.

5. Entwicklung

Alle drei Frameworks ermöglichen die komfortable und effiziente Entwicklung von Anwendungen und Microservices. Der Entwickler wird bereits beim Einrichten eines Projektes unterstützt.

5.1. Einrichten eines Projektes

Der Entwickler wird beim Aufsetzen eines Projektes von allen Kandidaten gut unterstützt. Spring Boot und das MicroProfile verwenden eine Webseite, über die man sein Projekt konfiguriert und anschließend ein ZIP mit einem Projektgerüst herunterladen kann. Micronaut geht einen anderen Weg und offenbart die Einflüsse vom Fullstack Framework Grails: Die Codegenerierung wird über die Kommandozeile gesteuert.

5.1.1 Micronaut

Micronaut verwendet wie bereits Grails ein Kommandozeilenwerkzeug für das Anlegen und Erweitern von Projekten. Das folgende Kommando legt ein neues Projekt mit den Funktionen Service Discovery und einer API Beschreibung mit Swagger an:

mn create-app --features=discovery-consul,swagger-kotlin de.predic8.bestell-service

Nach der Ausführung erhält der Entwickler ein Projektgerüst, das sich kompilieren und starten lässt.

Bei Micronaut kann einer Anwendung jederzeit neuer Gerüst-Code mit dem mn -Tool hinzugefügt werden. Es kann Code für die folgenden Komponenten erzeugt werden:

  • REST Controller
  • Bean
  • Test
  • Job
  • Web Sockets Server und Client
  • Client Interfaces

Der folgende Aufruf fügt einem Projekt einen Controller für REST Aufrufe hinzu:

mn create-controller Bestellungen

Die Arbeitsweise mit der Kommandozeile und dem Code Generator ist typisch für Micronaut. Bei Micronaut kommt das Kommandozeilenwerkzeug immer wieder während der Entwicklung zu Einsatz. Spring Boot unterstützt im Gegensatz dazu nur einmalig zu Beginn der Entwicklung die Verwendung eines Code Generators. Bei Quarkus können zusätzlich im Nachhinein über ein Maven Kommando Extensions eingebunden werden. Die Einbindung beschränkt sich aber auf die Manipulation des Maven POMs oder der build.gradle Datei. Quellcode für Java oder Kotlin wird im Nachhinein keiner erzeugt.

5.1.2 MicroProfile

Zu Beginn der Entwicklung eines Service kann über die Webseite:

https://code.quarkus.io/

mit einem Formular ein Programmgerüst für ein Quarkus Projekt erzeugt werden. Zur Auswahl stehen:

  • Das Buildtool: Maven oder Gradle
  • Verschiedene Bibliotheken (Dependencies)

Die Liste der Bibliotheken ist beachtlich, wenn man bedenkt, dass es den Quarkus erst seit kurzer Zeit gibt. Nach der Auswahl kann das Projektgerüst als ZIP-Datei heruntergeladen und in einer Entwicklungsumgebung geöffnet werden. Das erzeugte Projekt ist bereits ausführbar und kann mit dem Befehl:

mvn quarkus:dev

zusammen mit dem Quarkus-Server gestartet werden. Der Quarkus wird über Maven-Dependencies aus dem Netz geladen und sofort gestartet. Eine separate Installation ist nicht notwendig.

Das erzeugte Projekt erhält bereits:

  • Ein Profil für die Kompilierung eines nativen Graal Images
  • Dockerfiles für die Java VM und native Images
  • Ein Maven Plugin für den Quarkus Server

Die Dateien im Projektgerüst sind teilweise bereits individuell an das Projekt angepasst. Beispielsweise wird in den Dockerfiles der jeweilige Projektname verwendet.

Formular für die Konfiguration eines Starter Projektes für ein Quarkus Projekt

Abbildung : Konfigurator für den Projektgenerator des Quarkus Servers

Der Projektgenerator des Quarkus ermöglicht den problemlosesten Start bei der Entwicklung. Es gibt momentan nur eine Quarkus-Version zur Auswahl.

Neben dem Starter für Quarkus gibt es weitere Starter für die anderen Server des Microprofiles. Der Screenshot unten zeigt den Starter bei MicroProfile.io selbst, welcher eine Reihe von Servern wie den Helidon und OpenLiberty unterstützt.

Formular für die Konfiguration eines Starter Projektes für ein MicroProfile Projekt

Abbildung : Der Starter für das MicroProfile

5.1.3 Spring Boot

Wie beim Quarkus gibt es für Spring ebenfalls einen Initializr unter der Adresse:

https://start.spring.io

Mit dem Konfigurator kann ebenfalls ein Projektgerüst erzeugt werden. Die Auswahl an Optionen ist größer als beim Quarkus. Zur Auswahl stehen:

  • Maven oder Gradle
  • Java, Kotlin und Groovy
  • Die Spring Boot und die Java Version
  • War und Jar Packaging
  • Abhängigkeiten in Form von Bibliotheken

Der Screenshot unten zeigt den Spring Initializr.

Formular für die Konfiguration eines Starter Projektes für ein MicroProfile Projekt

Abbildung : Spring Initializr

5.2 Hot Reloading

Die Entwicklung von Server Software findet normalerweise in einen Try- und Error Zyklus mit den folgenden Phasen ab:

  • Ändern des Codes
  • Speichern
  • Bauen
  • Stoppen des Servers
  • Starten des Servers
  • Ausprobieren oder Testen

Mit dem Feedback aus der Phase des Ausprobierens wird wieder ein neuer Zyklus gestartet und erneut der Code geändert. Hot Reloading oder automatische Restarts des Servers können die Zyklen und damit die Entwicklung beschleunigen. Als Nebeneffekt erhöht sich die Zufriedenheit der Programmierer.

5.2.1 Micronaut

Micronaut trägt außer einer schnellen Startup Zeit nichts zur Beschleunigung des Entwicklungszyklus bei. Momentan ist ein Hot Reload nicht vorgesehen. Es gibt aber Diskussionen (Link: https://github.com/micronaut-projects/micronaut-core/blob/master/src/main/docs/guide/cli/reloading/automaticRestart.adoc ) im Internet, die beschreiben, wie sich mit Micronaut automatische Restarts realisieren lassen. In Verbindung mit der schnellen Startup-Zeit kann ein automatischer Restart bei Codeänderungen die Entwicklung beschleunigen.

5.2.2 MicroProfile / Quarkus

Quarkus besitzt eine Hot Reload Funktion mit der schnelle Try- and Error Schleifen möglich sind. Nach einer Codeänderung muss der Server nicht neu gestartet werden, um die Änderungen zu übernehmen. Bei jedem API Aufruf prüft der Server, ob es Veränderungen am Quellcode gab. Bei Bedarf, kompiliert Quarkus die betroffenen Dateien und lädt die Klassen zur Laufzeit. Die Try- and Error Zeiten werden dadurch erheblich verkürzt. Im Gegensatz zu den Spring Devtools überwacht Quarkus den Quellcode anstatt den Classpath. Auf diese Weise ist das Anstoßen eines neuen Build-Vorgangs nicht notwendig.

5.2.3 Spring Boot

Die Spring Boot Devtools bieten ebenfalls eine Funktion für automatische Reloads bei einer Änderung des Classpaths an. Dazu muss in der Entwicklungsumgebung ein Build ausgelöst werden. Zusätzlich unterstützen die Devtools ein LiveReload des Web Browsers. Wird eine Ressource, die im Browser angezeigt wird geändert, so lädt dieser automatisch die neue Ressource.

5.3 REST APIs

Für die Realisierung von REST APIs verwenden alle drei Frameworks unterschiedliche Bibliotheken. Das MicroProfile verwendet als einziger den JAX-RS Standard. Spring und Micronaut verwenden beide eine eigne Bibliothek. Der Unterschied zwischen den Frameworks ist wie aus den Listings unten ersichtlich, kleiner als man denken könnte. Alle drei verwenden ähnliche Annotationen, so dass Entwickler ohne Probleme auf ein anderes Framework wechseln können. Die Listings unten zeigen das Gerüst für ein einfaches REST API für das jeweilige Framework.

Das erste Listing zeigt einen REST Controller für Micronaut. Die Programmiersprache ist in diesem Beispiel Kotlin.

					@Controller("/bestellung")
					class BestellungController {
					
						@Post
						fun bestellen( bestellung: Bestellung): Preis {
					
						}
					}

Das nächste API ist mit dem JAX-RS Standard in Java für das MicroProfile realisiert.

					@Path("/bestellungen")
					public class BestellungenApi {
					
						@POST
						public Preis bestellen(Bestellung best) throws Exception {
					
						}
					}

Das letzte Beispiel verwendet Spring MVC.

					@RestController("/bestellungen")
					public class BestellApi {
					
						@PostMapping
						public Preis bestellen(@RequestBody Bestellung best) {
					   
						}
					}

Spring und das MicroProfile unterstützen die Verlinkung von Ressourcen mit Hypermedia. In der Micronaut Codebasis findet man Klassen, die auf eine Unterstützung von HATEOAS hinweisen. Wir konnten jedoch keine Code Beispiele finden und wissen nicht, ob Micronaut Hypermedia unterstützt. Da Hypermedia keine nennenswerte Verbreitung gefunden hat, ist dem Kriterium Hypermedia wenig Bedeutung beizumessen.

Micronaut

MicroProfile / Quarkus

Spring Boot & Cloud

REST API für Server

Eigene

JAX-RS

Spring MVC

Server Framework

Netty

Quarkus: Vert.x, Undertow

Tomcat, Jetty, Undertow, Netty

Open API / Swagger

x

x

x

HATEOAS

?

x

x

Ausliefern von statischen Web Seiten

x

x

x

Streaming

x

x

x

5.3.1 REST Clients

Möchte ein Microservice einen anderen aufrufen, so benötigt er einen HTTP oder besser einen speziellen REST Client. Alle drei Frameworks bieten REST Clients, die in den eigenen Code injiziert werden können.

Das Beispiel unten zeigt einen deklarativen Client für Micronaut, bei dem die Methode eines Interface über Annotationen mit dem notwendigen HTTP Mapping versorgt wird. Die Antwort des Servers im JSON Format wird automatisch in ein Objekt des im Interface angegebenem Preis-Typs umgewandelt. Das Framework kümmert sich um die Umsetzung zwischen Java Objekten und den HTTP Nachrichten.

					@Client
					interface PreisClient {
					
						@Get("/preis/{artikel}")
						fun bestellen(@PathVariable artikel: String ) : Preis
					}

Deklarative Clients für das MicroProfile und Spring Boot lassen sich auf ähnliche Weise entwickeln.

Die Adresse des Zielservice kann im Code hart codiert, konfiguriert oder dynamisch über Service Discovery und eine Registry ermittelt werden. Das MicroProfile unterstützt momentan nicht die Service Discovery über eine externe Registry. Das Listing unten zeigt eine Konfiguration für den Micronaut, bei der die Adresse eines Zielservice mit drei Werten für die Lastverteilung überschrieben wird:

				micronaut:
				http:
					services:
						preis-client:
							urls:
								- http://localhost:8081/preis/
								- http://localhost:8082/preis/
						 		- http://localhost:8082/preis/

Die anderen Frameworks bieten ebenfalls eine Möglichkeit, Adressen statisch zu konfigurieren.

Das MicroProfile schreibt dem JAX-RS Standard als API für den Client vor. Der tatsächlich verwendete Client hängt vom MicroProfile Server ab. Die MicroProfile Server verwenden u.a. Apache CXF, RESTEasy und Jersey.

Micronaut

MicroProfile / Quarkus

Spring Boot & Cloud

Client Bibliothek

Eigene

JAX-RS (Apache CXF, RESTEasy, Jersey)

Feign

Deklarativer Client

X

X

x

Loadbalancing

X

X

x

Retries

x

X

x

Service Discovery

X

-

x

5.4 Datenbankzugriff

Micronaut bietet mit Micronaut Data eine eigene Persistenz Lösung, die von GORM und Spring Data inspiriert wurde. GORM ist die Persistenzschicht des Fullstackframeworks Grails. Im Gegensatz zu Spring Data und GORM verwendet Micronaut Data Ahead of Time (AoT) compilation anstatt Reflection. Das heißt, dass z.B. nativer Code für Custom Finder bereits beim Kompilieren erzeugt wird, anstatt erst durch den Just in Time Compiler (JIT) bei der Ausführung des Programms. Der Vorteil liegt in einer leichtgewichtigeren und schnelleren Runtime. Als Backend unterstützt Micronaut Data SQL und Hibernate.

Quarkus bietet als Alternative zu JPA den Hibernate Wrapper Panache an. Mit Panache kann Persistenz-Code kompakter und übersichtlicher realisiert werden als mit JPA. Beispielsweise können Entitäten auf getter und setter Methoden verzichten.

Micronaut

MicroProfile / Quarkus

Spring Boot & Cloud

Persistenz API

JPA, GORM, Micronaut Data, MyBatis

JPA, Panache

JPA, jOOQ, R2DBC

NoSQL

MongoDB, Neo4j, Redis, Cassandra

Amazon DynamoDB, MongoDB, Neo4j

MongoDB, Neo4j, Redis, Cassandra, Geode

Transaktionen

X

X

X

5.5 Dependency Injection

Das MicroProfile verwendet für die Dependency Injection eine Teilmenge des Contexts and Dependency Injection for Java Standards der Java Enterprise Edition .

Die dynamische Erzeugung von Beans über Reflection zur Laufzeit hat negative Auswirkungen auf die Startzeit von Spring Boot Anwendungen. Quarkus und Micronaut verwenden dagegen für die Dependency Injection Prozessoren für Annotation, die über AoT bereits zur Compile-Zeit das Programm vorbereiten. Eine etwas längere Compile-Zeit verkürzt die Startzeit und den Ressourcenverbrauch. Kurze Startzeiten sind besonders für den Einsatz in der Cloud oder serverlosen Betrieb interessant.

Alle drei Frameworks nehmen dem Entwickler mit Magie, die im Verborgenen abläuft, Arbeit ab. Die Magie setzt auf der Dependency Injection und auf Autoconfiguration auf. Zum einen erleichtern diese Mechanismen im Hintergrund die Arbeit zum anderen werden Einsteiger verunsichert, wenn sie nicht verstehen, was genau abläuft und warum. Wer sich aber auf die Magie einlässt, möchte diese bereits nach kurzer Zeit nicht mehr missen.

Über eine Erweiterung kann auch das Spring Framework zusammen mit Micronaut eingesetzt werden. Spring Komponenten können dann auch mit Micronaut verwendet werden.

Micronaut

MicroProfile / Quarkus

Spring Boot & Cloud

Dependency Injection API

Eigen (Proprietär)

Teilmenge von CDI

Spring Framework

Zeitpunkt der Dependency Injection

Compile Time

Compile Time

Run Time

5.6 Konfiguration

Konfiguration ist eine der Stärken der Dependency Injection Frameworks. In der Anwendung hinterlegte Defaultwerte können auf vielfältige Weise überschrieben werden. Für den Betrieb in der Cloud ist die Konfiguration über Umgebungsvariablen geeignet.

Profile für verschiedene Umgebungen wie z.B. Development, Test und Produktion unterstützen alle drei Kandidaten.

Micronaut

MicroProfile / Quarkus

Spring Boot / Cloud

Format der Konfigurationsdateien

YAML oder Property Datei

Property Datei, Für die Quarkus Version 1.2 ist YAML Unterstützung geplant

YAML oder Property Datei

Externe Konfiguration

X (HashiCorp Consul, AWS, Spring Config Server)

Kann mit einer ConfigSource selbst entwickelt werden.

X (HashiCorp Consul, AWS, Spring Config Server)

Profile für Development, Test, Produktion

X

X

X

6. Test

Alle vorgestellten Frameworks unterstützen auf vielfältige Weise automatisierte Tests. Für Integration-Tests werden Test-Klassen angeboten, die die Dependency Injection unterstützen und über separate Profile konfiguriert werden können.

Um eine Klasse zu testen, müssen die von der Klasse verwendeten Objekte zur Verfügung gestellt werden. Möchte man z.B. einen REST Controller, der eine Abhängigkeit zu einem Service-Objekt hat testen, so gibt es die folgenden Möglichkeiten:

  • Von Hand mit dem new -Keyword das Service-Objekt erzeugen und setzen.
  • Ein Service-Objekt über Dependency Injection zu injizieren.
  • Das Service-Objekt zu mocken.

Alle drei Möglichkeiten werden von den Frameworks unterstützt. Mocking und API Tests werden ebenfalls von allen drei Kandidaten angeboten.

Insgesamt ist die Testunterstützung in allen drei Frameworks gut. Da Spring Boot die umfassendste Testunterstützung für Technologien wie JDBC, JPA, MongoDB und vieles mehr bietet, gewinnt Spring Boot diesen Punkt mit einem kleinen Vorsprung.

Micronaut

MicroProfile / Quarkus

Spring Boot / Cloud

Annotation für DI im Test

@MicronautTest

@QuarkusTest

@SpringBootTest

Mitgelieferte Testframeworks

JUnit 5, Spock

JUnit 5, REST-Assured

JUnit 5, AssertJ, Hamcrest, MockMvc, JSONassert

API Tests

- (1)

REST-Assured

MockMvc

Mock Framework

MockK

Über CDI @Alternative

Mockito

Tests mit Objekten

X

X

X

Mocking

X

X

X

Tests mit Abhängigkeiten

X

X

X

(1)Es wird kein spezielles Framework für API Tests mitgeliefert. Es kann aber jedes beliebige API Testframework für Micronaut verwendet werden.

7. Footprint und Ressourcenverbrauch

Der Ressourcenverbrauch von Java Anwendungen ist für normale Serveranwendungen und Clients kein Problem. In der Cloud sind 300 MByte Hauptspeicherverbrauch dagegen bereits eine Hausnummer. Besonders wenn anstatt eines Monolithen, dutzende oder hunderte Java basierte Microservices betrieben werden, macht sich der Hauptspeicherverbrauch schmerzhaft bemerkbar. Aus diesem Grund bieten die Frameworks Möglichkeiten den Footprint eines Microservice zu reduzieren und so die Nachteile von Java in der Cloud wett zu machen.

Um den Footprint der drei Frameworks vergleichen zu können, wurde für die Messungen ein einfacher Beispiel Service entwickelt. Der Service ruft einen weiteren Service auf und verwendet u.a. die Funktionen Retry und Loadbalancing .

Die Messungen wurden nicht wissenschaftlich durchgeführt. Um genau zu sein, müssten keine anderen Prozesse laufen und der Effekt durch Caching ausgeschaltet werden. Die Zeiten liegen aber so weit auseinander, dass sich die Ergebnisse reproduzieren lassen und eine gewisse Aussagekraft haben.

7.1 Startzeit

Mit einer Startzeit von einer Sekunde für einen typischen Microservice geht dieser Punkt an das MicroProfile und den Quarkus Server. Die mit Spring Boot realisierte Alternative kommt auf eine Startzeit von fast sieben Sekunden. Ursache für den großen Unterschied, ist die Art und Weise wie die Dependency Injection umgesetzt wurde. Bei Spring wird diese zur Laufzeit beim Start einer Anwendung durchgeführt. Beim Quarkus und Micronaut werden bereits beim Kompilieren die Abhängigkeiten eingeklinkt. Eine schnellere Startzeit und eine längere Kompilezeit sind die Folgen.

Diagramm: Startzeit der Microservices

Abbildung : Diagramm: Startzeit der Microservices

7.2 Größe der Services

Unter Größe eines Service verstehen wir die Größe eines FAT-Jars, also eines Archives, welches die Anwendung und alle benötigten Bibliotheken samt HTTP-Server enthält. Ein FAT-Jar kann mit einem Befehl wie dem Folgenden gestartet werden:

					java -jar bestell-service-0.1-all.jar

java -jar bestell-service-0.1-all.jar

Alle drei Frameworks sind bei den Abhängigkeiten und den Abhängigkeiten der Abhängigkeiten nicht wirklich leichtgewichtig. Für das einfache Microservices-Beispiel waren bei allen drei Frameworks zwischen 200 und 260 Abhängigkeiten notwendig.

Das Spring Boot FAT-Jar ist ungefähr doppelt so groß wie das der beiden anderen Implementierungen. Fairerweise muss erwähnt werden, dass das Spring Framework auch mehr Möglichkeiten bietet.

Diagramm: Größe der FAT-Jars in MByte

Abbildung : Diagramm: Größe der FAT-Jars in MByte

Neben den FAT-Jars gibt es die Möglichkeit, Anwendungen ohne Abhängigkeiten zu erzeugen und die benötigten Bibliotheken im Server bereitzustellen. Bei Application Servern und der Java Enterprise Edition ist dieses Vorgehen üblich. Da die Archive keine oder weniger Abhängigkeiten enthalten, werden die Archive bedeutend kleiner. Das JAR mit der Anwendung ohne Abhängigkeiten ist beim MicroProfile nur 8 KByte groß. Das FAT-Jar mit derselben Anwendung hatte eine Größe von 19 MByte. Bei den Microservices wird der Vorteil der kleinen JARs wieder relativiert, da jeder Service meist in einer eigenen Runtime läuft, die dann wieder die Abhängigkeiten enthält. Bei Spring sind zwar die FAT-Jars üblich, es können aber auch leichtgewichtigere WARs erzeugt werden.

7.3 Größe der Docker Images

Die Größe eines Docker Images hängt in erster Linie vom verwendeten Base-Image ab. Je nach Base-Image ist das Docker Image unseres Microservice zwischen 120 und 700 MByte groß. Das verwendete Framework spielt eine untergeordnete Rollte. Die Frage ist, ob die Größe eines Docker Images überhaupt eine Rolle spielt, da bei mehreren Images, gemeinsame Schichten geteilt werden.

Die Größe des Docker Images sollte kein Kriterium für das eine oder andere Framework spielen. Anders sieht es aus, wenn man Java Microservices mit in der Sprache Go realisierten Microservices vergleicht. Interessant ist die Größe eines Docker-Images im Vergleich zu einem nativen Image. Bei den Ergebnissen unten in der Tabelle ist zu beachten, dass für das MicroProfile ein kompaktes Java 8 Basisimage auf Basis von Alpine Linux zum Einsatz kommt. Die Images für Micronaut und Spring basieren auf einem größeren Java 11 Image.

Basis Image

Größe Basis Image

Größe Docker Image

Micronaut

adoptopenjdk/openjdk11-openj9:jdk-11.0.1.13-alpine-slim

237 MByte

258 MByte

MicroProfile / Quarkus

fabric8/java-alpine-openjdk8-jre

87 MByte

107 MByte

Spring Boot

adoptopenjdk/openjdk11-openj9:jdk-11.0.1.13-alpine-slim

237 MByte

282 MByte

7.4 Native Images mit der GraalVM

Die GraalVM erlaubt die Ausführung von mehreren Programmiersprachen auf einer virtuellen Java Maschine. Eine weitere für Microservices interessante Funktion ist die Kompilierung zu einem nativen Image, einer direkt auf dem Betriebssystem ausführbaren Datei. Die Vorteile der nativen Images, sind ihre geringe Größe, ihr sparsamer Ressourcenverbrauch und die kurze Startup-Zeit.

Um die Größe zu reduzieren werden in ein Image nur die benötigten Klassen eingebaut und alles Unnötige weggelassen.

7.5 MicroProfile mit Quarkus

Wird über den unter https://code.quarkus.io/ erreichbaren Generator ein Projekt erzeugt, enthält dieses bereits zwei Dockerfiles. Eines auf der Basis von OpenJDK für die Java VM und eines auf der Basis der GraalVM für ein natives Image. Die Dockerfiles enthalten in den Kommentaren Anleitungen mit denen das Bauen auf Anhieb gelingt.

Ein natives Image des Referenz-Microservice für Mac OSX ist nur 40 MBytes groß und startet in einer hundertstel Sekunde!

7.6 Micronaut

Wird beim Erzeugen eines Projektes das Feature graal-native-image angegeben, so enthält dieses bereits Konfigurationen, Skripte und Dockerfiles für das Erzeugen von nativen Images und passenden Docker Containern.

7.7 Spring Boot

Das kommende Spring Framework in der Version 5.3 wird native GraalVM Images ohne zusätzliche Konfiguration und Workarounds unterstützen. Bis dahin ist es nur mit viel Aufwand möglich ein kleines Beispiel als natives Graal Image zu kompilieren. Es finden sich im Internet nur wenige Beiträge, die mit einer nativen Kompilierung von Spring Boot Erfolg hatten. Die Unterstützung für die GraalVM ist im Dezember 2019 noch nicht gegeben. Eine Unterstützung für Spring erfordert Anpassungen der GraalVM, die noch nicht erfolgt sind. Wahrscheinlich wird es im Laufe des Jahres 2020 eine Unterstützung geben.

7.7.1 Fazit

Momentan gibt es mit Spring Boot aufgrund der Besonderheiten der Dependency Injection zur Laufzeit Schwierigkeiten ein natives Image zu erzeugen. Mit Quarkus und Micronaut gelingt die Erzeugung eines nativen Images auf Anhieb. Es ist abzusehen, dass die GraalVM reifen wird und weitere Limitierungen entfallen werden. Wahrscheinlich wird es bald mit allen Frameworks möglich sein, ohne Mehraufwand native Images zu erzeugen.

Die Ergebnisse in diesem Abschnitt sollte man aus diesen Gründen nicht zu ernst nehmen. Die GraalVM ist noch nicht ausgereift und die Unterschiede zwischen den Frameworks sollten bald ausgeglichen sein.

8. Unterstützung für die Cloud

Die Frameworks unterstützen die Cloud auf vielfältige Art und Weise. Die folgenden Abschnitte beschreiben die Cloud Funktionen, die unterstützten Plattformen und den serverless-Betrieb.

8.1 Cloud Features

Die folgenden Fragen stellen sich, wenn Services verteilt in der Cloud betrieben werden:

  • Wie findet ein Service andere Services um diese aufzurufen?
  • Wie können Serviceaufrufe zugestellt werden?
  • Wie lassen sich die einzelnen Services konfigurieren?
  • Wie behält man den Überblick über Serviceaufrufe und Servicebeziehungen?
  • Wie lassen sich Fehler und Engpässe in der Performanz finden?
  • Wie kann die Last verteilt und die Servicebereitschaft garantiert werden?
  • Wie läuft eine Authentifizierung und Autorisierung bei Service zu Service Aufrufen ab?

Microservices Frameworks bieten Funktionen an, die diese Fragen beantworten und es ermöglichen, zahlreiche Services in der Cloud zu betreiben. Die Tabelle unten führt die wichtigsten Cloud Features für Microservices und ihre Unterstützung durch die drei Frameworks auf.

Feature

Micronaut

MicroProfile

Spring Boot / Cloud

Distributed Configuration

X (Consul, Spring Cloud Config, AWS Parameter Store)

- (1)

X (Spring Cloud Config, Consul, Zookeeper)

Service Discovery

X ( Consul, Eureka )

- (2)

X (Consul, Eureka, etcd, Zookeeper)

Client Side Loadbalancer

X ( Ribbon)

X

X ( Ribbon, Spring Cloud Loadbalancer )

Retries

X

X

X

Circuit Breaker

X

X

X ( Resilience4J, Sentinel, Spring Retry, Hystrix )

OpenTracing

X

X

X

API Gateway

-

-

X ( Spring Cloud Gateway, Zuul)

Management Endpoints

X

X

X

(1) Über eigene ConfigSources können Erweiterungen für Config Server erstellt werden. Siehe: Eclipse Microprofile Config Consul Extension von Arik Dasen ( https://github.com/rikcarve/mp-config-consul )

Die Konfiguration von Microservices mit dem MicroProfile kann auch über die Runtime z.B. über Docker oder Kubernetes erfolgen.

(2)Das MicroProfile bietet keine Service Discovery. Diese kann aber über die Runtime z.B. mit Docker oder Kubernetes realisiert werden

Die Microservices Frameworks bieten die Cloud-Funktionen auf Service bzw. Anwendungsebene an. Diese Funktionen können auch von der Cloud selbst oder von einer Service Mesh Infrastruktur angeboten werden. Microsoft Azure, Amazon AWS und Kubernetes bieten beispielsweise ebenfalls Service-Discovery, Routing und Loadbalancing an. Ob die Cloud Funktionen auf der Ebene der Anwendung, der Cloud oder des Service Meshes realisiert werden, ist eine Architektur Entscheidung. Es ist auch möglich die Dienste auf verschiedenen Ebenen zu nutzen.

Cloud Plattformen

Die Unterstützung für die Cloud haben sich alle drei Frameworks auf die Fahnen geschrieben. Unterstützt werden neben Docker und Kubernetes die Cloud Plattformen:

  • Azure
  • Amazon Web Services
  • Cloud Foundry
  • Google Cloud Plattform

8.2.1 Docker Support

Die von der Quarkus Starter Webseite erzeugten Projekte beinhalten Dockerfiles für OpenJDK basierte und native GraalVM Images. Der Code Generator des Micronauts kann ebenfalls Docker Images erzeugen. Der Spring Initializr erzeugt keine Dockerfiles, was aber nicht dramatisch ist, da die Erstellung eines Dockerfiles kein Problem sein sollte. Im Internet finden sich genügend Vorlagen.

8.2.2 Kubernetes Support

Kubernetes wird bereits von allen drei Frameworks mit zahlreichen Funktionen unterstützt.

Um einen anderen Microservice über HTTP unter Kubernetes über dessen Service aufzurufen, muss dieser angesprochen werden können. Die Service Discovery vom Micronaut und von Spring Cloud unterstützen Kubernetes Services. Für das MicroProfile fehlt eine Service Discovery . Auch ohne Service Discovery kann vom MicroProfile aus über den DNS Namen auf Kubernetes Services zugegriffen werden.

Konfigurationsparameter und Secrets wie Passwörter oder Zertifikate können in Kubernetes zentral verwaltet werden. Microservices benötigen diese Parameter zur Laufzeit. Mit einer Unterstützung von Config Maps oder Secrets können diese Parameter per Dependency Injection der Anwendung zur Verfügung gestellt werden. Die aktuelle Version 1.1.0 des Quarkus unterstützt keine Secrets und Config Maps von Kubernetes. Unterstützung gibt es aber in der MicroProfile Implementierung von IBM, dem OpenLiberty Server.

Zeit spart die Erzeugung von Kubernetes Deskriptoren aus der Projektkonfiguration. Der Quarkus Server unterstützt über eine Erweiterung das Generieren dieser Deskriptoren. Über die application.properties Datei kann auf die Generierung der Deskriptoren über ca. 3 Dutzend Werte Einfluss genommen werden. Zu den Werten zählen u.a.:

  • Version
  • Host und Port
  • Anzahl der Replicas
  • URL der Docker Registry
  • Labels
  • Readiness und Liveness Probes
  • Volumes (Speicher und Verzeichnisse)

Micronaut und Spring Cloud erweitern den Health Indicator um Informationen aus dem Kubernetes Cluster wie z.B. den Podnamen und Namespace.

Eine Besonderheit des Quarkus Servers ist die Erzeugung von Deployment und Service Deskriptoren für Kubernetes aus den Metadaten, also dem Maven POM oder der Gradle Datei.

Die Spring Cloud Kubernetes Funktion bietet u.a. Service Discovery und Configuration.

Die Kubernetes Profile Autoconfiguration von Spring Cloud erkennt, dass ein Microservice in einem Kubernetes Pod ausgeführt wird und aktiviert automatisch ein Kubernetes Profil, welches die Werte für die Konfiguration liefert.

Werden z.B. Beans mit der folgenden Annotation versehen, so werden diese nur unter Kubernetes erzeugt:

@Profile("kubernetes")

Für die Service Mesh Lösung Istio gibt es ebenfalls eine Profile Autokonfiguration für Spring Cloud . Ferner kann auf das Istio API zugegriffen werden, um u.a. auf Istio Regeln und Circuit Breaker zuzugreifen.

Micronaut

MicroProfile / Quarkus

Spring Boot / Cloud

Service Discovery

x

-

x

Config Maps

x

X ( OpenLiberty Server)

x

Secrets

x

X ( OpenLiberty Server)

x

Erzeugen von Kubernetes Ressourcen

-

Deployment und Service Deskriptoren

-

Readiness und Liveness Probes

x

X (Bereitstellen, Beschreiben in erzeugten Deployment)

x

Pod Health Indicator

x

-

x

Die Frameworks bieten ganz unterschiedliche Kubernetes Unterstützung. Es gibt aber kein Framework, welches sich in die eine oder andere Richtung wesentlich von den anderen abhebt. Insgesamt gibt es in allen Frameworks noch Spielraum für einen Ausbau des Kubernetes Supports.

8.3 Serverless

Eine Funktion die serverless betrieben wird, muss schnell starten und darf nur wenig Speicher und CPU benötigen. Native Images ermöglichen Aufgrund ihrer schnellen Startzeit und ihres geringen Ressourcenverbrauchs Java im Container serverless zu betreiben.

Generell kann mit jedem Framework serverless gearbeitet werden. In der Tabelle unten wurde nur aufgeführt, wenn das jeweilige Framework spezielle Unterstützung für eine Serverless-Plattform bietet.

Micronaut ermöglicht es serverlose Funktionen lokal über einen Web Server oder von der Kommandozeile aus zu testen.

Die MicroProfile Spezifikationen beschreiben keinen Serverless-Betrieb. Für den Quarkus Server gibt es aber eine Erweiterung für Amazon Lambda und Microsoft Azure.

Micronaut

MicroProfile / Quarkus

Spring Boot / Cloud

AWS Lambda

X

x

X

Azure Functions

-

x

x

Apache OpenWhisk

-

-

x

Oracle Fn

-

-

x

OpenFaas

x

-

-

Project.Fn

x

-

-

Riff

x

-

-

Jedes der Frameworks unterstützt den serverless-Betrieb auf der einen oder anderen Plattform. Die Frameworks sind jedoch nicht primär für den serverless-Betrieb konzipiert. Der Schwerpunkt liegt ganz klar auf den Microservices.

9. Message-Driven Microservices

Microservices können nicht nur über REST APIs miteinander kommunizieren. Es gibt auch die Möglichkeit die Services lose über einen Bus zu koppeln.

Das MicroProfile bietet selbst nur JMS Unterstützung über die JEE Standards. Für Quarkus gibt es auch Module für den Apache Kafka Broker und das AMQP Protokoll.

Die Unterstützung für Nachrichten-basierte-Middleware ist im Micronaut auf Kafka und RabbitMQ begrenzt. Für JMS gibt es keine spezielle Unterstützung. Es ist aber möglich eine JMS Client oder eine ActiveMQ Client Bibliothek einzuklinken und den Zugriff selbst zu programmieren.

Spring Boot bietet eine breite Unterstützung an Protokollen für das Messaging.

Micronaut

MicroProfile / Quarkus

Spring Boot / Cloud

Apache Kafka

X

X (Quarkus)

X

RabbitMQ

X

X (Quarkus)

X

JMS

-

X

X

AMQP

-

X (Quarkus)

X

MQTT

-

X (Quarkus)

X ( Über Spring Integration)

10. Programmiersprachen

Im Prinzip kann jede Sprache, die auf der Java Virtuellen Maschine läuft, mit den Frameworks zusammen eingesetzt werden. Bei der Verwendung von Kotlin gibt es Besonderheiten wie z.B., dass der Zugriff auf statische Methoden in Basisklassen nicht möglich ist. Probleme wie diese, können bei der Benutzung von Frameworks wie z.B. der Persistenz mit Panache Entwicklungszeit kosten. Zeit sparen Guides und Dokumentation für die Verwendung des Frameworks mit der jeweiligen Sprache. In der Dokumentation liegen auch die Hauptunterschiede bei der Sprachunterstützung zwischen den einzelnen Frameworks. Die Guides zu Micronaut kommen jeweils in einer Version für Java, Groovy und Kotlin. Damit bietet Micronaut die beste Unterstützung für andere JVM Sprachen.

Micronaut

MicroProfile / Quarkus

Spring Boot / Cloud

Java

++

++

++

Kotlin

++

+

+

Groovy

++

-

+

11. Sicherheit

Das Absichern von verteilten Systemen ist keine leichte Aufgabe. Beispielsweise könnte es die folgenden Anforderungen geben:

  • Die Kommunikation zwischen den Services muss verschlüsselt werden.
  • Single Sign On muss über die Instanzen eines Service funktionieren.
  • Der Username eines Aufrufers muss von Service zu Service weitergegeben (propagiert) werden.

Die Frameworks unterstützen den Entwickler und Admin bei der Umsetzung dieser Anforderungen.

Die meisten Standards und Mechanismen zur Sicherheit bietet Spring Boot in Verbindung mit Spring Security . Das Spring Security Modul bietet u.a. Unterstützung für:

  • Access Control Lists ( ACL)
  • Single Sign On mit CAS
  • LDAP
  • OAuth2
  • OpenID
  • JSON Web Token (JWT)
  • JSON Web Signature (JWS)
  • JSON Web Encryption (JWE)
  • JSON Web Key (JWK)

Quarkus und Micronaut bieten Funktionen für die bei Microservices verbreiteten JSON Web Tokens und OAuth2.

Micronaut

MicroProfile / Quarkus

Spring Boot / Cloud

JSON Web Token

X

x

X

JWT Token Propagation

X

x

?

Rollenbasierte Zugriffskontrolle über Annotationen

X

x

X

OAuth2

X

x ( Quarkus Preview)

X

Open ID

X

x ( Quarkus Preview)

x

12. Verbreitung und Reife

Spring Boot hat mit deutlichem Abstand die größte Verbreitung der drei Kandidaten. Micronaut und MicroProfile bzw. Quarkus sind ungefähr gleich auf.

Ich habe den Eindruck, dass das MicroProfile und der Quarkus Server in den letzten Monaten viel Aufmerksamkeit bekommen haben. 2019 gab es die stolze Zahl von 37 Releases beim Quarkus. Bleibt zu hoffen, dass sich dieser Trend 2020 fortsetzen wird.

Die Anzahl der github-Sterne in der Tabelle unten ist nicht ganz vergleichbar, da sich für Spring Cloud keine separaten Sterne finden, und Spring Boot nicht nur für Microservices und die Cloud verwendet werden kann.

Seit einigen Jahren sind bereits zahlreiche mit Spring Boot und Cloud realisierte Anwendungen und Microservices in Produktion. Für den Betrieb der Anwendungen liegt bereits genügend Erfahrung für den Betrieb vor. Micronaut und MicroProfile sind Stand Ende 2019 noch so neu, dass es noch kaum unternehmenskritische Microservices in Betrieb gibt.

Micronaut

MicroProfile / Quarkus

Spring Boot / Cloud

Github Sterne

3.200

3.200 MicroProfile

3.500 Quarkus

44.200 Spring Boot

Bücher bei Amazon

-

2

Spring Boot 20

Spring Cloud 9

Stand vom 17. Dezember 2019

13. Fazit

Die Entscheidung für eines der drei Frameworks ist nicht leicht. Alle drei adressieren dieselben Probleme, bieten einen ähnlichen Funktionsumfang und bieten eine vergleichbares Programmiermodell an. Die falsche Entscheidung ist, kein Framework für die Entwicklung und den Betrieb von Microservices oder Anwendungen in der Cloud zu verwenden. Alle drei Frameworks nehmen dem Entwickler viel Arbeit ab und unterstützen den Betrieb in der Cloud.

Wer bereits das Spring Framework oder Spring Boot einsetzt, für den wäre die logische Entscheidung bei Spring zu bleiben und ebenfalls die Cloud Eigenschaften von Spring Boot bzw. Cloud zu nutzen. Da die anderen Frameworks von Spring stark inspiriert sind, kommt ein Spring Entwickler aber auch schnell mit dem MicroProfile und mit Micronaut zurecht.

Für Spring Boot spricht der Funktionsumfang, die Verbreitung, die Reife, die Größe der Community, die Fülle an Dokumentation und Beispielen.

Für diejenigen, die über Erfahrungen mit JEE verfügen, bietet sich das MicroProfile an, da sie weiter mit CDI und JEE APIs arbeiten können.

Micronaut und das MicroProfile bzw. Quarkus sind jünger und moderner als Spring Boot . Das macht sich in der Größe des Footprints, an den Startzeiten und der Unterstützung für die GraalVM bemerkbar. Code für die beiden Frameworks ist meinem Empfinden nach etwas cleaner und kompakter als bei Spring Boot.

Nicht zu unterschätzen sind die Einsparungen bei der Einarbeitung. Micronaut und das MicroProfile sind übersichtlicher und schneller zu beherrschen. Aufgrund der kleineren Altlasten gibt es auch weniger Versionsprobleme und Inkompatibilitäten unter den Modulen als bei Spring Boot und Cloud .

Vor einer Entscheidung für eines der Frameworks empfiehlt sich die prototypische Entwicklung einer kleinen Anzahl von Microservices. Am besten eine Portierung derselben Services auf alle drei Frameworks.

14. Quellen

GraalVM
https://github.com/oracle/graal

GraalVM native image support
https://github.com/spring-projects/spring-framework/wiki/GraalVM-native-image-support

Micronaut Dokumentation
https://docs.micronaut.io/latest/guide/

MicroProfile bei github
https://github.com/eclipse/microprofile

Quarkus - Guides
https://quarkus.io/guides/

Spring Boot Reference Documentation
https://docs.spring.io/spring-boot/docs/2.2.3.BUILD-SNAPSHOT/reference/html/

Spring Cloud Dokumentation
https://cloud.spring.io/spring-cloud-static/Hoxton.RELEASE/reference/html/spring-cloud.html