Definieren Sie den Lambda-Funktionshandler in Java - AWS Lambda

Die vorliegende Übersetzung wurde maschinell erstellt. Im Falle eines Konflikts oder eines Widerspruchs zwischen dieser übersetzten Fassung und der englischen Fassung (einschließlich infolge von Verzögerungen bei der Übersetzung) ist die englische Fassung maßgeblich.

Definieren Sie den Lambda-Funktionshandler in Java

Der Lambda-Funktionshandler ist die Methode in Ihrem Funktionscode, die Ereignisse verarbeitet. Wenn Ihre Funktion aufgerufen wird, führt Lambda die Handler-Methode aus. Ihre Funktion wird so lange ausgeführt, bis der Handler eine Antwort zurückgibt, beendet wird oder ein Timeout auftritt.

Das GitHub Repo für dieses Handbuch enthält easy-to-deploy Beispielanwendungen, die eine Vielzahl von Handlertypen demonstrieren. Weitere Informationen finden Sie am Ende dieses Themas.

Beispiel-Handler: Java-17-Laufzeiten

Im folgenden Java-17-Beispiel definiert eine Klasse mit dem Namen HandlerIntegerJava17 eine Handler-Methode mit dem Namen handleRequest. Die Handler-Methode nimmt die folgenden Eingaben auf:

  • Einen IntegerRecord, wobei es sich um einen benutzerdefinierten Java-Datensatz handelt, der Ereignisdaten darstellt. In diesem Beispiel definieren wir IntegerRecord wie folgt:

    record IntegerRecord(int x, int y, String message) { }
  • Ein Kontextobjekt stellt Methoden und Eigenschaften mit Informationen zum Aufruf, zur Funktion und zur Ausführungsumgebung bereit.

Angenommen, wir möchten eine Funktion schreiben, die message aus der Eingabe IntegerRecord protokolliert und die Summe von x und y zurückgibt. Das Folgende ist der Funktionscode:

Beispiel HandlerIntegerJava17.java
package example; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.LambdaLogger; import com.amazonaws.services.lambda.runtime.RequestHandler; // Handler value: example.HandlerInteger public class HandlerIntegerJava17 implements RequestHandler<IntegerRecord, Integer>{ @Override /* * Takes in an InputRecord, which contains two integers and a String. * Logs the String, then returns the sum of the two Integers. */ public Integer handleRequest(IntegerRecord event, Context context) { LambdaLogger logger = context.getLogger(); logger.log("String found: " + event.message()); return event.x() + event.y(); } } record IntegerRecord(int x, int y, String message) { }

Sie geben an, welche Methode Lambda aufrufen soll, indem Sie den Handler-Parameter für die Konfiguration Ihrer Funktion festlegen. Sie können den Hander in den folgenden Formaten ausdrücken:

  • package.Class::method – Vollständiges Format. Beispiel: example.Handler::handleRequest.

  • package.Class – Abgekürztes Format für Funktionen, die eine Handler-Schnittstelle implementieren. Beispiel: example.Handler.

Wenn Lambda Ihren Handler aufruft, empfängt die Lambda-Laufzeit ein Ereignis als JSON -formatierte Zeichenfolge und konvertiert es in ein Objekt. Für das vorherige Beispiel könnte ein Beispielereignis wie folgt aussehen:

Beispiel event.json
{ "x": 1, "y": 20, "message": "Hello World!" }

Sie können diese Datei speichern und Ihre Funktion lokal mit dem folgenden Befehl () testen: AWS Command Line Interface CLI

aws lambda invoke --function-name function_name --payload file://event.json out.json

Beispiel-Handler: Java-11-Laufzeiten und darunter

Lambda unterstützt Datensätze in der Laufzeit Java 17 und höher In allen Java-Laufzeiten können Sie eine Klasse verwenden, um Ereignisdaten darzustellen. Das folgende Beispiel verwendet eine Liste von Ganzzahlen und ein Kontextobjekt als Eingabe und gibt die Summe aller Ganzzahlen in der Liste zurück.

Beispiel handler.java

Im folgenden Beispiel definiert eine Klasse mit dem Namen Handler eine Handler-Methode mit dem Namen handleRequest. Die Handler-Methode nimmt ein Ereignis- und ein Kontextobjekt als Eingabe an und gibt eine Zeichenfolge zurück.

