Definisci il gestore di funzioni Lambda in Java - AWS Lambda

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à.

Definisci il gestore di funzioni 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.

Il GitHub repository di questa guida fornisce applicazioni di easy-to-deploy esempio che illustrano una varietà di tipi di gestori. Per informazioni dettagliate, consulta la parte finale di questo argomento.

Gestore di esempio: runtime di Java 17

Nell'esempio seguente di Java 17, una classe denominata HandlerIntegerJava17 definisce un metodo del gestore denominato handleRequest. Il metodo del gestore accetta i seguenti input:

  • Un IntegerRecord, che è un record Java personalizzato che rappresenta i dati degli eventi. In questo esempio, definiamo IntegerRecord come segue:

    record IntegerRecord(int x, int y, String message) { }
  • Un oggetto di contesto, che fornisce i metodi e le proprietà che forniscono le informazioni sull'invocazione, sulla funzione e sull'ambiente di esecuzione.

Supponiamo di voler scrivere una funzione che registri il message dall'IntegerRecord di input e restituisca la somma di x e y. Di seguito è riportato il codice della funzione:

Esempio 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) { }

Specifica quale metodo Lambda deve richiamare impostando il parametro del gestore sulla configurazione della funzione. È possibile specificare il gestore nei seguenti formati:

  • package.Class::method: formato completo . Ad esempio: example.Handler::handleRequest.

  • package.Class: formato abbreviato per le classi che implementano un'interfaccia del gestore. Ad esempio: example.Handler.

Quando Lambda richiama il gestore, il runtime Lambda riceve un evento come stringa in formato -format e lo converte in JSON un oggetto. Nell'esempio precedente, un evento di esempio potrebbe essere simile al seguente:

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

Puoi salvare questo file e testare la tua funzione localmente con il seguente comando (): AWS Command Line Interface CLI

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

Gestore di esempio: runtime di Java 11 e versioni precedenti

Lambda supporta i record nei runtime di Java 17 e successivi. In tutti i runtime di Java, è possibile utilizzare una classe per rappresentare i dati degli eventi. L'esempio seguente utilizza un elenco di numeri interi e un oggetto di contesto come input e restituisce la somma di tutti i numeri interi nell'elenco.

Esempio Handler.java

Nell'esempio seguente, una classe denominata Handler definisce un metodo del gestore denominato handleRequest. Il metodo del gestore accetta un oggetto evento e contesto come input e restituisce una stringa.

Esempio 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(); } }

Per altri esempi, consulta la pagina Codice del gestore di esempio.

Codice di inizializzazione

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 un'invocazione e l'altra e possono essere riutilizzate dal gestore migliaia di volte. È possibile aggiungere il codice di inizializzazione al di fuori del metodo del gestore per risparmiare tempo di calcolo e riutilizzare le risorse tra più invocazioni.

Nell'esempio seguente, il codice di inizializzazione del client non rientra nel metodo del gestore principale. Il runtime inizializza il client prima che la funzione esegua il suo primo evento. Gli eventi successivi sono molto più veloci perché Lambda non ha bisogno di inizializzare nuovamente il client.

Esempio 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"; } }

Scelta dei tipi di input e di output

Specificare il tipo di oggetto a cui l'evento si mappa nella firma del metodo del gestore. Nell'esempio precedente, il runtime Java deserializza l'evento in un tipo che implementa l'interfaccia. Map<String,String> String-to-stringle mappe funzionano per eventi flat come i seguenti:

Esempio Event.json – Dati meteo
{ "temperatureK": 281, "windKmh": -3, "humidityPct": 0.55, "pressureHPa": 1020 }

Tuttavia, il valore di ogni campo deve essere una stringa o un numero. Se l'evento include un campo con un oggetto come valore, il runtime non può deserializzarlo e restituisce un errore.

Scegliere un tipo di input che funzioni con i dati degli eventi elaborati dalla funzione. È possibile utilizzare un tipo di base, un tipo generico o un tipo ben definito.

Tipi di input
  • Integer, Long, Double e così via. – L' evento è un numero senza formattazione aggiuntiva, ad esempio 3.5. Il runtime converte il valore in un oggetto del tipo specificato.

  • String— L'evento è una JSON stringa che include virgolette, ad esempio. "My string." Il runtime converte il valore (senza virgolette) in un oggetto String.

  • Type, Map<String,Type> e così via. — L'evento è un oggetto. JSON Il runtime lo deserializza in un oggetto del tipo o dell'interfaccia specificati.

  • List<Integer>, List<String>, List<Object> e così via. — L'evento è un JSON array. Il runtime lo deserializza in un oggetto del tipo o dell'interfaccia specificati.

  • InputStream— L'evento è JSON di qualsiasi tipo. 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 dai AWS servizi, utilizza i tipi presenti nella aws-lambda-java-eventslibreria.

Se definite il vostro tipo di input, dovrebbe essere un semplice oggetto Java (POJO) deserializzabile e mutabile, con un costruttore e proprietà predefiniti per ogni campo dell'evento. Le chiavi nell'evento che non si mappano a una proprietà e le proprietà che non sono incluse nell'evento vengono eliminate senza errori.

Il tipo di output può essere un oggetto o void. Il runtime serializza i valori restituiti in testo. Se l'output è un oggetto con campi, il runtime lo serializza in un documento. JSON Se è un tipo che esegue il wrapping di un valore primitivo, il runtime restituisce una rappresentazione testuale di tale valore.

Interfacce del gestore

