Lesezeit: 6 Minuten
 

AWS Lambda: Erste Schritte mit Java


Serverless-Angebote erfreuen sich zurzeit großer Beliebtheit. Amazon hat mit AWS ein breites Spektrum an Diensten, u.a. auch AWS Lambda. In diesem Artikel lernen wir, was die Vorteile bei der Verwendung von AWS Lambda sind und wie wir Java-Code in AWS-Lambda installieren und ausführen können. 

Wir verwenden bei der Implementierung Java 17 mit Gradle und Lombok. Den Beispielcode aus diesem Artikel haben wir in einem Git-Repository bereitgestellt. Weiterhin benötigen wir für die Installation und Ausführung einen AWS-Account
 

Warum AWS Lambda? 

Cloud Computing bietet mit IaaS/CaaS/PaaS/FaaS/SaaS ein breites Spektrum an Diensten. Allen gemeinsam ist die höhere Effizienz und Optimierung durch Auslagern der Verantwortlichkeiten für Elastizität, Skalierbarkeit, Sicherheit, Disaster Recovery und vieles mehr. 

Eine weit verbreitete Nutzung besteht in der Implementierung von Microservices-Architekturen mit CaaS bzw. PaaS. Anwendungen werden dabei üblicherweise fachlich geschnitten (Domain Driven Design) und mithilfe von Container-Technologien in der Cloud betrieben. Die Verwendung von Containern ermöglicht eine automatisierte Skalierung, die Kommunikation mit standardisierten Protokollen außerdem auch Flexibilität bei der Auswahl der Technologien für die Implementierung. Jedoch laufen diese Container dauerhaft und erzeugen damit je nach Modell laufende Kosten unabhängig von deren Nutzung. Außerdem verbrauchen Cloud-Systeme mit laufenden Containern mehr Strom, was dem derzeitig aufkommenden Trend zur Minimierung des CO2-Fußabdrucks (Green IT) entgegensteht. 

Hier kommt AWS Lambda ins Spiel. Java-Entwickler kennen seit Java 8 bereits die gleichnamigen Lambda Expressions. Und die Namensgleichheit kommt nicht von ungefähr – stehen beide Technologien für eine Ausführung On-Demand. Jedoch betrachten wir bei AWS Lambda die Ausführung von Containern, die dann im Bauch wiederum beliebige Technologien enthalten können. Um dies zu ermöglichen, ist es wichtig, eine standardisierte Aufrufschnittstelle zu definieren und die Startzeiten für Container so performant wie möglich zu gestalten. 

AWS Lambda ist außerdem Teil einer Serverless-Familie. Zwar erlaubt es durchaus, eigens konfigurierte Container zu deployen, es bietet aber bereits vordefinierte Laufzeitumgebungen an, aktuell für Java, C#, node.js, Python, Ruby und Go. Damit entfällt das Server-Management, was die Entwicklung effizienter gestaltet und die Anwendungen von Natur aus skalierbar macht. Außerdem sind die Kosten für die Nutzung solcher Angebote im Sinne von „Pay-as-you-go“ geringer als sonst. 
 

Was kann mit AWS Lambda ausgeführt werden?

Wie so oft bietet AWS Lambda ein Werkzeug an, das unterschiedlich genutzt werden kann. Die Bandbreite ergibt sich hierbei durch die Einbettung in die Landschaft an Cloud-Diensten. So kann eine Lambda-Funktion beispielsweise im Rahmen eines Prozess-Flows mit AWS Step Functions aufgerufen werden. Solche Funktionen erledigen dann kleine, abgeschlossene Aufgaben, z.B. den Aufruf eines REST-Service oder einen kleinen Teil an Business-Logik. Bei Einbindung hinter einem API-Gateway ist es allerdings durchaus möglich, komplette Microservices als Lambda-Funktion zu betreiben. Somit können bestehende Anwendungen leicht auf On-Demand-Betrieb migriert werden (Lift and Shift). Spring Boot und Quarkus haben für diesen Anwendungsfall bereits Integrationen. Mittel- und langfristig ist es jedoch empfehlenswert, Lambdas kleinteiliger auf einzelne Funktionen zu schneiden. 

