Definir el controlador de las funciones de Lambda en Java - AWS Lambda

Definir el controlador de las funciones de Lambda en Java

El controlador de la función de Lambda es el método del código de la función que procesa eventos. Cuando se invoca una función, Lambda ejecuta el método del controlador. La función se ejecuta hasta que el controlador devuelve una respuesta, se cierra o se agota el tiempo de espera.

El repositorio de GitHub para esta guía contiene aplicaciones de ejemplo fáciles de implementar en las que se muestran diferentes tipos de controladores. Para obtener más información, consulte el final de este tema.

Ejemplo de controlador: tiempos de ejecución de Java 17

En el siguiente ejemplo de Java 17, una clase llamada HandlerIntegerJava17 define un método de controlador denominado handleRequest. El método del controlador incluye las siguientes entradas:

  • Un IntegerRecord, que es un registro de Java personalizado que representa los datos de eventos. En este ejemplo, definimos IntegerRecord de la siguiente manera:

    record IntegerRecord(int x, int y, String message) { }
  • Un objeto de contexto, que proporciona métodos y propiedades que brindan información sobre la invocación, la función y el entorno de ejecución.

Supongamos que queremos escribir una función que registre el message de la entrada IntegerRecord y devuelva la suma de x y y. El siguiente es el código de la función:

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

Usted especifica qué método desea que invoque Lambda al establecer el parámetro del controlador en la configuración de su función. Puede expresar el controlador en los siguientes formatos:

  • package.Class::method: formato completo. Por ejemplo: example.Handler::handleRequest.

  • package.Class: formato abreviado para las clases que implementan una interfaz de controlador. Por ejemplo: example.Handler.

Cuando Lambda invoca su controlador, el tiempo de ejecución de Lambda recibe un evento como una cadena con formato JSON y lo convierte en un objeto. En el ejemplo anterior, un evento de ejemplo podría verse así:

ejemplo evento.json
{ "x": 1, "y": 20, "message": "Hello World!" }

Puede guardar este archivo y probar la función de forma local con el siguiente comando de AWS Command Line Interface (CLI):

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

Ejemplo de controlador: tiempos de ejecución de Java 11 e inferiores

Lambda solo admite registros de Java 17 y tiempos de ejecución posteriores. En todos los entornos de ejecución de Java, puede usar una clase para representar los datos de eventos. En el siguiente ejemplo, se toma una lista de números enteros y un objeto de contexto como entrada y se devuelve la suma de todos los números enteros de la lista.

ejemplo Handler.java

En el siguiente ejemplo, la clase Handler define un método de controlador llamado handleRequest. El método handler toma un evento y un objeto contextual como entrada y devuelve una cadena.

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

Para ver más ejemplos, consulte Ejemplo de código de controlador.

Código de inicialización

Lambda ejecuta su código estático y el constructor de clases durante la fase de inicialización antes de invocar la función por primera vez. Los recursos que se crean durante la inicialización permanecen en la memoria entre las invocaciones y el controlador puede reutilizarlos miles de veces. Por lo tanto, puede agregar un código de inicialización fuera de su método de controlador principal para ahorrar tiempo de cómputo y reutilizar recursos en varias invocaciones.

En el siguiente ejemplo, el código de inicialización del cliente está fuera del método del controlador principal. El tiempo de ejecución inicializa el cliente antes de que la función publique su primer evento. Los eventos posteriores son mucho más rápidos porque Lambda no necesita volver a inicializar el cliente.

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

Elección de los tipos de entrada y salida

Especifique el tipo de objeto al que se asigna el evento en la firma del método del controlador. En el ejemplo anterior, el entorno de ejecución de Java deserializa el evento en un tipo que implementa la interfaz Map<String,String>. Los mapas de cadena a cadena funcionan para eventos planos como los siguientes:

ejemplo Event.json: datos del tiempo
{ "temperatureK": 281, "windKmh": -3, "humidityPct": 0.55, "pressureHPa": 1020 }

Sin embargo, el valor de cada campo debe ser una cadena o un número. Si el evento contiene un campo que tiene un objeto como valor, el entorno de ejecución no puede deserializarlo y devuelve un error.

Elija un tipo de entrada que funcione con los datos del evento que procesa la función. Puede utilizar un tipo básico, un tipo genérico o un tipo bien definido.

Tipos de entrada
  • Integer, Long, Double, etc.: - El evento es un número sin formato adicional; por ejemplo, 3.5. El entorno de ejecución convierte el valor en un objeto del tipo especificado.

  • String: el evento es una cadena JSON, incluidas las comillas; por ejemplo, "My string.". El tiempo de ejecución convierte el valor (sin comillas) en un objeto String.

  • Type, Map<String,Type>, etc.: - El evento es un objeto JSON. El entorno de ejecución lo deserializa en un objeto del tipo especificado o una interfaz.

  • List<Integer>, List<String>, List<Object>, etc.: - El evento es una matriz JSON. El entorno de ejecución lo deserializa en un objeto del tipo especificado o una interfaz.

  • InputStream: el evento es cualquier tipo JSON. El entorno de ejecución pasa una secuencia de bytes del documento al controlador sin modificaciones. Usted deserializa la entrada y escribe la salida en una secuencia de salida.

  • Tipo de biblioteca: en el caso de los eventos enviados por los servicios de AWS, utilice los tipos de la biblioteca aws-lambda-java-events.