La aws-lambda-java-corelibreria definisce due interfacce per i metodi del gestore. Utilizzare le interfacce fornite per semplificare la configurazione del gestore e convalidare la firma del metodo del gestore in fase di compilazione.

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. Quando si utilizza questa interfaccia, il runtime Java deserializza l'evento in un 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.

Esempio Handler.java – Interfaccia del gestore
// Handler value: example.Handler public class Handler implements RequestHandler<Map<String,String>, String>{ @Override public String handleRequest(Map<String,String> event, Context context)

Per utilizzare la propria serializzazione, 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.

Il seguente esempio di Java 21 mostra come utilizzare una funzione Lambda per elaborare gli ordini. L'esempio utilizza tipi di lettore e scrittore con buffer per lavorare con i flussi di input e output e mostra come definire record Java personalizzati da utilizzare all'interno della funzione.

Esempio 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) { } }

Procedure consigliate di codice per le funzioni Java Lambda

Segui le linee guida riportate nell'elenco seguente per utilizzare le migliori pratiche di codifica durante la creazione delle funzioni Lambda:

  • Separare il gestore Lambda dalla logica principale. In questo modo è possibile creare una funzione di cui è più semplice eseguire l'unit test.

  • Controllare le dipendenze nel pacchetto di distribuzione della funzione. L'ambiente di AWS Lambda esecuzione contiene diverse librerie. Per abilitare il set di caratteristiche e aggiornamenti della sicurezza più recenti, Lambda aggiorna periodicamente tali librerie. Tali aggiornamenti possono 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. In questo modo viene ridotta la quantità di tempo necessaria per il download del pacchetto e per la relativa decompressione prima dell'invocazione. Per le funzioni create in Java, evitate di caricare l'intera AWS SDK libreria come parte del pacchetto di distribuzione. Dipendi invece in modo selettivo dai moduli che raccolgono i componenti di cui SDK hai bisogno (ad esempio DynamoDB, moduli Amazon SDK S3 e librerie di base Lambda).

  • Sfruttare il riutilizzo del contesto di esecuzione per migliorare le prestazioni della funzione. Inizializza SDK i client e le connessioni al database all'esterno del gestore delle funzioni e memorizza nella cache gli asset statici localmente 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, vedere Riutilizzo delle connessioni con Keep-Alive in 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 vedi un volume involontario di chiamate, imposta la concorrenza riservata alla funzione su «0immediate» per limitare tutte le chiamate alla funzione mentre aggiorni il codice.

  • Non utilizzare documenti non documentati e non pubblici APIs nel codice della funzione Lambda. Per i runtime AWS Lambda gestiti, Lambda applica periodicamente aggiornamenti di sicurezza e funzionalità all'interno di Lambda. APIs Questi API aggiornamenti interni possono essere incompatibili con le versioni precedenti e portare a conseguenze indesiderate, come errori di chiamata se la funzione dipende da questi aggiornamenti non pubblici. APIs Vedi il riferimento per un elenco di quelli disponibili al pubblico. API APIs

  • 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 DNS cache Java. Le funzioni Lambda memorizzano già nella cache DNS le risposte. Se utilizzi un'altra DNS cache, potrebbero verificarsi dei timeout di connessione.

    La java.util.logging.Logger classe può abilitare indirettamente la cache. JVM DNS Per sovrascrivere le impostazioni predefinite, imposta networkaddress.cache.ttl su 0 prima dell'inizializzazione. 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 .jar della dipendenza in una directory /lib separata. Questo metodo è più rapido rispetto all'inserimento di tutto il codice della funzione in un unico file .jar con un elevato numero di file .class. Per istruzioni, consulta Implementa le funzioni Java Lambda con archivi di JAR file.zip o di file.

Codice di esempio del gestore

L' GitHub archivio di questa guida include applicazioni di esempio che dimostrano l'uso di vari tipi di gestori e interfacce. Ogni applicazione di esempio include script per facilitare la distribuzione e la pulizia, un AWS SAM modello e risorse di supporto.

Applicazioni Lambda di esempio in Java
  • java17-examples: una funzione Java che dimostra come utilizzare un record Java per rappresentare un oggetto di dati dell'evento di input.

  • java-basic: una raccolta di funzioni Java minimali con unit test e configurazione della registrazione dei log delle variabili.

  • java-events — Una raccolta di funzioni Java che contengono codice scheletrico su come gestire gli eventi di vari servizi come Amazon GatewayAPI, Amazon e Amazon SQS Kinesis. Queste funzioni utilizzano la versione più recente della aws-lambda-java-eventslibreria (3.0.0 e successive). Questi esempi non richiedono che sia una dipendenza AWS SDK.

  • s3-java: una funzione Java che elabora gli eventi di notifica da Amazon S3 e utilizza la Java Class Library JCL () per creare miniature dai file di immagine caricati.

  • Usa API Gateway per richiamare una funzione Lambda, una funzione Java che analizza una tabella Amazon DynamoDB che contiene informazioni sui dipendenti. Quindi utilizza Amazon Simple Notification Service per inviare un messaggio di testo ai dipendenti per festeggiare i loro anniversari di lavoro. Questo esempio utilizza API Gateway per richiamare la funzione.

Le s3-java applicazioni java-events and accettano un evento AWS di servizio come input e restituiscono una stringa. L'applicazione java-basic include vari tipi di gestori:

Per testare diversi tipi di gestore, basta modificare il valore del gestore nel modello. AWS SAM Per istruzioni dettagliate, consultare il file readme dell'applicazione di esempio.