Beispiel HandlerList.java
package example; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.LambdaLogger; import com.amazonaws.services.lambda.runtime.RequestHandler; import java.util.List; // Handler value: example.HandlerList public class HandlerList implements RequestHandler<List<Integer>, Integer>{ @Override /* * Takes a list of Integers and returns its sum. */ public Integer handleRequest(List<Integer> event, Context context) { LambdaLogger logger = context.getLogger(); logger.log("EVENT TYPE: " + event.getClass().toString()); return event.stream().mapToInt(Integer::intValue).sum(); } }

Weitere Beispiele finden Sie unter Beispiel-Handler-Code.

Initialisierungscode

Lambda führt Ihren statischen Code und den Klassenkonstruktor während der Initialisierungsphase aus, bevor Ihre Funktion zum ersten Mal aufgerufen wird. Ressourcen, die während der Initialisierung erstellt werden, bleiben zwischen Aufrufen im Speicher und können vom Handler tausende Male wiederverwendet werden. Daher können Sie Initialisierungscode außerhalb Ihrer Haupt-Handler-Methode hinzufügen, um Rechenzeit zu sparen und Ressourcen für mehrere Aufrufe wiederzuverwenden.

Im folgenden Beispiel befindet sich der Client-Initialisierungscode außerhalb der Haupt-Handler-Methode. Die Laufzeit initialisiert den Client, bevor die Funktion ihr erstes Ereignis ausführt. Nachfolgende Ereignisse sind viel schneller, da Lambda den Client nicht erneut initialisieren muss.

Beispiel handler.java
package example; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.LambdaLogger; import com.amazonaws.services.lambda.runtime.RequestHandler; import java.util.Map; import software.amazon.awssdk.services.lambda.LambdaClient; import software.amazon.awssdk.services.lambda.model.GetAccountSettingsResponse; import software.amazon.awssdk.services.lambda.model.LambdaException; // Handler value: example.Handler public class Handler implements RequestHandler<Map<String,String>, String> { private static final LambdaClient lambdaClient = LambdaClient.builder().build(); @Override public String handleRequest(Map<String,String> event, Context context) { LambdaLogger logger = context.getLogger(); logger.log("Handler invoked"); GetAccountSettingsResponse response = null; try { response = lambdaClient.getAccountSettings(); } catch(LambdaException e) { logger.log(e.getMessage()); } return response != null ? "Total code size for your account is " + response.accountLimit().totalCodeSize() + " bytes" : "Error"; } }

Auswählen von Ein- und Ausgabetypen

In der Signatur der Handler-Methode geben Sie den Objekttyp an, dem das Ereignis zugeordnet wird. Im vorherigen Beispiel deserialisiert die Java-Laufzeit das Ereignis in einen Typ, der die Schnittstelle implementiert. Map<String,String> String-to-stringMaps funktionieren für pauschale Ereignisse wie die folgenden:

Beispiel Event.json – Wetterdaten
{ "temperatureK": 281, "windKmh": -3, "humidityPct": 0.55, "pressureHPa": 1020 }

Der Wert jedes Feldes muss jedoch eine Zeichenfolge oder eine Zahl sein. Wenn das Ereignis ein Feld mit einem Objekt als Wert enthält, kann es von der Laufzeit nicht deserialisiert werden und gibt einen Fehler zurück.

Wählen Sie einen Eingabetyp aus, der mit den Ereignisdaten arbeitet, die Ihre Funktion verarbeitet. Sie können einen Basistyp, einen generischen Typ oder einen gut definierten Typ verwenden.

Eingabetypen
  • Integer, Long, Double, usw. – Das Ereignis ist eine Zahl ohne zusätzliche Formatierung, zum Beispiel: 3.5. Die Laufzeit wandelt den Wert in ein Objekt des angegebenen Typs um.

  • String— Das Ereignis ist eine JSON Zeichenfolge, die Anführungszeichen enthält — zum Beispiel. "My string." Die Laufzeit wandelt den Wert (ohne Anführungszeichen) in ein String-Objekt um.

  • Type, Map<String,Type> usw. — Das Ereignis ist ein Objekt. JSON Die Laufzeitumgebung deserialisiert sie in ein Objekt des angegebenen Typs oder der angegebenen Schnittstelle.

  • List<Integer>, List<String>, List<Object>, usw. — Das Ereignis ist ein JSON Array. Die Laufzeitumgebung deserialisiert sie in ein Objekt des angegebenen Typs oder der angegebenen Schnittstelle.

  • InputStream— Das Ereignis hat einen beliebigen JSON Typ. Die Laufzeit übergibt einen Bytestream des Dokuments ohne Änderung an den Handler. Sie deserialisieren die Eingabe und schreiben Ausgabe in einen Ausgabestream.

  • Bibliothekstyp — Verwenden Sie für Ereignisse, die von AWS Diensten gesendet werden, die Typen in der aws-lambda-java-eventsBibliothek.