Wie funktioniert ein Aufruf mit AWS Lambda?

JSON

AWS arbeitet mit sogenannten Event-Objekten, die in Form von JSON bei der Kommunikation zwischen den Bausteinen der Gesamtarchitektur übertragen werden. So auch bei Aufruf eines Lambdas – es wird ein JSON-Objekt übergeben und es wird mit einem JSON-Objekt geantwortet. Die Bearbeitung übernimmt dann ein RequestHandler, den wir mit der jeweiligen Logik implementieren. 

Während beim Aufruf über Step Functions das JSON-Objekt beliebig definiert werden kann, erhalten wir vom API-Gateway ein vordefiniertes Schema. Ein Beispiel dafür finden wir in der AWS-Dokumentation

Kalt- vs. Warmstart 

Lambdas haben einen Lebenszyklus. Bei der ersten Anfrage wird der Code geladen (aus dem eigenen Speicher bzw. üblicherweise aus einem S3-Bucket) und die Laufzeitumgebung generiert (Kaltstart). Danach erfolgt dann die Ausführung des Codes. Nach der Ausführung wird die Laufzeitumgebung behalten, sodass bei erneuter Anfrage lediglich der Code ausgeführt werden muss (Warmstart). Erfolgt längere Zeit keine Anfrage, wird die Laufzeitumgebung freigegeben. Bei erneuter Anfrage erfolgt dann wieder ein Kaltstart. Details dazu finden wir in einem AWS-eigenen Blogeintrag

Für die Performance eines Lambdas ist also vor allem die Zeit zur Initialisierung entscheidend. Das gilt einerseits für den Container beim Kaltstart, als auch bei jeder Anfrage für unseren Code. Für die Optimierung des Codes sollten wir vor allem auf minimale Initialisierungen achten, z.B. Lazy Initialization konfigurieren oder unnötige Dependencies vermeiden. Native Builds mit GraalVM verlagern Initialisierungsvorgänge wie Classpath-Scanning, Reflection, Dependency-Injection-Analysen und JIT-Compiling nach vorn in die Bauzeit. In letzter Instanz kann AWS Lambda bereits initialisierte Anwendungen „warm“ halten, was jedoch zusätzliche Kosten verursacht (Provisioned Concurrency). 
 

Wie implementiere ich eine Funktion für AWS Lambda?

Grundsätzlich können wir Lambdas mit beliebigen Programmiersprachen oder als Shell-Skript implementieren. Für Java, C#, node.js, Python, Ruby und Go gibt es bereits fertige, optimierte Laufzeitumgebungen. Wir implementieren unser Beispiel mit Java und verzichten zur Vereinfachung bewusst auf Frameworks wie Spring oder Quarkus. 

Beispiel 

Als fachliches Beispiel implementieren wir eine Funktion zur Reservierung von Räumen für jeweils einen bestimmten Tag. Hierfür implementieren wir im Kern einen RoomReservationService mit folgender Aufrufschnittstelle:

Einstiegspunkt 

Für die Aufrufschnittstelle kann jede öffentliche Funktion einer beliebigen Klasse definiert werden. Üblich ist jedoch die Implementierung eines in AWS bereitgestellten RequestHandler-Interfaces. Dieses wird vom AWS Lambda SDK bereitgestellt, das wir als Dependency ebenso wie Lombok in unserem Projekt einbinden:

Input

Für die Definition der Aufrufschnittstelle benötigen wir nun einen Datentyp, der von AWS Lambda automatisch auf das Event-Objekt gemappt wird. Der Aufruf für die Raumreservierung soll mit diesem Objekt erfolgen:

Der zugehörige Datentyp sieht dann in etwa so aus:

Output

Und auch für die Antwort des Lambdas benötigen wir einen Datentyp:

Damit antwortet unser Lambda im Erfolgsfall mit diesem JSON:

Handler-Implementierung

Die Implementierung des Handlers verbindet dann alle Komponenten miteinander:

JSON-Serialisierung mit Standard-Frameworks

Das Mapping der Objekte auf JSON erfolgt mit einer Eigenimplementierung von AWS. Für eine bessere, standardisierte Konfiguration kann es sinnvoll sein, auf Standard-Frameworks wie Jackson, GSON oder JSON-B zurückzugreifen. Für größer geschnittene Lambdas kann es außerdem erforderlich sein, flexibel auf unterschiedliche Eingangs-JSON reagieren zu können.

Wir möchten in unserem Beispiel auf Jackson umbauen. Dafür binden wir die entsprechenden Dependencies ein:

Nun implementieren wir statt dem Request Handler einen RequestStreamHandler, der anstelle von fertigen Objekten mit In- und OutputStream arbeitet. In unserem Beispiel nutzen wir die o.g. Implementierung als Delegate. Damit trennen wir auch automatisch Verantwortlichkeiten:

Jetzt haben wir die Möglichkeit, die Jackson-eigenen Konfigurationsmöglichkeiten zu nutzen. Unser Datentyp für das Event-Objekt könnte nun auch so ausschauen:

Logging

AWS Lambda Logging in AWS CloudWatch erfolgt automatisch bei Nutzung der JRE-eigenen System-Klasse oder aber bei Nutzung des über den Context-Parameter mitgelieferten Loggers, der allerdings keine Log-Levels unterscheiden kann:

Eine Integration von Log4j2 kann sinnvoll sein – z.B. um Ausgaben von Dependencies auf Basis von Log4j2 ebenfalls zu erhalten. Hierfür nehmen wir die entsprechende Dependency auf:

Wir konfigurieren Log4j2 wie gewohnt per log4j2.xml. Hierbei ist zu beachten, dass das AWS Lambda SDK einen eigenen Appender bereitstellt, der die AWS Request ID automatisch mitloggen kann, was für Tracing notwendig ist:

Nun können wir in unseren Anwendungsklassen Log4j2-Logger verwenden:

AWS-spezifische Events

Wie bereits erwähnt, können Lambdas auch von anderen Komponenten der AWS-Infrastruktur mit standardisierten Event-Objekten aufgerufen werden. In diesem Fall wäre es mühsam, die entsprechenden Java-Datentypen selbst zu erstellen und zu verwalten. Selbstverständlich bietet AWS diese Datentypen bereits in einer Bibliothek an. Wir erweitern hierfür unser Projekt lediglich um eine weitere Dependency:

Für einen Aufruf vom API-Gateway z.B. können wir den Handler nun so implementieren:

 

Wie installiere und starte ich die Lambda-Funktion?


Build

Neben dem klassischen JAR erlaubt AWS auch die Bereitstellung einer ZIP-Datei. Dies ermöglicht das Verpacken mitsamt der Abhängigkeiten, ohne sog. Shaded JARs bzw. Uber JARs erzeugen zu müssen. Hierzu erweitern wir unsere Gradle-Konfiguration wie folgt:

Beim Bauen wird damit ein AWS-Lambda-konformes ZIP erstellt, das alle Abhängigkeiten enthält:

Layers

Mithilfe von Layers können Abhängigkeiten unserer Lambda-Funktion separat eingestellt werden. Dies kann gerade bei externen Abhängigkeiten sinnvoll sein, um die Größe der Deployment-Pakete zu minimieren – aber auch bei einer Trennung von Lambda unabhängiger Logik und der Aufrufschnittstelle. So ist es auch möglich, eine Business-Logik zu entwickeln und zentral zu deployen, die dann mit mehreren RequestHandler-Implementierungen und damit in mehreren Lambda-Funktionen ansprechbar ist.

Deployment

Die ZIP-Datei können wir nun in AWS installieren und ausführen. Dazu haben wir mehrere Möglichkeiten.

