Définir le gestionnaire de fonctions Lambda en Java - AWS Lambda

Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.

Définir le gestionnaire de fonctions Lambda en Java

Le gestionnaire de fonction Lambda est la méthode dans votre code de fonction qui traite les événements. Lorsque votre fonction est invoquée, Lambda exécute la méthode du gestionnaire. Votre fonction s’exécute jusqu’à ce que le gestionnaire renvoie une réponse, se ferme ou expire.

Le GitHub référentiel de ce guide fournit des easy-to-deploy exemples d'applications illustrant différents types de gestionnaires. Pour de plus amples informations, veuillez consulter la fin de cette rubrique.

Exemple de gestionnaire : exécutions Java 17

Dans l’exemple Java 17 suivant, une classe nommée HandlerIntegerJava17 définit une méthode de gestionnaire nommée handleRequest. La méthode du gestionnaire prend en compte les entrées suivantes :

  • Un IntegerRecord, qui est un enregistrement Java personnalisé qui représente les données d’un événement. Dans cet exemple, nous définissons IntegerRecord comme suit :

    record IntegerRecord(int x, int y, String message) { }
  • Un objet de contexte, qui fournit des méthodes et des propriétés fournissant des informations sur l’invocation, la fonction et l’environnement d’exécution.

Supposons que nous voulions écrire une fonction qui enregistre le message de l’entrée IntegerRecord et renvoie la somme de x et y. Le code de la fonction est le suivant :

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

Vous spécifiez quelle méthode vous voulez que Lambda invoque en définissant le paramètre de gestionnaire sur la configuration de votre fonction. Vous pouvez exprimer le gestionnaire dans les formats suivants :

  • package.Class::method – Format complet. olpPar exemple : example.Handler::handleRequest.

  • package.Class – Format abrégé pour les catégories qui implémentent une interface de gestionnaire. olpPar exemple : example.Handler.

Lorsque Lambda appelle votre gestionnaire, le moteur d'exécution Lambda reçoit un événement sous forme de chaîne JSON formatée et le convertit en objet. Dans l’exemple précédent, un exemple d’événement peut s’apparenter à ce qui suit :

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

Vous pouvez enregistrer ce fichier et tester votre fonction localement à l'aide de la commande suivante AWS Command Line Interface (CLI) :

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

Exemple de gestionnaire : exécutions Java 11 et antérieures

Lambda prend en charge les enregistrements dans les exécutions Java 17 et ultérieures. Dans toutes les exécutions Java, vous pouvez utiliser une classe pour représenter les données des événements. L’exemple suivant prend une liste d’entiers et un objet contextuel en entrée, et renvoie la somme de tous les entiers de la liste.

Exemple handler.java

Dans l’exemple suivant, une classe nommée Handler définit une méthode de gestionnaire nommée handleRequest. La méthode de gestionnaire prend un événement et un objet de contexte en entrée et renvoie une chaîne.

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

Pour plus d’exemples, voir Exemple de code de gestionnaire.

Code d’initialisation

Lambda exécute votre code statique et le constructeur de classe pendant la phase d’initialisation avant d’invoquer votre fonction pour la première fois. Les ressources créées pendant l’initialisation restent en mémoire entre les invocations et peuvent être réutilisées par le gestionnaire des milliers de fois. Vous pouvez ajouter du code d’initialisation en dehors de votre méthode de gestionnaire principale pour économiser du temps de calcul et réutiliser les ressources sur plusieurs invocations.

Dans l’exemple suivant, le code d’initialisation du client se trouve en dehors de la méthode du gestionnaire principal. L’exécution initialise le client avant que la fonction n’exécute son premier événement. Les événements suivants sont beaucoup plus rapides, car Lambda n’a pas besoin d’initialiser à nouveau le client.

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

Choix des types d’entrée et de sortie

Vous spécifiez le type d’objet auquel l’événement est mappé dans la signature de la méthode de gestion. Dans l'exemple précédent, le moteur d'exécution Java désérialise l'événement dans un type qui implémente l'Map<String,String>interface. String-to-stringles cartes fonctionnent pour les événements plats tels que les suivants :

Exemple Event.json – Données météorologiques.
{ "temperatureK": 281, "windKmh": -3, "humidityPct": 0.55, "pressureHPa": 1020 }

Cependant, la valeur de chaque champ doit être une chaîne ou un nombre. Si l’événement inclut un champ qui a un objet comme valeur, le moteur d’exécution ne peut pas le désérialiser et renvoie une erreur.

Choisissez un type d’entrée qui fonctionne avec les données d’événement traitées par votre fonction. Vous pouvez utiliser un type de base, un type générique ou un type bien défini.