Wenn Sie Ihren eigenen Eingabetyp definieren, sollte es sich um ein deserialisierbares, veränderbares, einfaches Java-Objekt (POJO) mit einem Standardkonstruktor und Eigenschaften für jedes Feld im Ereignis handeln. Schlüssel für das Ereignis, die nicht einer Eigenschaft zugeordnet werden, sowie Eigenschaften, die nicht im Ereignis enthalten sind, werden fehlerfrei gelöscht.

Der Ausgabetyp kann ein Objekt oder sei void. Die Laufzeit serialisiert Rückgabewerte in Text. Wenn es sich bei der Ausgabe um ein Objekt mit Feldern handelt, serialisiert die Runtime es in ein Dokument. JSON Wenn es sich um einen Typ handelt, der einen primitiven Wert umschließt, gibt die Laufzeit eine Textdarstellung dieses Wertes zurück.

Handler-Schnittstellen

Die aws-lambda-java-coreBibliothek definiert zwei Schnittstellen für Handler-Methoden. Verwenden Sie die bereitgestellten Schnittstellen, um die Handler-Konfiguration zu vereinfachen und die Handler-Methodensignatur zur Kompilierzeit zu validieren.

Die RequestHandler-Schnittstelle ist ein generischer Typ, der zwei Parameter verwendet: den Eingabetyp und den Ausgabetyp. Beide Typen müssen Objekte sein. Wenn Sie diese Schnittstelle verwenden, deserialisiert die Java-Laufzeitumgebung das Ereignis in ein Objekt mit dem Eingabetyp und serialisiert die Ausgabe in Text. Verwenden Sie diese Schnittstelle, wenn die integrierte Serialisierung mit Ihren Ein- und Ausgabetypen funktioniert.

Beispiel Handler.java – Handler-Schnittstelle
// Handler value: example.Handler public class Handler implements RequestHandler<Map<String,String>, String>{ @Override public String handleRequest(Map<String,String> event, Context context)

Um Ihre eigene Serialisierung zu verwenden, implementieren Sie die RequestStreamHandler-Schnittstelle. Mit dieser Schnittstelle übergibt Lamda Ihrem Handler einen Eingabestream und einen Ausgabestream. Der Handler liest Bytes aus dem Eingabestream, schreibt in den Ausgabestream und gibt „void“ zurück.

Das folgende Java 21-Beispiel zeigt, wie Sie eine Lambda-Funktion verwenden können, um Bestellungen zu verarbeiten. Das Beispiel verwendet gepufferte Reader- und Writer-Typen, um mit den Eingabe- und Ausgabestreams zu arbeiten, und zeigt, wie Sie benutzerdefinierte Java-Datensätze für die Verwendung in Ihrer Funktion definieren können.