AWS Management Console

Über die AWS Management Console erhalten wir eine Browser UI zur Verwaltung der Lambdas. Dort können wir manuell Lambdas erstellen, Anwendungscode hochladen oder direkt editieren sowie Einstellungen dazu vornehmen. Wir erstellen zuerst eine neue Funktion:

Hier können wir der Funktion einen Namen geben (z.B. room-reservation) sowie einen der vorkonfigurierten Container auswählen (hier. Java 17). Bei Bedarf können auch Berechtigungen angepasst und Layer deklariert bzw. eingebunden werden.

Beim Bearbeiten der Funktion laden wir die gebaute ZIP-Datei hoch und geben den Namen des Handlers (Klassenname::Methodenname) an:

Fertig! Über den Reiter „Test“ können wir die Funktion nun probeweise ausführen. Hierzu spezifizieren wir lediglich das Event-Objekt:

Wir erhalten nun das Ergebnis:

Kommandozeile

Die AWS CLI bietet auch für Lambdas entsprechende Funktionen an. Für eine CI/CD-Pipeline kann beispielsweise die Aktualisierung des Codes interessant sein. Nach dem Gradle-Build kann dies mit dem Befehl update-function-code geschehen:

Als Funktionsname kann alternativ auch die in AWS zugewiesene ARN verwendet werden. Statt dem ZIP-File kann auch ein S3-Bucket angegeben werden.

Zu beachten ist, dass für diese Operation und den in der CLI verwendeten Benutzeraccount eine Berechtigung vergeben werden muss. Hierfür müssen wir in den Details zur Lambda-Funktion im Reiter „Konfiguration“ auf „Berechtigungen“ gehen und eine ressourcenbasierte Richtlinienanweisung vergeben:

Ausführen lässt sich die Funktion ebenfalls über die CLI mit entsprechender lambda:InvokeFunction-Berechtigung:

Serverless Application Model (SAM)

SAM ist ein Toolkit für ein effizienteres Erstellen und Ausführen von Serverless-Anwendungen. Es ermöglicht, die Infrastruktur als YML-Datei zu deklarieren, ein entsprechendes Paket zu bauen und als S3-Bucket hochzuladen. Damit befassen wir uns in einem späteren Artikel.

Testen

Wir haben bereits gelernt, die Lambda-Funktion in der AWS Management Console sowie über die Kommandozeile zu starten. Für die Entwicklung sind aber auch weitere Unterstützungen sinnvoll.

Integration in Entwicklungsumgebungen

Für die gängigen Entwicklungsumgebungen gibt es das AWS Toolkit (z.B. IntelliJ, VS Code). Damit lassen sich u.a. Lambda-Funktionen ausführen – sowohl in AWS (remote) als auch lokal. Letzteres benötigt eine Docker Runtime sowie eine installierte SAM CLI.

Nach der Ausführung ist ein entsprechendes lokales Docker-Image vorhanden:


Automatisierte Tests

In der AWS Lambda Dokumentation werden die unterschiedlichen Testmöglichkeiten erwähnt:

  • Modultests mit Mocks – unser Code sollte so strukturiert sein, dass wir unabhängig von einer Lambda-Umgebung testen können.
  • Integrationstests – Deployment in AWS Lambda und Ausführen etwa mit der CLI wie beschrieben.
  • Lokale Emulation – z.B. mit AWS SAM local.

Letztlich ist die lokale Emulation nur eingeschränkt möglich (z.B. wenn weitere AWS-Ressourcen benötigt werden), sodass ein gutes Testkonzept auf Modul- und Integrationstests aufbaut. Spezielle Dependencies braucht es dafür nicht unbedingt. Eine kleine AWS-Lambda-eigene JUnit-basierte Testbibliothek für das Laden von Events aus JSON-Testdaten gibt es dennoch.
 

Fazit

