In diesem Artikel sehen wir uns eine Möglichkeit an, ein Java AWS Lambda mit dem noch recht neuen Microframework Micronaut zu schreiben und unseren Code mit Hilfe von Serverless in die Produktion zu bringen.
Wenn du das liest, bist du wahrscheinlich bereits mit AWS Lambda vertraut, einem AWS-Produkt, das es Entwicklern ermöglicht, kleine Anwendungen auszuführen, ohne sich um die Infrastruktur kümmern zu müssen, die sie stützt. Man muss nur ein Stück Code zu AWS hochladen und das Programm wird aufgerufen. Dann kümmert sich AWS darum, herauszufinden, wo und wie es ausgeführt werden soll (bei Bedarf).
In diesem Artikel sehen wir uns eine Möglichkeit an, ein Java-AWS-Lambda mit dem noch recht neuen Mikro-Framework Micronaut zu schreiben, und wir werden unseren Code mit Hilfe von Serverless in die Produktion überführen.
In diesem Beispiel möchten wir einen AWS Lambda schreiben, der einen REST-Endpunkt namens /capital/{country} implementiert. Dieser Endpunkt nutzt die REST-Länder-API und antwortet uns mit der Hauptstadt eines bestimmten Landes. Schließlich verwenden wir ein AWS API Gateway, um unser Lambda wie folgt aufzurufen:
{
"capital”: “<capital>”
}
curl <api-gateway-url>/capital/{country}
Wir bauen eine klassische Klassenstruktur auf, die mit einem Controller beginnt, der einen Service aufruft, und dieser Service ruft dann einen Client auf:
Eine Controller-Klasse ist immer mit @Controller annotiert und hat einen Pfad als Parameter. In ähnlicher Weise ist unser öffentlicher Endpunkt mit @Get annotiert:
@Controller("/")
public class CapitalController {
@Inject
public RestCountriesService restCountriesService;
@Get("/capital/{country}")
public String getCapital(@PathVariable @NotBlank String country) {
return "{\"capital\": \"" +
restCountriesService.getCapital(country) + "\"}";
}
}
As you can see, this controller injects a service, which is typically annotated with @Prototype:
@Prototype
public class RestCountriesService {
@Inject
public RestCountriesClient restCountriesClient;
public String getCapital(String country) {
returnOptional.ofNullable
(restCountriesClient.fetchCountry(country))
.flatMap(restCountriesResponses ->
restCountriesResponses.stream().findFirst())
.map(RestCountriesResponse::getCapital)
.orElseThrow(() -> new HttpStatusException(NOT_FOUND,
"Couldn't find country " + country));
}
}
@Prototype weist Micronaut an, an jedem Injektionspunkt eine neue Instanz der annotierten Klasse zu erstellen. Dies ist äquivalent zu @Component in Spring Boot.
Im einen HTTP-Aufruf auszuführen, bietet Micronaut eine Funktion namens "deklarativer Client", die aus einer Schnittstelle besteht, die einen bestimmten Endpunkt aufruft und die Antwort mit Hilfe von Jackson auf ein DTO abbildet:
@Client("${restCountries.apiUrl}")
interface RestCountriesClient {
@Get("/name/{country}")
List<RestCountriesResponse> fetchCountry(@PathVariable String
country);
}
Diese Schnittstelle muss mit @Client und der Basis-URL des Endpunkts, den wir aufrufen wollen, annotiert werden. In diesem Fall haben wir diese URL in einer application.yml-Datei definiert, so wie man es in Spring Boot tun würde:
restCountries:
apiUrl: https://restcountries.eu/rest/v2
Dann muss für jeden Endpunkt eine Methode definiert werden, die mit der gewünschten HTTP-Methode und optional einem Pfad versehen ist, der an die zuvor in @Client definierte Basis-URL angehängt wird. Zusätzliche Parameter wie Header oder Abfrageparameter müssen der Methode übergeben werden.
Die vollständige Liste der möglichen Parameter findest du hier: https://docs.micronaut.io/latest/guide/index.html#binding
Nach dem Aufruf von fetchCountry() wird die Klasse RestCountriesResponse bereits mit den Werten aus dem Antwortkörper der Anfrage initialisiert.
Da wir unseren Dienst mit Serverless bereitstellen wollen, müssen wir ihn zunächst mit npm installieren:
npm install
Dann stellen wir den Service mit diesem Skript bereit: deploy.sh
Damit wird der Gradle-Build ausgeführt und der Dienst in AWS bereitgestellt. Als Ergebnis können wir unseren Endpunkt von überall im Internet aufrufen.
Bei der Arbeit mit Micronaut haben wir festgestellt, dass es einfach zu erlernen ist, insbesondere wenn man bereits Erfahrung mit anderen Frameworks wie Spring Boot hat, und dass es gut dokumentiert ist, mit vielen Beispielen und Anleitungen.
Obwohl wir in diesem Artikel nicht darüber gesprochen haben, haben wir auch GraalVM verwendet, eine vielversprechende Lösung, um Lambda Kaltstarts zu reduzieren.Es handelt sich jedoch um eine recht komplexe Lösung, die sicherlich nicht einfach einzurichten ist, insbesondere wenn Bibliotheken von Drittanbietern benötigt werden.
Die gesamte Anwendung mitsamt Tests ist auf GitHub unter dem unten stehenden Link verfügbar.
We have received your feedback.