Types d’entrée
  • Integer, Long, Double, etc. – L’événement est un nombre sans mise en forme supplémentaire, par exemple, 3.5. Le moteur d’exécution convertit la valeur en un objet du type spécifié.

  • String— L'événement est une JSON chaîne, y compris des guillemets, par exemple,. "My string." Le moteur d’exécution convertit la valeur (sans guillemets) en objet String.

  • Type, Map<String,Type> etc. — L'événement est un JSON objet. Le moteur d’exécution le désérialise en un objet du type ou de l’interface spécifié.

  • List<Integer>, List<String>, List<Object>, etc. — L'événement est un JSON tableau. Le moteur d’exécution le désérialise en un objet du type ou de l’interface spécifié.

  • InputStream— L'événement est de n'importe quel JSON type. Le moteur d’exécution transmet un flux d’octets du document au gestionnaire sans modification. Vous désérialisez l’entrée et écrivez la sortie dans un flux de sortie.

  • Type de bibliothèque : pour les événements envoyés par AWS les services, utilisez les types de la aws-lambda-java-eventsbibliothèque.

Si vous définissez votre propre type d'entrée, il doit s'agir d'un ancien objet Java désérialisable et mutable (POJO), avec un constructeur et des propriétés par défaut pour chaque champ de l'événement. Les clés de l’événement qui ne correspondent pas à une propriété, ainsi que les propriétés qui ne sont pas incluses dans l’événement sont supprimées sans erreur.

Le type de sortie peut être un objet ou void. Le moteur d’exécution sérialise les valeurs de retour dans le texte. Si le résultat est un objet avec des champs, le moteur d'exécution le sérialise dans un JSON document. S’il s’agit d’un type qui enveloppe une valeur primitive, le moteur d’exécution renvoie une représentation textuelle de cette valeur.

Interfaces du gestionnaire

La aws-lambda-java-corebibliothèque définit deux interfaces pour les méthodes de gestion. Utilisez les interfaces fournies pour simplifier la configuration du gestionnaire et valider la signature de la méthode du gestionnaire au moment de la compilation.

L’interface RequestHandler est un type générique qui prend deux paramètres : le type d’entrée et le type de sortie. Les deux types doivent être des objets. Lorsque vous utilisez cette interface, le moteur d’exécution Java désérialise l’événement dans un objet avec le type d’entrée et sérialise la sortie en texte. Utilisez cette interface lorsque la sérialisation intégrée fonctionne avec vos types d’entrée et de sortie.

Exemple Handler.java – Interface du gestionnaire.
// Handler value: example.Handler public class Handler implements RequestHandler<Map<String,String>, String>{ @Override public String handleRequest(Map<String,String> event, Context context)

Pour utiliser votre propre sérialisation, implémentez l’interface RequestStreamHandler. Avec cette interface, Lambda transmet à votre gestionnaire un flux d’entrée et un flux de sortie. Le gestionnaire lit les octets du flux d’entrée, écrit dans le flux de sortie et renvoie une valeur vide.

L'exemple Java 21 suivant montre comment utiliser une fonction Lambda pour traiter les commandes. L'exemple utilise des types de lecteur et d'écriture mis en mémoire tampon pour travailler avec les flux d'entrée et de sortie, et montre comment définir des enregistrements Java personnalisés à utiliser dans votre fonction.

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

Bonnes pratiques en matière de code pour les fonctions Java Lambda