Beispiel HandlerStream.java
import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestStreamHandler; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.List; public class HandlerStream implements RequestStreamHandler { private static final ObjectMapper objectMapper = new ObjectMapper(); @Override public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException { Order order = objectMapper.readValue(input, Order.class); processOrder(order); OrderAccepted orderAccepted = new OrderAccepted(order.orderId); objectMapper.writeValue(output, orderAccepted); } private void processOrder(Order order) { // business logic } public record Order(@JsonProperty("orderId") String orderId, @JsonProperty("items") List<Item> items) { } public record Item(@JsonProperty("name") String name, @JsonProperty("quantity") Integer quantity) { } public record OrderAccepted(@JsonProperty("orderId") String orderId) { } }

Bewährte Codemethoden für Java-Lambda-Funktionen

Halten Sie sich an die Richtlinien in der folgenden Liste, um beim Erstellen Ihrer Lambda-Funktionen die besten Codierungspraktiken anzuwenden:

  • Trennen Sie den Lambda-Handler von Ihrer Core-Logik. Auf diese Weise können Sie eine Funktion zur besseren Prüfbarkeit von Einheiten schaffen.

  • Kontrollieren Sie die Abhängigkeiten im Bereitstellungspaket Ihrer Funktion. Die AWS Lambda Ausführungsumgebung enthält eine Reihe von Bibliotheken. Um die neuesten Funktionen und Sicherheitsupdates zu aktivieren, wird Lambda diese Bibliotheken regelmäßig aktualisieren. Diese Updates können das Verhalten Ihrer Lambda-Funktion geringfügig verändern. Um die Abhängigkeiten, die Ihre Funktion verwendet, vollständig zu kontrollieren, empfehlen wir, alle Abhängigkeiten mit Ihrem Bereitstellungspaket zu bündeln.

  • Minimieren Sie die Komplexität Ihrer Abhängigkeiten. Ziehen Sie einfachere Frameworks vor, die sich schnell beim Start der Ausführungsumgebung laden lassen. Verwenden Sie beispielsweise einfachere Java Dependency Injection (IoC) Frameworks wie z. B. Dagger or Guice, als komplexere wie Spring Framework.

  • Minimieren Sie die Größe Ihres Bereitstellungspakets auf die für die Laufzeit erforderliche Größe. Dadurch verkürzt sich die Zeit, die für das Herunterladen und Entpacken Ihres Bereitstellungspakets vor dem Aufruf benötigt wird. Vermeiden Sie es, bei Funktionen, die in Java verfasst wurden, die gesamte AWS SDK Bibliothek als Teil Ihres Bereitstellungspakets hochzuladen. Verlassen SDK Sie sich stattdessen selektiv auf die Module, die die benötigten Komponenten aufnehmen (z. B. DynamoDB, Amazon S3 SDK S3-Module und Lambda-Kernbibliotheken).

  • Nutzen Sie die Wiederverwendung der Ausführungsumgebung zur Verbesserung Ihrer Funktion. Initialisieren Sie SDK Clients und Datenbankverbindungen außerhalb des Funktionshandlers und speichern Sie statische Ressourcen lokal im Verzeichnis. /tmp Nachfolgende Aufrufe, die von derselben Instance Ihrer Funktion verarbeitet werden, können diese Ressourcen wiederverwenden. Dies spart Kosten durch Reduzierung der Funktionslaufzeit.

    Um potenzielle Datenlecks über Aufrufe hinweg zu vermeiden, verwenden Sie die Ausführungsumgebung nicht, um Benutzerdaten, Ereignisse oder andere Informationen mit Sicherheitsauswirkungen zu speichern. Wenn Ihre Funktion auf einem veränderbaren Zustand beruht, der nicht im Speicher innerhalb des Handlers gespeichert werden kann, sollten Sie für jeden Benutzer eine separate Funktion oder separate Versionen einer Funktion erstellen.

  • Verwenden Sie eine Keep-Alive-Direktive, um dauerhafte Verbindungen zu pflegen. Lambda bereinigt Leerlaufverbindungen im Laufe der Zeit. Der Versuch, eine Leerlaufverbindung beim Aufruf einer Funktion wiederzuverwenden, führt zu einem Verbindungsfehler. Um Ihre persistente Verbindung aufrechtzuerhalten, verwenden Sie die Keep-Alive-Direktive, die Ihrer Laufzeit zugeordnet ist. Ein Beispiel finden Sie unter Wiederverwenden von Verbindungen mit Keep-Alive in Node.js.

  • Verwenden Sie Umgebungsvariablen um Betriebsparameter an Ihre Funktion zu übergeben. Wenn Sie z. B. Daten in einen Amazon-S3-Bucket schreiben, anstatt den Bucket-Namen, in den Sie schreiben, hartzucodieren, konfigurieren Sie den Bucket-Namen als Umgebungsvariable.

  • Vermeiden Sie rekursive Aufrufe in Ihrer Lambda-Funktion, bei denen sich die Funktion selbst aufruft oder einen Prozess initiiert, der die Funktion möglicherweise erneut aufruft. Dies kann zu unvorhergesehenen Mengen an Funktionsaufrufen führen und höhere Kosten zur Folge haben. Wenn Sie eine unbeabsichtigte Anzahl von Aufrufen sehen, setzen Sie die für die Funktion reservierte Parallelität auf 0 sofort, um alle Aufrufe der Funktion zu drosseln, während Sie den Code aktualisieren.

  • Verwenden Sie APIs in Ihrem Lambda-Funktionscode nicht undokumentiert, nicht öffentlich. Für AWS Lambda verwaltete Laufzeiten führt Lambda regelmäßig Sicherheits- und Funktionsupdates für interne Lambda-Laufzeiten durch. APIs Diese internen API Updates können abwärtsinkompatibel sein, was zu unbeabsichtigten Folgen wie Aufruffehlern führen kann, wenn Ihre Funktion von diesen nicht öffentlichen Daten abhängig ist. APIs Eine Liste der öffentlich verfügbaren Dateien finden Sie in der API Referenz. APIs

  • Schreiben Sie idempotenten Code. Das Schreiben idempotenter Code für Ihre Funktionen stellt sicher, dass doppelte Ereignisse auf die gleiche Weise behandelt werden. Ihr Code sollte Ereignisse ordnungsgemäß validieren und doppelte Ereignisse ordnungsgemäß behandeln. Weitere Informationen finden Sie unter Wie mache ich meine Lambda-Funktion idempotent?.

  • Vermeiden Sie die Verwendung des DNS Java-Caches. Lambda-Funktionen speichern bereits DNS Antworten im Cache. Wenn Sie einen anderen DNS Cache verwenden, kann es zu Verbindungstimeouts kommen.

    Die java.util.logging.Logger Klasse kann den JVM DNS Cache indirekt aktivieren. Um die Standardeinstellungen zu überschreiben, setzen Sie networkaddress.cache.ttl vor der Initialisierung auf 0. logger Beispiel:

    public class MyHandler { // first set TTL property static{ java.security.Security.setProperty("networkaddress.cache.ttl" , "0"); } // then instantiate logger var logger = org.apache.logging.log4j.LogManager.getLogger(MyHandler.class); }
  • Reduzieren Sie den Zeitaufwand, den Lambda für das Entpacken von Bereitstellungspaketen benötigt, die in Java erstellt wurden, indem Sie Ihre Abhängigkeitsdateien .jar in einem separaten /lib-Verzeichnis ablegen. Dies geht schneller, als den gesamten Code Ihrer Funktion in einem einzigen Jar mit einer großen Anzahl von .class Dateien zu speichern. Detaillierte Anweisungen finden Sie unter Stellen Sie Java-Lambda-Funktionen mit.zip- oder JAR Dateiarchiven bereit.

Beispiel-Handler-Code

Das GitHub Repository für dieses Handbuch enthält Beispielanwendungen, die die Verwendung verschiedener Handlertypen und Schnittstellen demonstrieren. Jede Beispielanwendung enthält Skripts für die einfache Bereitstellung und Bereinigung, eine AWS SAM Vorlage und unterstützende Ressourcen.

Lambda-Beispielanwendungen in Java
  • java17-examples – Eine Java-Funktion, die demonstriert, wie ein Java-Datensatz verwendet wird, um ein Eingabeereignis-Datenobjekt darzustellen.

  • Java-Basis – Eine Sammlung minimaler Java-Funktionen mit Einheitentests und variabler Protokollierungskonfiguration.

  • java-events — Eine Sammlung von Java-Funktionen, die Grundcode für den Umgang mit Ereignissen verschiedener Dienste wie Amazon API Gateway, Amazon und Amazon SQS Kinesis enthalten. Diese Funktionen verwenden die neueste Version der aws-lambda-java-eventsBibliothek (3.0.0 und neuer). Für diese Beispiele ist das nicht AWS SDK als Abhängigkeit erforderlich.

  • s3-java — Eine Java-Funktion, die Benachrichtigungsereignisse von Amazon S3 verarbeitet und die Java Class Library (JCL) verwendet, um Miniaturansichten aus hochgeladenen Bilddateien zu erstellen.

  • Verwenden Sie API Gateway, um eine Lambda-Funktion aufzurufen — eine Java-Funktion, die eine Amazon DynamoDB-Tabelle scannt, die Mitarbeiterinformationen enthält. Anschließend verwendet es Amazon Simple Notification Service, um eine Textnachricht an Mitarbeiter zu senden, die ihr Betriebsjubiläum feiern. In diesem Beispiel wird API Gateway verwendet, um die Funktion aufzurufen.

Die s3-java Anwendungen java-events und verwenden ein AWS Dienstereignis als Eingabe und geben eine Zeichenfolge zurück. Die java-basic-Anwendung umfasst verschiedene Arten von Handlern:

Um verschiedene Handler-Typen zu testen, ändern Sie einfach den Handler-Wert in der AWS SAM Vorlage. Ausführliche Anweisungen finden Sie in der Liesmich-Datei der Beispielanwendung.