Tiempo de ejecución de Java para instancias administradas de Lambda - AWS Lambda

Tiempo de ejecución de Java para instancias administradas de Lambda

En el caso de los tiempos de ejecución de Java, las instancias administradas de Lambda utilizan subprocesos del sistema operativo para la concurrencia. Lambda carga el objeto controlador una vez por entorno de ejecución durante la inicialización y, a continuación, crea varios subprocesos. Estos subprocesos se ejecutan en paralelo y requieren una gestión segura de subprocesos de los recursos compartidos y los estados. Cada subproceso comparte el mismo objeto controlador y todos los campos estáticos.

Configuración de concurrencia

El número máximo de solicitudes simultáneas que envía Lambda a cada entorno de ejecución se controla mediante el ajuste PerExecutionEnvironmentMaxConcurrency de la configuración de la función. Se trata de una configuración opcional y el valor predeterminado varía en función del tiempo de ejecución. Para los tiempos de ejecución de Java, el valor predeterminado es de 32 solicitudes simultáneas por vCPU, o puede configurar su propio valor. Este valor también determina el número de subprocesos utilizados por el tiempo de ejecución de Java. Lambda ajusta automáticamente el número de solicitudes simultáneas hasta el máximo configurado en función de la capacidad de cada entorno de ejecución para absorber esas solicitudes.

Creación de funciones para la concurrencia múltiple

Debe aplicar las mismas prácticas de seguridad de subprocesos cuando utilice instancias administradas de Lambda que en cualquier otro entorno de subprocesos múltiples. Como el objeto controlador se comparte entre todos los subprocesos de trabajo en tiempo de ejecución, cualquier estado mutable debe ser seguro para subprocesos. Esto incluye las colecciones, las conexiones a bases de datos y cualquier objeto estático que se modifique durante el procesamiento de la solicitud.

Los clientes de AWS SDK son seguros para subprocesos y no requieren una gestión especial.

Ejemplo: grupos de conexiones a bases de datos

El código siguiente utiliza un objeto de conexión de base de datos estático que se comparte entre subprocesos. Según la biblioteca de conexiones utilizada, es posible que no sea segura para subprocesos.