Respectez les directives de la liste suivante pour utiliser les meilleures pratiques de codage lors de la création de vos fonctions Lambda :

  • Séparez le gestionnaire Lambda de votre logique principale. Cela vous permet de créer une fonction testable plus unitaire.

  • Contrôlez les dépendances du package de déploiement de vos fonctions. L'environnement AWS Lambda d'exécution contient un certain nombre de bibliothèques. Pour activer le dernier ensemble de mises à jour des fonctionnalités et de la sécurité, Lambda met régulièrement à jour ces bibliothèques. Ces mises à jour peuvent introduire de subtiles modifications dans le comportement de votre fonction Lambda. Pour disposer du contrôle total des dépendances que votre fonction utilise, empaquetez toutes vos dépendances avec votre package de déploiement.

  • Réduisez la complexité de vos dépendances. Privilégiez les infrastructures plus simples qui se chargent rapidement au démarrage de l’environnement d’exécution. Par exemple, préférez les infrastructures d’injection (IoC) de dépendances Java plus simples comme Dagger ou Guice, à des plus complexes comme Spring Framework.

  • Réduisez la taille de votre package de déploiement selon ses besoins d’exécution. Cela contribue à réduire le temps nécessaire au téléchargement et à la décompression de votre package de déploiement avant l’invocation. Pour les fonctions créées en Java, évitez de télécharger l'intégralité de la AWS SDK bibliothèque dans le cadre de votre package de déploiement. Dépendez plutôt de manière sélective des modules qui récupèrent les composants dont SDK vous avez besoin (par exemple DynamoDB, les SDK modules Amazon S3 et les bibliothèques principales Lambda).

  • Tirez parti de la réutilisation de l’environnement d’exécution pour améliorer les performances de votre fonction. Initialisez les SDK clients et les connexions à la base de données en dehors du gestionnaire de fonctions et mettez en cache les actifs statiques localement dans le /tmp répertoire. Les invocations ultérieures traitées par la même instance de votre fonction peuvent réutiliser ces ressources. Cela permet d’économiser des coûts, tout en réduisant le temps d’exécution de la fonction.

    Pour éviter des éventuelles fuites de données entre les invocations, n’utilisez pas l’environnement d’exécution pour stocker des données utilisateur, des événements ou d’autres informations ayant un impact sur la sécurité. Si votre fonction repose sur un état réversible qui ne peut pas être stocké en mémoire dans le gestionnaire, envisagez de créer une fonction distincte ou des versions distinctes d’une fonction pour chaque utilisateur.

  • Utilisez une directive keep-alive pour maintenir les connexions persistantes. Lambda purge les connexions inactives au fil du temps. Si vous tentez de réutiliser une connexion inactive lorsque vous invoquez une fonction, cela entraîne une erreur de connexion. Pour maintenir votre connexion persistante, utilisez la directive Keep-alive associée à votre environnement d’exécution. Pour obtenir un exemple, consultez Réutilisation des connexions avec Keep-Alive dans Node.js.

  • Utilisez des variables d’environnement pour transmettre des paramètres opérationnels à votre fonction. Par exemple, si vous écrivez dans un compartiment Amazon S3 au lieu de coder en dur le nom du compartiment dans lequel vous écrivez, configurez le nom du compartiment comme variable d’environnement.

  • Évitez d'utiliser des invocations récursives dans votre fonction Lambda, lorsque la fonction s'invoque elle-même ou initie un processus susceptible de l'invoquer à nouveau. Cela peut entraîner un volume involontaire d’invocations de fonction et des coûts accrus. Si vous constatez un volume d'invocations involontaire, réglez la fonction réservée à la simultanéité sur 0 immédiatement afin de limiter toutes les invocations à la fonction pendant que vous mettez à jour le code.

  • N'utilisez pas de code non documenté ni public APIs dans votre code de fonction Lambda. Pour les AWS Lambda environnements d'exécution gérés, Lambda applique régulièrement des mises à jour de sécurité et fonctionnelles aux applications internes de Lambda. APIs Ces API mises à jour internes peuvent être rétroincompatibles, ce qui peut entraîner des conséquences imprévues, telles que des échecs d'invocation si votre fonction dépend de ces mises à jour non publiques. APIs Voir la API référence pour une liste des documents accessibles au publicAPIs.

  • Écriture du code idempotent. L’écriture de code idempotent pour vos fonctions garantit ne gestion identique des événements dupliqués. Votre code doit valider correctement les événements et gérer correctement les événements dupliqués. Pour de plus amples informations, veuillez consulterComment faire en sorte que ma fonction Lambda soit idempotente ?.

  • Évitez d'utiliser le DNS cache Java. Les fonctions Lambda mettent déjà en cache DNS les réponses. Si vous utilisez un autre DNS cache, vous risquez de rencontrer des délais de connexion.

    La java.util.logging.Logger classe peut activer indirectement le JVM DNS cache. Pour remplacer les paramètres par défaut, définissez networkaddress.cache.ttl sur 0 avant l'initialisation. logger Exemple :

    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); }
  • Réduisez le temps que Lambda met à décompresser les packages de déploiement créés dans Java en plaçant vos fichiers .jar de dépendance dans un répertoire /lib distinct. Cette solution est plus rapide que le placement de tout le code de votre fonction dans un seul fichier .jar comprenant une multitude de fichiers .class. Pour obtenir des instructions, consultez Déployez des fonctions Java Lambda avec des archives .zip ou de fichiers JAR.

Exemple de code de gestionnaire

Le GitHub référentiel de ce guide inclut des exemples d'applications illustrant l'utilisation de différents types de gestionnaires et d'interfaces. Chaque exemple d'application inclut des scripts pour faciliter le déploiement et le nettoyage, un AWS SAM modèle et des ressources de support.

Exemples d’applications Lambda en Java
  • java17-examples : fonction Java qui montre comment utiliser un enregistrement Java pour représenter un objet de données d’événement en entrée.

  • java-basic – Ensemble de fonctions Java minimales avec des tests unitaires et une configuration de journalisation variable.

  • java-events — Ensemble de fonctions Java contenant du code squelette expliquant comment gérer les événements provenant de divers services tels qu'Amazon API GatewaySQS, Amazon et Amazon Kinesis. Ces fonctions utilisent la dernière version de la aws-lambda-java-eventsbibliothèque (3.0.0 et versions ultérieures). Ces exemples ne nécessitent pas le AWS SDK comme dépendance.

  • s3-java — Fonction Java qui traite les événements de notification provenant d'Amazon S3 et utilise la bibliothèque de classes Java (JCL) pour créer des miniatures à partir de fichiers image téléchargés.

  • Utilisez API Gateway pour appeler une fonction Lambda : fonction Java qui analyse une table Amazon DynamoDB contenant des informations sur les employés. Il utilise ensuite Amazon Simple Notification Service pour envoyer un message texte aux employés qui fêtent leur anniversaire professionnel. Cet exemple utilise API Gateway pour appeler la fonction.

Les s3-java applications java-events et prennent un événement AWS de service en entrée et renvoient une chaîne. L’application java-basic comprend plusieurs types de gestionnaires :

Pour tester différents types de gestionnaires, il suffit de modifier la valeur du gestionnaire dans le AWS SAM modèle. Pour obtenir des instructions détaillées, consultez le fichier readme de l’exemple d’application.