Lambda 托管实例的 Java 运行时
对于 Java 运行时,Lambda 托管实例使用操作系统线程来实现并发。在初始化过程中,Lambda 会为每个执行环境加载一次您的处理程序对象,然后创建多个线程。这些线程并行执行,并且需要对状态和共享资源进行线程安全的处理。每个线程共享同一个处理程序对象以及任何静态字段。
并发配置
Lambda 向每个执行环境发送的最大并发请求数由函数配置中的 PerExecutionEnvironmentMaxConcurrency 设置控制。这是一项可选设置,其默认值因运行时而异。对于 Java 运行时而言,其默认设置为每个 vCPU 32 个并发请求,或者您也可以自行配置其他数值。该值还决定了 Java 运行时所使用的线程数量。Lambda 会根据每个执行环境吸收这些请求的容量,自动调整并发请求的数量,最高到配置的最大值。
为多并发构建函数
在使用 Lambda 托管实例时,您应像在任何其他多线程环境中一样,采用相同的线程安全措施。由于处理程序对象在所有运行时工作线程中是共享的,因此任何可变状态都必须是线程安全的。这包括集合、数据库连接以及在请求处理过程中被修改的任何静态对象。
AWS SDK 客户端是线程安全的,且不需要特殊处理。
示例:数据库连接池
以下代码使用静态的数据库连接对象,该对象在线程之间共享。根据使用的连接库,这可能并不具备线程安全性。
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(); } }
线程安全的方法是使用连接池。在以下示例中,函数处理程序从连接池中检索一个连接。该连接仅在单个请求的上下文中使用。
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(); } }
示例:集合
标准的 Java 集合不是线程安全的:
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"; } }
使用线程安全的集合替代之:
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"; } }
共享的 /tmp 目录
/tmp 目录在执行环境中为所有并发请求共享使用。对同一个文件进行并发写入可能会导致数据损坏,例如,如果另一个进程覆盖了该文件。要解决这个问题,要么为共享文件实施文件锁定机制,要么根据每个线程或每次请求使用唯一的文件名以避免冲突。记得清理不再需要的文件,以免耗尽可用空间。
日志记录
在多并发系统中,日志交错(即来自不同请求的日志条目在日志中交错排列)是常见现象。
使用 Lambda 托管实例的函数始终使用高级日志记录控制引入的结构化 JSON 日志格式。此格式包括 requestId,使得日志条目能够与单个请求相关联。当您使用 context.getLogger() 中的 LambdaLogger 对象时,requestId 会自动包含在每个日志条目中。有关更多信息,请参阅将 Lambda 高级日志记录控制与 Java 结合使用。
请求上下文
context 对象将与请求线程捆绑在一起。使用 context.getAwsRequestId() 将提供对当前请求的请求 ID 的线程安全性访问权限。
使用 context.getXrayTraceId() 访问 X-Ray 跟踪 ID。这为当前请求的跟踪 ID 提供了线程安全的访问权限。Lambda 不支持将 _X_AMZN_TRACE_ID 环境变量用于 Lambda 托管实例。使用 AWS SDK 时,X-Ray 跟踪 ID 会自动传播。
如果您在程序中使用虚拟线程或在初始化期间创建线程,则需要将任何必要的请求上下文传递给这些线程。
初始化和关闭
函数初始化会在每个执行环境中发生一次。初始化期间创建的对象在线程之间共享。
对于带有扩展程序的 Lambda 函数,其执行环境在关闭时会发出一个 SIGTERM 信号。扩展程序使用此信号来触发清理任务,例如刷新缓冲区。您可以订阅 SIGTERM 事件来触发函数清理任务,例如关闭数据库连接。要了解有关执行环境生命周期的更多信息,请参阅了解 Lambda 执行环境生命周期。
依赖项版本
Lambda 托管实例需要以下最低程序包版本:
-
AWS SDK for Java 2.0:版本 2.34.0 或更高版本
-
AWS X-Ray SDK for Java:版本 2.20.0 或更高版本
-
适用于 OpenTelemetry 的 AWS Distro - 适用于 Java 的检测工具:版本 2.20.0 或更高版本
-
适用于 AWS Lambda 的 Powertools(Java):版本 2.8.0 或更高版本
Powertools for AWS Lambda (Java)
适用于AWS Lambda 的 Powertools(Java)与 Lambda 托管实例兼容,并提供用于日志记录、跟踪、指标等的实用工具。有关更多信息,请参阅适用于 AWS Lambda 的 Powertools(Java)