public class DBQueryHandler implements RequestHandler<Object, String> { // Single connection shared across all threads - NOT SAFE private static Connection connection; public DBQueryHandler() { this.connection = DriverManager.getConnection(jdbcUrl, username, password); } @Override public String handleRequest(Object input, Context context) { PreparedStatement stmt = connection.prepareStatement(query); ResultSet rs = stmt.executeQuery(); // Multiple threads using same connection causes issues return result.toString(); } }

Un enfoque seguro para subprocesos consiste en utilizar un grupo de conexiones. En el siguiente ejemplo, el controlador de funciones recupera una conexión del grupo. La conexión solo se utiliza en el contexto de una única solicitud.

public class DBQueryHandler implements RequestHandler<Object, String> { private static HikariDataSource dataSource; public DBQueryHandler() { HikariConfig config = new HikariConfig(); config.setJdbcUrl("jdbc:mysql://localhost:3306/your_database"); dataSource = new HikariDataSource(config); // Create pool once per Lambda container } @Override public String handleRequest(Object input, Context context) { String query = "SELECT column_name FROM your_table LIMIT 10"; StringBuilder result = new StringBuilder("Data:\n"); // try-with-resources automatically calls close() on the connection, // which returns it to the HikariCP pool (does NOT close the physical DB connection) try (Connection connection = dataSource.getConnection(); PreparedStatement stmt = connection.prepareStatement(query); ResultSet rs = stmt.executeQuery()) { while (rs.next()) { result.append(rs.getString("column_name")).append("\n"); } } catch (Exception e) { context.getLogger().log("Error: " + e.getMessage()); return "Error"; } return result.toString(); } }

Ejemplo: colecciones

Las recopilaciones estándar de Java no son seguras para los subprocesos:

public class Handler implements RequestHandler<Object, String> { private static List<String> items = new ArrayList<>(); private static Map<String, Object> cache = new HashMap<>(); @Override public String handleRequest(Object input, Context context) { items.add("list item"); // Not thread-safe cache.put("key", input); // Not thread-safe return "Success"; } }

En su lugar, utilice colecciones seguras para subprocesos:

public class Handler implements RequestHandler<Object, String> { private static final List<String> items = Collections.synchronizedList(new ArrayList<>()); private static final ConcurrentHashMap<String, Object> cache = new ConcurrentHashMap<>(); @Override public String handleRequest(Object input, Context context) { items.add("list item"); // Thread-safe cache.put("key", input); // Thread-safe return "Success"; } }

Directorio /tmp compartido

El directorio /tmp se comparte entre todas las solicitudes simultáneas del entorno de ejecución. Las escrituras simultáneas en el mismo archivo pueden dañar los datos, por ejemplo, si otro proceso sobrescribe el archivo. Para solucionar este problema, implemente el bloqueo de archivos para los archivos compartidos o utilice nombres de archivo únicos por subproceso o por solicitud para evitar conflictos. Recuerde limpiar los archivos innecesarios para no agotar el espacio disponible.

Registro

El intercalado de registros (las entradas de registro de diferentes solicitudes se intercalan en los registros) es normal en los sistemas simultáneos múltiples.

Las funciones que utilizan instancias administradas de Lambda utilizan siempre el formato de registro JSON estructurado introducido con los controles de registro avanzados. Este formato incluye el requestId, lo que permite correlacionar las entradas de registro con una sola solicitud. Cuando utiliza el objeto LambdaLogger de context.getLogger(), el requestId se incluye automáticamente en cada entrada de registro. Para obtener más información, consulte Uso de los controles de registro avanzados de Lambda con Java.

El contexto de la solicitud

El objeto context está enlazado al subproceso de solicitud. El uso de context.getAwsRequestId() proporciona un acceso seguro para subprocesos al identificador de solicitud de la solicitud actual.

Utilice el context.getXrayTraceId() para acceder al identificador de seguimiento de X-Ray. Esto proporciona un acceso seguro para subprocesos al identificador de seguimiento de la solicitud actual. Lambda no admite la variable de entorno _X_AMZN_TRACE_ID con las instancias administradas de Lambda. El identificador de seguimiento de X-Ray se propaga automáticamente cuando se utiliza AWS SDK.

Si utiliza subprocesos virtuales en su programa o crea subprocesos durante la inicialización, tendrá que pasar cualquier contexto de solicitud necesario a estos subprocesos.

Inicialización y apagado

La inicialización de la función se produce una vez por entorno de ejecución. Los objetos creados durante la inicialización se comparten entre los subprocesos.

En el caso de las funciones de Lambda con extensiones, el entorno de ejecución emite una señal SIGTERM durante el cierre. Las extensiones utilizan esta señal para desencadenar tareas de limpieza, como vaciar los búferes. Puede suscribirse a los eventos SIGTERM para desencadenar tareas de limpieza de funciones, como cerrar las conexiones a las bases de datos. Para obtener más información sobre el ciclo de vida del entorno de ejecución, consulte Descripción del ciclo de vida del entorno de ejecución de Lambda.

Versiones de dependencias

Las instancias administradas de Lambda requieren las siguientes versiones de paquete como mínimo:

  • AWS SDK para Java 2.0: versión 2.34.0 o posterior

  • AWS X-Ray SDK para Java: versión 2.20.0 o posterior

  • AWS Distro para OpenTelemetry, instrumentación para Java: versión 2.20.0 o posterior

  • Powertools para AWS Lambda (Java): versión 2.8.0 o posterior

Powertools para AWS Lambda (Java)

Powertools para AWS Lambda (Java) es compatible con las instancias administradas de Lambda y proporciona utilidades para el registro, el seguimiento, las métricas y mucho más. Para obtener más información, consulte Powertools para AWS Lambda (Java).

Siguientes pasos