Le traduzioni sono generate tramite traduzione automatica. In caso di conflitto tra il contenuto di una traduzione e la versione originale in Inglese, quest'ultima prevarrà.
Definire l'handler della funzione Lambda in Java
Il gestore di funzioni Lambda è il metodo nel codice della funzione che elabora gli eventi. Quando viene richiamata la funzione, Lambda esegue il metodo del gestore. La funzione viene eseguita fino a quando il gestore non restituisce una risposta, termina o scade.
Questa pagina descrive come lavorare con i gestori di funzioni Lambda in Java, incluse le opzioni per la configurazione del progetto, le convenzioni di denominazione e le best practice. Questa pagina include anche un esempio di funzione Java Lambda che acquisisce informazioni su un ordine, genera una ricevuta in un file di testo e lo inserisce in un bucket Amazon Simple Storage Service (Amazon S3). Per informazioni su come distribuire una funzione dopo averla scritta, consulta o. Distribuisci funzioni Lambda per Java con archivi di file .zip o JAR Distribuisci funzioni Java Lambda con immagini di container
Sections
Configurazione del progetto Java handler
Quando si lavora con le funzioni Lambda in Java, il processo prevede la scrittura del codice, la compilazione e la distribuzione degli artefatti compilati in Lambda. È possibile inizializzare un progetto Java Lambda in vari modi. Ad esempio, puoi utilizzare strumenti come Maven Archetype for Lambda functions
Un tipico progetto di funzione Java Lambda segue questa struttura generale:
/project-root └ src └ main └ java └ example └ OrderHandler.java (contains main handler) └ <other_supporting_classes> └ build.gradle OR pom.xml
Puoi usare Maven o Gradle per creare il tuo progetto e gestire le dipendenze.
La logica principale del gestore per la funzione risiede in un file Java nella directory. src/main/java/example Nell'esempio di questa pagina, diamo un nome a questo file. OrderHandler.java Oltre a questo file, è possibile includere classi Java aggiuntive, se necessario. Quando distribuisci la tua funzione su Lambda, assicurati di specificare la classe Java che contiene il metodo di gestione principale che Lambda deve richiamare durante un'invocazione.
Esempio di codice della funzione Java Lambda
Il seguente esempio di codice funzione Java 21 Lambda acquisisce informazioni su un ordine, produce una ricevuta in un file di testo e inserisce questo file in un bucket Amazon S3.
Esempio Funzione Lambda OrderHandler.java
package example; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import software.amazon.awssdk.core.sync.RequestBody; import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.services.s3.model.PutObjectRequest; import software.amazon.awssdk.services.s3.model.S3Exception; import java.nio.charset.StandardCharsets; /** * Lambda handler for processing orders and storing receipts in S3. */ public class OrderHandler implements RequestHandler<OrderHandler.Order, String> { private static final S3Client S3_CLIENT = S3Client.builder().build(); /** * Record to model the input event. */ public record Order(String orderId, double amount, String item) {} @Override public String handleRequest(Order event, Context context) { try { // Access environment variables String bucketName = System.getenv("RECEIPT_BUCKET"); if (bucketName == null || bucketName.isEmpty()) { throw new IllegalArgumentException("RECEIPT_BUCKET environment variable is not set"); } // Create the receipt content and key destination String receiptContent = String.format("OrderID: %s\nAmount: $%.2f\nItem: %s", event.orderId(), event.amount(), event.item()); String key = "receipts/" + event.orderId() + ".txt"; // Upload the receipt to S3 uploadReceiptToS3(bucketName, key, receiptContent); context.getLogger().log("Successfully processed order " + event.orderId() + " and stored receipt in S3 bucket " + bucketName); return "Success"; } catch (Exception e) { context.getLogger().log("Failed to process order: " + e.getMessage()); throw new RuntimeException(e); } } private void uploadReceiptToS3(String bucketName, String key, String receiptContent) { try { PutObjectRequest putObjectRequest = PutObjectRequest.builder() .bucket(bucketName) .key(key) .build(); // Convert the receipt content to bytes and upload to S3 S3_CLIENT.putObject(putObjectRequest, RequestBody.fromBytes(receiptContent.getBytes(StandardCharsets.UTF_8))); } catch (S3Exception e) { throw new RuntimeException("Failed to upload receipt to S3: " + e.awsErrorDetails().errorMessage(), e); } } }
Questo file OrderHandler.java contiene le sezioni seguenti:
-
package example: In Java, può essere qualsiasi cosa, ma deve corrispondere alla struttura delle directory del progetto. Qui, usiamopackage exampleperché la struttura delle directory èsrc/main/java/example. -
importdichiarazioni: usale per importare le classi Java richieste dalla tua funzione Lambda. -
public class OrderHandler ...: definisce la tua classe Java e deve essere una definizione di classe valida. -
private static final S3Client S3_CLIENT ...: inizializza un client S3 al di fuori di qualsiasi metodo della classe. Ciò fa sì che Lambda esegua questo codice durante la fase di inizializzazione. -
public record Order ...: definisce la forma dell'evento di input previsto in questo recordJava personalizzato. -
public String handleRequest(Order event, Context context): questo è il metodo dell'handler principale, che contiene la logica principale dell'applicazione. -
private void uploadReceiptToS3(...) {}: questo è un metodo helper a cui fa riferimento il metodo dell'handler principalehandleRequest.
Il seguente file build.gradle o pom.xml accompagna questa funzione.
Affinché questa funzione funzioni correttamente, il suo ruolo di esecuzione deve consentire l'azione. s3:PutObject Inoltre, assicuratevi di definire la variabile di RECEIPT_BUCKET ambiente. Dopo una chiamata riuscita, il bucket Amazon S3 dovrebbe contenere un file di ricevuta.
Nota
Questa funzione potrebbe richiedere impostazioni di configurazione aggiuntive per funzionare correttamente senza scadere. Si consiglia di configurare 256 MB di memoria e un timeout di 10 secondi. La prima chiamata potrebbe richiedere più tempo a causa di un avvio a freddo. Avvii a freddo e latenza Le chiamate successive dovrebbero essere eseguite molto più velocemente grazie al riutilizzo dell'ambiente di esecuzione.
Definizioni di classe valide per i gestori Java
Per definire la tua classe, la libreria https://github.com/aws/aws-lambda-java-libs/tree/master/aws-lambda-java-core
L'interfaccia RequestHandler è un tipo generico che accetta due parametri: il tipo di input e il tipo di output. Entrambi i tipi devono essere oggetti. In questo esempio, la nostra OrderHandler classe implementa. RequestHandler<OrderHandler.Order, String> Il tipo di input è il Order record che definiamo all'interno della classe e il tipo di output èString.
public class OrderHandler implements RequestHandler<OrderHandler.Order, String> { ... }
Quando si utilizza questa interfaccia, il runtime Java deserializza l'evento nell'oggetto con il tipo di input e serializza l'output in testo. Utilizzare questa interfaccia quando la serializzazione integrata funziona con i tipi di input e output.
Per utilizzare la propria serializzazione, è possibile implementare l'interfaccia. RequestStreamHandler Con questa interfaccia, Lambda passa al gestore un flusso di input e un flusso di output. Il gestore legge i byte dal flusso di input, scrive nel flusso di output e restituisce il valore void. Per un esempio di ciò utilizzando il runtime Java 21, vedere HandlerStream.java
Se state lavorando solo con tipi base e generici (ad esempio StringInteger,List, oMap) nella vostra funzione Java, non è necessario implementare un'interfaccia. Ad esempio, se la tua funzione accetta un Map<String, String> input e restituisce unString, la definizione della classe e la firma del gestore potrebbero assomigliare alle seguenti:
public class ExampleHandler { public String handleRequest(Map<String, String> input, Context context) { ... } }
Inoltre, quando non si implementa un'interfaccia, l'oggetto context è opzionale. Ad esempio, la definizione della classe e la firma del gestore potrebbero essere le seguenti:
public class NoContextHandler { public String handleRequest(Map<String, String> input) { ... } }
Convenzioni di denominazione dei gestori
Per le funzioni Lambda in Java, se si implementa l'interfaccia RequestHandler o RequestStreamHandler, è necessario denominare il metodo del gestore principale handleRequest. Inoltre, includi il @Override tag sopra il tuo handleRequest metodo. Quando distribuisci la tua funzione su Lambda, specifica il gestore principale nella configurazione della tua funzione nel seguente formato:
-
<package>.<Class>— Ad esempio,example.OrderHandler.
Per le funzioni Lambda in Java che non implementano l'RequestStreamHandlerinterfaccia RequestHandler or, puoi utilizzare qualsiasi nome per il gestore. Quando distribuisci la tua funzione su Lambda, specifica il gestore principale nella configurazione della tua funzione nel seguente formato:
-
<package>.<Class>::<handler_method_name>— Ad esempio,example.Handler::mainHandler.
Definizione e accesso all'oggetto evento di input
JSON è il formato di input più comune e standard per le funzioni Lambda. In questo esempio, la funzione prevede un input simile a quanto segue:
{ "orderId": "12345", "amount": 199.99, "item": "Wireless Headphones" }
Quando si lavora con le funzioni Lambda in Java 17 o versioni successive, è possibile definire la forma dell'evento di input previsto come record Java. In questo esempio, definiamo un record all'interno della OrderHandler classe per rappresentare un Order oggetto:
public record Order(String orderId, double amount, String item) {}
Questo record corrisponde alla forma di input prevista. Dopo aver definito il record, puoi scrivere una firma del gestore che accetta un input JSON conforme alla definizione del record. Il runtime Java deserializza automaticamente questo JSON in un oggetto Java. È quindi possibile accedere ai campi dell'oggetto. Ad esempio, event.orderId recupera il valore di orderId dall'input originale.
Nota
I record Java sono una funzionalità solo dei runtime di Java 17 e versioni successive. In tutti i runtime di Java, è possibile utilizzare una classe per rappresentare i dati degli eventi. In questi casi, puoi usare una libreria come jackson
Altri tipi di eventi di input
Esistono molti possibili eventi di input per le funzioni Lambda in Java:
-
Integer,LongDouble, e altri tipi numerici: l'evento è un numero senza formattazione aggiuntiva, ad esempio.3.5Il runtime Java converte il valore in un oggetto del tipo specificato. -
String: l'evento è una stringa JSON, incluse le virgolette, ad esempio"My string". Il runtime converte il valore in unStringoggetto senza virgolette. -
List<Integer>,List<String>List<Object>, e tipi simili: l'evento è un array JSON. Il runtime lo deserializza in un oggetto del tipo o dell'interfaccia specificati. -
InputStream: l'evento è un tipo qualsiasi di JSON. Il runtime passa un flusso di byte del documento al gestore senza modifiche. Si deserializza l'output di input e scrittura in un flusso di output. -
Tipo di libreria: per gli eventi inviati da altri AWS servizi, usa i tipi nella libreria https://github.com/aws/aws-lambda-java-libs/tree/main/aws-lambda-java-events
aws-lambda-java-events. Ad esempio, se la tua funzione Lambda viene richiamata da Amazon Simple Queue Service (SQS), utilizza l'oggetto come input. SQSEvent
Accesso e utilizzo dell'oggetto contestuale Lambda
L'oggetto contesto: contiene informazioni sulla chiamata, sulla funzione e sull'ambiente di esecuzione. In questo esempio, l'oggetto contesto è di tipo com.amazonaws.services.lambda.runtime.Context ed è il secondo argomento della funzione del gestore principale.
public String handleRequest(Order event, Context context) { ... }
Se la tua classe implementa l' RequestStreamHandler
Se si effettuano chiamate ad altri servizi utilizzando l' AWS SDK, l'oggetto context è richiesto in alcune aree chiave. Ad esempio, per produrre log delle funzioni per Amazon CloudWatch, puoi utilizzare il context.getLogger() metodo per ottenere un LambdaLogger oggetto per la registrazione. In questo esempio, possiamo usare il logger per registrare il log di un messaggio di errore se l'elaborazione fallisce per qualche motivo:
context.getLogger().log("Failed to process order: " + e.getMessage());
Oltre alla registrazione di log, puoi anche utilizzare l'oggetto contesto per il monitoraggio delle funzioni. Per ulteriori informazioni sulla copia di oggetti, consulta la sezione Utilizzo dell'oggetto di contesto Lambda per recuperare le informazioni sulla funzione Java.
Utilizzo di AWS SDK per Java v2 nel tuo gestore
Spesso, utilizzerai le funzioni Lambda per interagire o apportare aggiornamenti ad altre risorse. AWS Il modo più semplice per interfacciarsi con queste risorse è utilizzare l' AWS SDK per Java v2.
Nota
L' AWS SDK per Java (v1) è in modalità manutenzione e terminerà il supporto il 31 dicembre 2025. Ti consigliamo di utilizzare solo l' AWS SDK per Java v2 in futuro.
Per aggiungere dipendenze SDK alla tua funzione, aggiungile nel tuo file build.gradle per Gradle o per Maven. pom.xml Ti consigliamo di aggiungere solo le librerie necessarie per la tua funzione. Nel codice di esempio precedente, abbiamo usato la libreria software.amazon.awssdk.services.s3. In Gradle, puoi aggiungere questa dipendenza aggiungendo la seguente riga nella sezione delle dipendenze del tuo: build.gradle
implementation 'software.amazon.awssdk:s3:2.28.29'
In Maven, aggiungi le seguenti righe nella sezione del tuo: <dependencies> pom.xml
<dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>s3</artifactId> <version>2.28.29</version> </dependency>
Nota
Questa potrebbe non essere la versione più recente dell'SDK. Scegli la versione appropriata dell'SDK per la tua applicazione.
Quindi, importa le dipendenze direttamente nella tua classe Java:
import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.services.s3.model.PutObjectRequest; import software.amazon.awssdk.services.s3.model.S3Exception;
Il codice di esempio inizializza quindi un client Amazon S3 come segue:
private static final S3Client S3_CLIENT = S3Client.builder().build();
In questo esempio, abbiamo inizializzato il nostro client Amazon S3 all’esterno della funzione del gestore principale per evitare di doverlo inizializzare ogni volta che invochiamo la nostra funzione. Dopo aver inizializzato il client SDK, puoi utilizzarlo per interagire con altri servizi. AWS Il codice di esempio richiama l'API PutObject Amazon S3 nel modo seguente:
PutObjectRequest putObjectRequest = PutObjectRequest.builder() .bucket(bucketName) .key(key) .build(); // Convert the receipt content to bytes and upload to S3 S3_CLIENT.putObject(putObjectRequest, RequestBody.fromBytes(receiptContent.getBytes(StandardCharsets.UTF_8)));
Accesso alle variabili d'ambiente
Nel codice del gestore, puoi fare riferimento a qualsiasi variabile di ambiente utilizzando il metodo. System.getenv() In questo esempio, facciamo riferimento alla variabile di RECEIPT_BUCKET ambiente definita utilizzando la seguente riga di codice:
String bucketName = System.getenv("RECEIPT_BUCKET"); if (bucketName == null || bucketName.isEmpty()) { throw new IllegalArgumentException("RECEIPT_BUCKET environment variable is not set"); }
Utilizzo dello stato globale
Lambda esegue il codice statico e il costruttore della classe durante la fase di inizializzazione prima di richiamare la funzione per la prima volta. Le risorse create durante l'inizializzazione restano in memoria tra le invocazioni, in modo da evitare di doverle creare ogni volta che si invoca la funzione.
Nel codice di esempio, il codice di inizializzazione del client S3 non rientra nel metodo del gestore principale. Il runtime inizializza il client prima che la funzione gestisca il primo evento e il client rimane disponibile per il riutilizzo in tutte le chiamate.
Best practice di codice per funzioni Lambda in Java
Segui le linee guida riportate nell'elenco seguente per utilizzare le best practice di codifica durante la creazione delle funzioni Lambda:
-
Separare il gestore Lambda dalla logica principale. Con questo approccio, è possibile creare una funzione più testabile a livello di unità.
-
Controllare le dipendenze nel pacchetto di distribuzione della funzione. L'ambiente di esecuzione AWS Lambda contiene diverse librerie. Per abilitare l'ultimo set di funzionalità e aggiornamenti di sicurezza, Lambda aggiorna periodicamente queste librerie. Questi aggiornamenti potrebbero introdurre lievi modifiche al comportamento della funzione Lambda. Per mantenere il controllo completo delle dipendenze utilizzate dalla funzione, inserire tutte le dipendenze nel pacchetto di implementazione.
-
Ridurre la complessità delle dipendenze. Preferire framework più semplici che si caricano velocemente all'avvio del contesto di esecuzione. Preferire ad esempio l'utilizzo di framework di inserimento di dipendenze Java, come Dagger
o Guice , rispetto a framework più complessi come Spring Framework . -
Ridurre al minimo le dimensioni del pacchetto di implementazione al fine di soddisfare le esigenze di runtime. Ciò riduce il tempo necessario per scaricare e decomprimere il pacchetto di distribuzione prima dell'invocazione. Per le funzioni create in Java, evita di caricare l'intera libreria AWS SDK come parte del pacchetto di distribuzione. Dipendi invece in modo selettivo dai moduli che raccolgono i componenti dell'SDK di cui hai bisogno (ad esempio, DynamoDB, moduli SDK Amazon S3 e librerie di base Lambda sul sito web). https://github.com/aws/aws-lambda-java-libs
GitHub
Sfruttare il riutilizzo del contesto di esecuzione per migliorare le prestazioni della funzione. Inizializzare i client SDK e le connessioni al database all'esterno del gestore di funzioni e memorizzare localmente nella cache gli asset statici nella directory /tmp. Le chiamate successive elaborate dalla stessa istanza della funzione possono riutilizzare queste risorse. Ciò consente di risparmiare sui costi riducendo i tempi di esecuzione delle funzioni.
Per evitare potenziali perdite di dati tra le chiamate, non utilizzare il contesto di esecuzione per archiviare dati utente, eventi o altre informazioni con implicazioni di sicurezza. Se la funzione si basa su uno stato mutabile che non può essere archiviato in memoria all'interno del gestore, considerare la possibilità di creare una funzione separata o versioni separate di una funzione per ogni utente.
Utilizzare una direttiva keep-alive per mantenere le connessioni persistenti. Lambda elimina le connessioni inattive nel tempo. Se si tenta di riutilizzare una connessione inattiva quando si richiama una funzione, si verificherà un errore di connessione. Per mantenere la connessione persistente, utilizzare la direttiva keep-alive associata al runtime. Per un esempio, consulta Reusing Connections with in. Keep-Alive Node.js
Utilizzare le variabili di ambiente per passare i parametri operativi alla funzione. Se ad esempio si scrive in un bucket Amazon S3 anziché impostare come hard-coded il nome del bucket in cui si esegue la scrittura, configurare tale nome come una variabile di ambiente.
Evita di usare invocazioni ricorsive nella tua funzione Lambda, in cui la funzione si richiama da sola o avvia un processo che potrebbe richiamare nuovamente la funzione. Ciò potrebbe provocare un volume non desiderato di invocazioni della funzione e un aumento dei costi. Se noti un volume indesiderato di invocazioni, imposta immediatamente la simultaneità riservata della funzione su 0 per interrompere tutte le invocazioni della funzione mentre si aggiorna il codice.
Non utilizzare API non documentate e non pubbliche nel codice della funzione Lambda. Per i runtime AWS Lambda gestiti, Lambda applica periodicamente aggiornamenti di sicurezza e funzionali alle API interne di Lambda. Questi aggiornamenti API interni potrebbero essere incompatibili con le versioni precedenti, causando conseguenze indesiderate come errori di chiamata se la funzione ha una dipendenza su queste API non pubbliche. Consulta il riferimento all'API per un elenco di API disponibili pubblicamente.
Scrivi un codice idempotente. La scrittura di un codice idempotente per le tue funzioni garantisce che gli eventi duplicati vengano gestiti allo stesso modo. Il tuo codice dovrebbe convalidare correttamente gli eventi e gestire con garbo gli eventi duplicati. Per ulteriori informazioni, consulta Come posso rendere idempotente la mia funzione Lambda?
-
Evita di usare la cache DNS Java. Le funzioni Lambda memorizzano già nella cache le risposte DNS. Se viene utilizzata un'altra cache DNS, è possibile che si verifichino dei timeout di connessione.
La classe
java.util.logging.Loggerpuò abilitare indirettamente la cache DNS JVM. Per sovrascrivere le impostazioni predefinite, imposta networkaddress.cache.ttlsu 0 prima dell'inizializzazione di logger. Esempio: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); } -
Ridurre il tempo necessario a Lambda per decomprimere i pacchetti di distribuzione creati in Java inserendo i file
.jardella dipendenza in una directory /lib separata. Questo è più veloce rispetto all'inserimento di tutto il codice della funzione in un singolo jar con un numero elevato di file..classPer istruzioni, consulta Distribuisci funzioni Lambda per Java con archivi di file .zip o JAR.