AWS Lambda lässt dem Entwickler keine Wünsche offen. Die API ist minimal-invasiv – es ließen sich sogar komplett Lambda-unabhängige JARs deployen, was auch die Migration bestehender Anwendungen vereinfachen kann. Mit Browser-UI und CLI sowie Integrationen in Entwicklungsumgebungen bleiben keine Wünsche offen. Lediglich die JSON-basierte Aufrufschnittstelle und das bevorzugte kleinere Schneiden bedeutet ein wenig Umdenken. Allerdings lässt sich über die Layer auch hier eine entsprechende Abstraktion erreichen.

Blogautor

Gergely Papp
Softwareentwickler ARS Computer und Consulting GmbH
Ihr Erfolg ist unser Ziel

Stehen Sie vor komplexen IT-Projekten? Mit unserer Expertise bieten wir Ihnen maßgeschneiderte Lösungen. Erfahren Sie mehr.

Werde Teil unseres Teams

Wir suchen ständig nach neuen Talenten. Für dich haben wir genau die richtige Stelle. Schau dir unsere offenen Positionen an.

Noch Fragen? Wir helfen Ihnen gerne!

Blog 01.08.24

Migration von HOST-Anwendungen zu AWS: Modernisierung

Legacy-Systeme bieten oft wertvolle Funktionen für Unternehmen. Dieser Artikel beschreibt, wie HOST-Anwendungen in die Cloud migriert werden können, ohne dass die Datensicherheit leidet. Lernen Sie, wie moderne AWS-Services nahtlos in bestehende Host-Landschaften integriert werden und profitieren Sie von den Vorteilen von Serverless-Technologien.

Blog 05.10.23

AWS Lambda vs. AWS Fargate

Entdecken Sie die Unterschiede zwischen Serverless-Technologie und Container-Technologie und erfahren Sie, wie AWS Lambda und AWS Fargate von Amazon Web Services diese Ansätze unterstützen. Erfahren Sie, welche Technologie am besten zu Ihren Anforderungen für Anwendungsbereitstellung und -verwaltung passt.

Referenz

Digitale Transformation: HUK ersetzt Papier durch Cloud

ARS unterstützte HUK-COBURG dabei, Papierprozesse durch moderne Cloud-Microsites zu ersetzen, um fehlende Versicherungsdaten effizient zu erfassen.

Blog 15.02.24

DynamoDB starten: Einblicke in AWS Key-Value Datenbank 1

Erfahren Sie alles über die Grundlagen von DynamoDB, die Kernkomponenten, Schlüsselkonzepte und erfahren Sie, wann der Einsatz von DynamoDB sinnvoll ist.

Wüste der Integration
Wissen

Auf Kamelen durch die Wüste der Integration

Im Rahmen eines Kundenprojektes sollte die Anbindung eines RESTful Webservices an eine DB2 Datenbank realisiert werden. Open Source Integrationsframework Apache Camel lieferte die Lösung. Der Blogartikel geht ins Detail.

Blog 22.09.23

Optimierung von Serverless Funktionen

Entdecken Sie die Unterschiede zwischen Serverless-Technologie und Container-Technologie und erfahren Sie, wie AWS Lambda und AWS Fargate von Amazon Web Services diese Ansätze unterstützen. Erfahren Sie, welche Technologie am besten zu Ihren Anforderungen für Anwendungsbereitstellung und -verwaltung passt.

Wüste der Integration
Wissen

Auf Kamelen durch die Wüste der Integration - Teil 2

Für Integrationsprojekte bietet Open Source Framework Apache Camel einige Lösungen. In diesem fortsetzenden Beitrag wird WebSphere Message Broker als alternatives Produkt beleuchtet.

Event Archive 25.02.25

Enterprise Observability bei Trusted Shops

Trusted Shops ist auf die Zertifizierung von Online-Shops spezialisiert. In unserem Webinar berichtet das Unternehmen live von dem Mehrwert, den die Software Lösung IBM Instana ihm bringt.

Wissen

Cast Iron Express – Ein erster Einblick

