Micronaut bietet AOT Kompilierung, reaktives NIO und Cloud-native Unterstützung für Microservices und serverlose Entwicklung. Könnte es Ihr nächstes Java-Framework sein? [...]
Das Spring-Framework hat die Backend-Java-Entwicklung lange Zeit dominiert, aber mehrere neue Frameworks stellen diesen Status quo in Frage. Micronaut ist eines der überzeugendsten. Entwickelt von dem Team, das Grails entwickelt hat, ist Micronaut für moderne Architekturen gemacht.
Dieser Artikel ist eine praktische Einführung in Micronaut. Wir beginnen mit einer einfachen RESTful-API-basierten Anwendung, refaktorisieren sie für reaktives Non-Blocking IO (reactive NIO) und werfen dann einen kurzen Blick auf Micronauts Unterstützung für Cloud-native Entwicklung in Microservices und serverlosen Architekturen.
Das Besondere an Micronaut
Micronaut bietet eine ganze Reihe von Vorteilen, die von älteren Frameworks wie Spring und Grails übernommen wurden. Es wird als „nativ cloud-nativ“ bezeichnet, was bedeutet, dass es von Grund auf für Cloud-Umgebungen entwickelt wurde. Zu seinen Cloud-nativen Funktionen gehören Umgebungserkennung, Service-Erkennung und verteiltes Tracing.
Micronaut bietet außerdem einen neuen IoC-Container (Inversion-of-Control), der eine AoT-Kompilierung (AoT = ahead-of-time) für einen schnelleren Start verwendet. AoT-Kompilierung bedeutet, dass die Startzeit nicht mit der Größe der Codebasis zunimmt. Das ist besonders wichtig für serverlose und container-basierte Bereitstellungen, bei denen die Nodes oft heruntergefahren und je nach Bedarf wieder hochgefahren werden.
Micronaut ist ein polyglottes JVM-Framework, das derzeit Java, Groovy und Kotlin unterstützt und dessen Unterstützung für Scala in Vorbereitung ist.
Schließlich unterstützt Micronaut die reaktive Programmierung. Entwickler können entweder ReactiveX oder Reactor innerhalb des Frameworks verwenden. Ab Micronaut 3, das im Juli 2021 veröffentlicht wurde, ist Reactor der empfohlene Ansatz. (Beachten Sie, dass in den neuen Versionen keine Reactive-Bibliotheken als transitive Abhängigkeiten enthalten sind).
Erste Schritte mit Micronaut
Micronaut ist auf jedem Unix-basierten System, einschließlich Linux und macOS, über SDKMan einfach zu installieren. Unter Windows laden Sie das Micronaut Binary herunter und fügen es zu Ihrem Pfad hinzu.
Sobald die Installation abgeschlossen ist, finden Sie das mn-Tool in Ihrer Befehlszeile.
Öffnen Sie eine Shell und suchen Sie eine geeignete Stelle. Geben Sie mn create-app micronaut-idg –build maven ein. Micronaut unterstützt Gradle und Maven über Wrapper, so dass Sie das Build-Tool nicht selbst installieren müssen. Ich bevorzuge Maven. Wenn Sie Gradle bevorzugen, lassen Sie das Flag –build maven im vorherigen Befehl weg.
Wenn Sie den Server mit mvnw mn:run starten, können Sie in Ihrem Browser auf http://localhost:8080/ gehen, und Sie erhalten eine Standard-JSON-Antwort „nicht gefunden“.
Wenn Sie das Layout des Beispielprojekts untersuchen, ist es ein Standard-Maven-Projekt mit einer Hauptklasse unter src/main/java/micronaut/idg/Application.java. Beachten Sie, dass die Hauptklasse einen eingebetteten Server ausführt. Wenn Sie Codeänderungen vornehmen, aktualisiert der Micronaut-Entwicklungsserver automatisch die laufende Anwendung.
Hinzufügen eines Micronaut-Controllers
Genau wie in Spring MVC können Sie Controller-Klassen hinzufügen, um URLs auf Code-Handler zu mappen. Fügen Sie eine Klasse unter src/main/java/micronaut/idg/controller/SimpleController hinzu. Verwenden wir diesen Controller, um eine Textantwort zu erstellen, wie in Listing 1 gezeigt.
Listing 1. Verwendung eines Micronaut-Controllers
package micronaut.idg.controller;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
@Controller("/simple")
public class SimpleController {
@Get(produces = MediaType.TEXT_PLAIN)
public String index() {
return "A Simple Endpoint";
}
}
In Listing 2 können Sie sehen, wie einfach es ist, eine JSON-formatierte Antwort zu erstellen.
Listing 2. Die JSON-formatierte Antwort
package micronaut.idg.controller;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import java.util.Map;
import java.util.HashMap;
@Controller("/simple")
public class SimpleController {
@Get(produces = MediaType.APPLICATION_JSON)
public Map index() {
Map msg = new HashMap();
msg.put("message", "Eine simple Nachricht");
return msg;
}
}
Listing 2 demonstriert Micronauts intelligenten Umgang mit dem Argument produces der @Get-Annotation. In diesem Fall gibt es die JSON-formatierte Antwort aus, die wir festgelegt haben.
Hinzufügen einer Micronaut-Serviceschicht
Die IoC-Implementierung von Micronaut ist unter der Haube einzigartig, weil sie vorher ausgeführt wird, aber sie ist dennoch eine vollständige Implementierung der CDI-Spezifikation (Contexts and Dependency Injection). Das bedeutet, dass Sie alle vertrauten DI-Annotationen verwenden können, die Sie wahrscheinlich von Spring kennen (wie @Inject).
In Listing 3 verdrahten wir eine Service Layer Bean, um eine Nachricht bereitzustellen. In einer realen Anwendung könnte diese Klasse einen Datenspeicher oder eine andere Remote-API über eine Datenzugriffs-Bean aufrufen.
Erstellen Sie einen Ordner src/main/java/micronaut/idg/service und fügen Sie die beiden Dateien aus Listing 3 hinzu – eine Schnittstelle (Simple) und ihre Implementierung (SimpleService).
Listing 3. Erstellen einer einfachen Service Layer Bean
// Simple.java
package micronaut.idg.service;
public interface Simple {
public String getMessage();
}
// SimpleService.java
package micronaut.idg.service;
import jakarta.inject.Singleton;
@Singleton
public class SimpleService implements Simple {
public String getMessage(){
return "A simple service message";
}
}
Jetzt können Sie Ihre neue Serviceschicht verwenden, indem Sie den Service in den SimpleController injizieren, den Sie in Listing 1 erstellt haben. Listing 4 zeigt die Constructor-Injektion.
Listing 4. Injizieren der Service-Bean in den Controller
@Controller("/simple")
public class SimpleController {
@Inject
private final Simple simpleService;
public SimpleController(@Named("simpleService") Simple simple) { // (1)
this.simpleService = simple;
}
@Get(produces = MediaType.APPLICATION_JSON)
public Map index() {
Map msg = new HashMap();
msg.put("message", simpleService.getMessage());
return msg;
}
}
Die entscheidende Arbeit wird in der mit „(1)“ kommentierten Zeile geleistet, in der die Service-Bean mit ihrem Namen verdrahtet ist. Wenn Sie nun http://localhost:8080/simple besuchen, sehen Sie die Antwort der Serviceschicht: {„message“: „Eine simple Nachricht“}
Reaktive NIO mit Micronaut
Als nächstes wollen wir die Verwendung von Micronaut mit Reactor untersuchen. In diesem Fall werden wir einfach unsere aktuelle Anwendung so umgestalten, dass sie Reactor und non-blocking IO verwendet. Die Anwendung führt dieselbe Aufgabe aus, verwendet aber einen nicht-blockierenden Stack – Reactor und Netty – unter der Haube.
Wie bereits erwähnt, enthält Micronaut 3 standardmäßig keine reaktive Bibliothek, also fügen Sie zunächst den Reactor-Kern zu Ihrem Maven-POM hinzu, wie in Listing 5 gezeigt.
Listing 5. Reactor zur pom.xml hinzufügen
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>3.4.11</version>
</dependency>
Nun können Sie zum SimpleController zurückkehren und ihn wie in Listing 6 gezeigt ändern.
Listing 6. Den Controller nicht-blockierend machen
import reactor.core.publisher.Mono;
//...
@Get
public Mono<map> index() {
Map msg = new HashMap();
msg.put("message", simpleService.getMessage());
return Mono.just(msg);
}
}
Wie Sie sehen können, verpacken wir einfach den gleichen Rückgabetyp (eine Abbildung von string/string) mit der Reactor Mono-Klasse.
Ähnliche Unterstützung gibt es für die reaktive Nutzung von Remote-Diensten, so dass Sie eine Anwendung vollständig auf nicht-blockierende IO ausführen können.
Verwendung von Micronauts CLI zur Erstellung neuer Komponenten
Sie können das Kommandozeilen-Tool von Micronaut verwenden, um Komponenten hinzuzufügen. Wenn Sie zum Beispiel einen neuen Controller hinzufügen möchten, können Sie den Befehl mn add-controller MyController verwenden. Dies gibt einen neuen Controller und die Tests für ihn aus, wie in Listing 7 gezeigt.
Listing 7. Erstellen eines neuen Controllers mit der Micronaut-Kommandozeile
mn create-controller MyController
| Rendered controller to src/main/java/micronaut/idg/MyControllerController.java
| Rendered test to src/test/java/micronaut/idg/MyControllerControllerTest.java
Cloud-native Entwicklung mit Micronaut
Ich habe bereits erwähnt, dass Micronaut von Grund auf für Cloud-native Microservices und serverlose Entwicklung entwickelt wurde. Ein Cloud-natives Konzept, das Micronaut unterstützt, ist die Föderation. Die Idee einer Föderation besteht darin, dass mehrere kleinere Anwendungen dieselben Einstellungen teilen und im Tandem bereitgestellt werden können. Wenn sich das sehr nach einer Microservices-Architektur anhört, haben Sie recht. Ziel ist es, die Entwicklung von Microservices zu vereinfachen und überschaubar zu halten. In der Micronaut-Dokumentation erfahren Sie mehr über föderierte Dienste.
Micronaut macht es auch einfach, Cloud-Umgebungen für die Bereitstellung zu nutzen. So können Sie beispielsweise die Docker-Registry von Google Cloud Platform anvisieren, wie in Listing 8 gezeigt.
Listing 8. Bereitstellen einer Micronaut-Anwendung mithilfe der Docker-Registry von GCP
./mvnw deploy \
-Dpackaging=docker \
-Djib.to.image=gcr.io/my-org/my-project:latest
In diesem Fall würde das Projekt als Docker-Image in die GCP-Docker-Registry geladen werden. Beachten Sie, dass wir das Jib Maven-Plugin verwendet haben, das ein Java-Projekt in ein Docker-Image verwandelt, ohne dass Sie eine eigentliche Docker-Datei erstellen müssen.
Beachten Sie auch, dass wir Docker als Paketierungswerkzeug mit -Dpackaging=docker angegeben haben. Sobald die Paketierung abgeschlossen ist, können Sie Ihr Projekt mit dem GCP-Befehlszeilentool bereitstellen, wie in Listing 9 gezeigt.
Listing 9. Ausführen des Docker-Images über die Kommandozeile
gcloud run deploy \
--image=gcr.io/my-org/my-project:latest \
--platform managed \
--allow-unauthenticated
Tracing ist eine weitere Cloud-native Funktion, die Micronaut unterstützt. Micronaut macht es zum Beispiel ziemlich einfach, das verteilte Tracing von Jaeger über Annotationen zu aktivieren.
In Listing 10 wird Jaeger so konfiguriert, dass alle Anfragen in der application.xml-Datei einer Microservices-Anwendung verfolgt werden.
Listing 10. Jaeger-Konfiguration in application.xml
tracing:
jaeger:
enabled: true
sampler:
probability: 1
Fazit
Micronaut bietet eine Reihe von Funktionen, die sich hervorragend für die Cloud-Native- und Microservice-Entwicklung eignen. Gleichzeitig macht das Framework die eher traditionelle API-basierte Entwicklung unkompliziert und einfach. Und es lässt sich gut mit Reactor und Netty für reaktives NIO integrieren.
Micronaut kann neben Quarkus, Dropwizard und anderen Cloud-nativen Java-Frameworks eingesetzt werden. Es ist eine erfrischende Alternative zu bestehenden Lösungen.
*Matthew Tyson ist Mitbegründer der Dark Horse Group, Inc. Er glaubt an Technologie, bei der der Mensch an erster Stelle steht. Wenn er nicht gerade Gitarre spielt, erkundet Matt das Hinterland und die philosophischen Gefilde. Er schreibt seit 2007 für JavaWorld.
Be the first to comment