Si define su propio tipo de entrada, debería ser un objeto Java estándar (POJO) mutable y deserializable, con un constructor predeterminado y unas propiedades predeterminadas para cada campo del evento. Las claves del evento que no se asignan a una propiedad, así como las propiedades que no están incluidas en el evento, se eliminan sin errores.

El tipo de salida puede ser un objeto o void. El entorno de ejecución serializa los valores devueltos en texto. Si la salida es un objeto con campos, el entorno de ejecución lo serializa en un documento JSON. Si se trata de un tipo que encapsula un valor primitivo, el tiempo de ejecución devuelve una representación de texto de ese valor.

Interfaces de controlador

La biblioteca aws-lambda-java-core define dos interfaces para los métodos de controlador. Utilice las interfaces proporcionadas para simplificar la configuración del controlador y validar la firma del método del controlador en tiempo de compilación.

La interfaz RequestHandler es un tipo genérico que toma dos parámetros: el tipo de entrada y el tipo de salida. Los dos tipos deben ser objetos. Cuando se utiliza esta interfaz, el entorno de ejecución de Java deserializa el evento en un objeto con el tipo de entrada y serializa la salida en texto. Utilice esta interfaz si la serialización integrada funcione con los tipos de entrada y salida.

ejemplo Handler.java: interfaz del controlador
// Handler value: example.Handler public class Handler implements RequestHandler<Map<String,String>, String>{ @Override public String handleRequest(Map<String,String> event, Context context)

Para usar su propia serialización, implemente la interfaz RequestStreamHandler. Con esta interfaz, Lambda pasa al controlador un flujo de entrada y otrp flujo de salida. El controlador lee los bytes de la secuencia de entrada, escribe en la secuencia de salida y devuelve void.

En el ejemplo siguiente se utilizan unos tipos de lectores y escritores almacenados en el búfer para trabajar con las secuencias de entrada y salida.

ejemplo HandlerStream.java
import com.amazonaws.services.lambda.runtime.Context import com.amazonaws.services.lambda.runtime.LambdaLogger import com.amazonaws.services.lambda.runtime.RequestStreamHandler ... // Handler value: example.HandlerStream public class HandlerStream implements RequestStreamHandler { @Override /* * Takes an InputStream and an OutputStream. Reads from the InputStream, * and copies all characters to the OutputStream. */ public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException { LambdaLogger logger = context.getLogger(); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("US-ASCII"))); PrintWriter writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(outputStream, Charset.forName("US-ASCII")))); int nextChar; try { while ((nextChar = reader.read()) != -1) { outputStream.write(nextChar); } } catch (IOException e) { e.printStackTrace(); } finally { reader.close(); String finalString = writer.toString(); logger.log("Final string result: " + finalString); writer.close(); } } }

Ejemplo de código del controlador

El repositorio de GitHub para esta guía contiene aplicaciones de ejemplo en las que se muestra el uso de diferentes tipos de controladores e interfaces. Cada una de las aplicaciones de ejemplo contiene scripts para facilitar la implementación y la limpieza, una plantilla de AWS SAM y recursos de soporte.

Aplicaciones de Lambda de ejemplo en Java
  • java17-examples: una función de Java que demuestra cómo utilizar un registro de Java para representar un objeto de datos de eventos de entrada.

  • java-basic: una colección de funciones de Java mínimas con pruebas unitarias y configuración de registro variable.

  • java-events: una colección de funciones Java que contiene un código básico sobre cómo gestionar los eventos de varios servicios, como Amazon API Gateway, Amazon SQS y Amazon Kinesis. Estas funciones utilizan la última versión de la biblioteca aws-lambda-java-events (3.0.0 y más recientes). Estos ejemplos no requieren utilizar AWS SDK como una dependencia.

  • s3-java: una función de Java que procesa los eventos de notificación de Amazon S3 y utiliza Java Class Library (JCL) para crear miniaturas de los archivos de imagen cargados.

  • Uso de API Gateway para invocar una función de Lambda: una función Java que escanea una tabla de Amazon DynamoDB que contiene información sobre los empleados. Luego, utiliza Amazon Simple Notification Service para enviar un mensaje de texto a los empleados que celebran sus aniversarios laborales. En este ejemplo, se utiliza API Gateway para invocar la función.

Las aplicaciones java-events y s3-java toman un evento de un servicio de AWS como entrada y devuelven una cadena. La aplicación java-basic contiene varios tipos de controladores:

Para probar diferentes tipos de controlador, solo tiene que cambiar el valor del controlador en la plantilla de AWS SAM. Para obtener instrucciones detalladas, consulte el archivo readme (léame) de la aplicación de ejemplo.