Software-as-a-Service Lösungen (SaaS) können einfach, bedarfsgerecht und ohne Betriebsaufwand genutzt werden. Ist es möglich die Integration von SaaS genau so einfach, bedarfsgerecht und ohne Betriebsaufwand zu realisieren? Mehr erfahren Sie in diesem Artikel!

Wissen

Individuelle Java-Anpassungen in Optimierungsprojekten

Die Oberflächenanpassung im IBM-Optimierungstool ILOG DOC (ehemals ILOG ODM Enterprise) mit Hilfe von Java-Code ergibt ein Optimierungsmodell, das schnell für gute Ergebnisse sorgt. Dieser Blogartikel beleuchtet einige Kniffe, die dies möglich machen.

Arbeit am Laptop
Wissen

Qualität der Messagingsysteme

Welche Funktionalitäten bieten Messagesysteme? Wo liegen die Unterschiede zwischen frei verfügbaren und kommerziellen Messagingsystemen? Lesen Sie in diesem Artikel mehr.

Blog 19.08.22

Shopware auf der Überholspur

Lernen Sie in diesem Artikel, wie Ihre Daten und die Ihrer Kunden sicher mit einer internen Web Application Firewall (WAF) geschützt werden können und warum dies sinnvoll ist. Wir zeigen Ihnen Codeauszüge innerhalb einer kurzen Anleitung mit Tipps und Tricks.

Blog 16.05.24

In 8 Schritten zu AI-Innovationen im Unternehmen

Künstliche Intelligenz ist längst mehr als ein Schlagwort – sie schafft echten Business Value. Mit unserem achtstufigen Ansatz unterstützen wir Unternehmen auf dem Weg zur erfolgreichen AI-Nutzung.

Kompetenz

Mehr Transparenz mit Oracle Java-Report

Wir bieten mit unserem Partner Raynet ein kontinuierliches und automatisiertes Monitoring von Oracle Java-Applikationen. Wir helfen Ihnen nicht nur beim Audit, sondern sorgen auch für eine korrekte Lizenzierung. Sparen Sie so deutliche Kosten bei der Nutzung Ihrer Oracle Java-Lizenzen!

Person arbeitet am Computer
Wissen

What's new in Message Broker V8?

Liegt der Fokus beim Message Broker V8 auf neuen Features oder die Erweiterung bestehender Features? Bringen diese für Message Broker Entwickler und Administratoren Produktivitätsgewinne mit sich, die eine Migration auf V8 rechtfertigen? Dieser Blogartikel gibt Antwort darauf.

Partner

AWS – Amazon Web Services

AWS – Amazon Web Services

Blog 20.05.22

In 6 Schritten zur passenden Data Analytics-Lösung

Um Innovation im Unternehmen voranzutreiben, braucht es eine state-of-the-art Data Analytics-Lösung. Oftmals ist hier eine Modernisierung von Nöten. Erfahren Sie in sechs Schritten, wie Sie die für Sie passende Lösung finden!

Blog 10.10.24

DevOps? Warum APIOps der nächste logische Schritt ist

APIOps erweitert DevOps-Praktiken auf APIs, um deren Entwicklung zu automatisieren und zu optimieren. Dieser Ansatz verbessert Qualität, Sicherheit und Geschwindigkeit im API-Management.

Technologie Übersicht

Beratung und Lösungen rund um AWS Services

Als führender AWS Select Partner stehen wir Ihnen mit Rat und Tat zur Seite.

Puzzleteil zur Visualisierung von Integration
Wissen

IBM Integration Bus 9 – Der neue Message Broker

Auf der Impact 2013 in Las Vegas wurde Version 9 des bewährten WebSphere Message Brokers (WMB) unter dem Namen IBM Integration Bus (IIB) vorgestellt. Der Grund für die Umbenennung ist sicher die neue ESB-Strategie der IBM. Der Blogartikel liefert Einzelheiten.

Bleiben Sie mit dem TIMETOACT GROUP Newsletter auf dem Laufenden!