

# Lambda 托管实例运行时
<a name="lambda-managed-instances-runtimes"></a>

使用 Lambda 托管实例时，Lambda 处理请求的方式有所不同。与在每个执行环境中依次处理请求不同，Lambda 托管实例会在每个执行环境中同时处理多个请求。执行模型的这一变化意味着使用 Lambda 托管实例的函数需要考虑线程安全、状态管理和上下文隔离等问题，而这些在 Lambda（默认）单并发模式中是不存在的。此外，不同运行时中的多并发实施方式各有不同。

## 支持的语言
<a name="lambda-managed-instances-supported-runtimes"></a>

Lambda 托管实例可以与以下编程语言和运行时一起使用：
+ **Java：**Java 21 及更高版本。
+ **Python：**Python 3.13 及更高版本。
+ **Node.js：**Node.js 22 及更高版本。
+ **.NET：**.NET 8 及更高版本。
+ **Rust：**支持使用仅限操作系统的运行时 `provided.al2023` 及更高版本。

## 服务特定注意事项
<a name="lambda-managed-instances-runtime-considerations"></a>

每种编程语言实现多并发的方式不同。您需要了解如何使用所选编程语言实施多并发，以便应用相应的并发最佳实践。

**Java**

采用单一进程并结合操作系统线程来实现并发。多个线程会同时执行处理程序方法，因此需要对状态和共享资源进行线程安全的处理。

**Python**

使用多个 Python 进程，在这些进程中，每个并发请求都在单独的进程中运行。这能有效避免大多数并发问题，但对于 `/tmp` 目录这类共享资源则仍需要小心处理。

**Node.js**

使用具有异步执行功能的[工作线程](https://nodejs.org/api/worker_threads.html)。并发请求分布在工作线程中，而每个工作线程还可以异步处理并发请求，这就需要对状态和共享资源进行安全的处理。

**.NET**

使用 .NET 任务实现对多个并发请求的异步处理。需要对状态和共享资源进行安全处理。

**Rust**

使用由 [Tokio](https://tokio.rs/) 支持的带有异步任务的单一进程。处理程序必须是 `Clone` \$1 `Send`。

## 后续步骤
<a name="lambda-managed-instances-runtime-next-steps"></a>

有关每个运行时的详细信息，请参阅以下主题：
+ [Lambda 托管实例的 Java 运行时](lambda-managed-instances-java-runtime.md)
+ [Lambda 托管实例的 Node.js 运行时](lambda-managed-instances-nodejs-runtime.md)
+ [Lambda 托管实例的 Python 运行时](lambda-managed-instances-python-runtime.md)
+ [Lambda 托管实例的 .NET 运行时](lambda-managed-instances-dotnet-runtime.md)
+ [Rust 对 Lambda 托管实例的支持](lambda-managed-instances-rust.md)

# Lambda 托管实例的 Java 运行时
<a name="lambda-managed-instances-java-runtime"></a>

对于 Java 运行时，Lambda 托管实例使用操作系统线程来实现并发。在初始化过程中，Lambda 会为每个执行环境加载一次您的处理程序对象，然后创建多个线程。这些线程并行执行，并且需要对状态和共享资源进行线程安全的处理。每个线程共享同一个处理程序对象以及任何静态字段。

## 并发配置
<a name="lambda-managed-instances-java-concurrency-config"></a>

Lambda 向每个执行环境发送的最大并发请求数由函数配置中的 `PerExecutionEnvironmentMaxConcurrency` 设置控制。这是一项可选设置，其默认值因运行时而异。对于 Java 运行时而言，其默认设置为每个 vCPU 32 个并发请求，或者您也可以自行配置其他数值。该值还决定了 Java 运行时所使用的线程数量。Lambda 会根据每个执行环境吸收这些请求的容量，自动调整并发请求的数量，最高到配置的最大值。

## 为多并发构建函数
<a name="lambda-managed-instances-java-building"></a>

在使用 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 目录
<a name="lambda-managed-instances-java-shared-tmp"></a>

`/tmp` 目录在执行环境中为所有并发请求共享使用。对同一个文件进行并发写入可能会导致数据损坏，例如，如果另一个进程覆盖了该文件。要解决这个问题，要么为共享文件实施文件锁定机制，要么根据每个线程或每次请求使用唯一的文件名以避免冲突。记得清理不再需要的文件，以免耗尽可用空间。

## 日志记录
<a name="lambda-managed-instances-java-logging"></a>

在多并发系统中，日志交错（即来自不同请求的日志条目在日志中交错排列）是常见现象。

使用 Lambda 托管实例的函数始终使用[高级日志记录控制](monitoring-logs.md#monitoring-cloudwatchlogs-advanced)引入的结构化 JSON 日志格式。此格式包括 `requestId`，使得日志条目能够与单个请求相关联。当您使用 `context.getLogger()` 中的 `LambdaLogger` 对象时，`requestId` 会自动包含在每个日志条目中。有关更多信息，请参阅 [在 Java 中使用 Lambda 高级日志记录控件](java-logging.md#java-logging-advanced)。

## 请求上下文
<a name="lambda-managed-instances-java-request-context"></a>

`context` 对象将与请求线程捆绑在一起。使用 `context.getAwsRequestId()` 将提供对当前请求的请求 ID 的线程安全性访问权限。

使用 `context.getXrayTraceId()` 访问 X-Ray 跟踪 ID。这为当前请求的跟踪 ID 提供了线程安全的访问权限。Lambda 不支持将 `_X_AMZN_TRACE_ID` 环境变量用于 Lambda 托管实例。使用 AWS SDK 时，X-Ray 跟踪 ID 会自动传播。

使用 `com.amazonaws.services.lambda.runtime.Context.getRemainingTimeInMillis()` 检测超时。请参阅[错误处理和恢复](lambda-managed-instances-execution-environment.md#lambda-managed-instances-error-handling)了解更多信息。

如果您在程序中使用虚拟线程或在初始化期间创建线程，则需要将任何必要的请求上下文传递给这些线程。

## 初始化和关闭
<a name="lambda-managed-instances-java-init-shutdown"></a>

函数初始化会在每个执行环境中发生一次。初始化期间创建的对象在线程之间共享。

对于带有扩展程序的 Lambda 函数，其执行环境在关闭时会发出一个 SIGTERM 信号。扩展程序使用此信号来触发清理任务，例如刷新缓冲区。您可以订阅 SIGTERM 事件来触发函数清理任务，例如关闭数据库连接。要了解有关执行环境生命周期的更多信息，请参阅 [了解 Lambda 执行环境生命周期](lambda-runtime-environment.md)。

## 依赖项版本
<a name="lambda-managed-instances-java-dependencies"></a>

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)
<a name="lambda-managed-instances-java-powertools"></a>

适用于 AWS Lambda 的 Powertools（Java）与 Lambda 托管实例兼容，并提供用于日志记录、跟踪、指标等的实用工具。有关更多信息，请参阅[适用于 AWS Lambda 的 Powertools（Java）](https://github.com/aws-powertools/powertools-lambda-java)。

## 后续步骤
<a name="lambda-managed-instances-java-next-steps"></a>
+ 查看 [Lambda 托管实例的 Node.js 运行时](lambda-managed-instances-nodejs-runtime.md)
+ 查看 [Lambda 托管实例的 Python 运行时](lambda-managed-instances-python-runtime.md)
+ 查看 [Lambda 托管实例的 .NET 运行时](lambda-managed-instances-dotnet-runtime.md)
+ 了解有关[扩展 Lambda 托管实例](lambda-managed-instances-scaling.md)的信息

# Lambda 托管实例的 Node.js 运行时
<a name="lambda-managed-instances-nodejs-runtime"></a>

对于 Node.js 运行时而言，Lambda 托管实例采用基于 `async`/`await` 的执行的工作线程来处理并发请求。函数初始化会在每个工作线程中发生一次。并发调用在两个维度上得到处理：工作线程在各个 vCPU 上实现并行处理，而异步执行则在每个线程内部实现并发。由同一个工作线程处理的每个并发请求都共享同一个处理程序对象和全局状态，因此需要在多个并发请求下进行安全处理。

## 最大并发数
<a name="lambda-managed-instances-nodejs-max-concurrency"></a>

Lambda 向每个执行环境发送的最大并发请求数由函数配置中的 `PerExecutionEnvironmentMaxConcurrency` 设置控制。这是一项可选设置，其默认值因运行时而异。对于 Node.js 运行时而言，其默认设置为每个 vCPU 64 个并发请求，或者您也可以自行配置其他数值。Lambda 会根据每个执行环境吸收这些请求的容量，自动调整并发请求的数量，最高到配置的最大值。

对于 Node.js，每个执行环境能够处理的并发请求数量取决于工作线程的数量以及每个工作线程异步处理并发请求的能力。工作线程的默认数量由可用的 vCPU 数量决定，或者您可以通过设置 `AWS_LAMBDA_NODEJS_WORKER_COUNT` 环境变量来配置工作线程的数量。我们建议使用异步函数处理程序，因为这样可以允许每个工作线程处理多个请求。如果您的函数处理程序是同步的，则每个工作线程一次只能处理一个请求。

## 为多并发构建函数
<a name="lambda-managed-instances-nodejs-building"></a>

通过异步函数处理程序，每个运行时工作进程能够同时处理多个请求。全局对象将在多个并发请求之间进行共享。对于可变对象，请避免使用全局状态或使用 `AsyncLocalStorage`。

AWS SDK 客户端是异步安全的，且不需要特殊处理。

**示例：全局状态**

以下代码使用了一个在函数处理程序内部发生突变的全局对象。该对象不具备异步安全性。

```
let state = {
    currentUser: null,
    requestData: null
};

export const handler = async (event, context) => {
    state.currentUser = event.userId;
    state.requestData = event.data;

    await processData(state.requestData);

    // state.currentUser might now belong to a different request
    return { user: state.currentUser };
};
```

在函数处理程序内初始化 `state` 对象可以避免共享全局状态。

```
export const handler = async (event, context) => {
    let state = {
        currentUser: event.userId,
        requestData: event.data
    };
    
    await processData(state.requestData);

    return { user: state.currentUser };
};
```

**示例：数据库连接**

以下代码使用一个共享的客户端对象，该对象在多个调用之间共享。根据使用的连接库，这可能并不具备并发安全性。

```
const { Client } = require('pg');

// Single connection created at init time
const client = new Client({
  host: process.env.DB_HOST,
  database: process.env.DB_NAME,
  user: process.env.DB_USER,
  password: process.env.DB_PASSWORD
});

// Connect once during cold start
client.connect();

exports.handler = async (event) => {
  // Multiple parallel invocations share this single connection = BAD
  // With multi-concurrent Lambda, queries will collide
  const result = await client.query('SELECT * FROM users WHERE id = $1', [event.userId]);
  
  return {
    statusCode: 200,
    body: JSON.stringify(result.rows[0])
  };
};
```

并发安全的方法是使用连接池。该池对每个并发数据库查询使用单独的连接。

```
const { Pool } = require('pg');

// Connection pool created at init time
const pool = new Pool({
  host: process.env.DB_HOST,
  database: process.env.DB_NAME,
  user: process.env.DB_USER,
  password: process.env.DB_PASSWORD,
  max: 20,  // Max connections in pool
  idleTimeoutMillis: 30000,
  connectionTimeoutMillis: 2000
});

exports.handler = async (event) => {
  // Pool gives each parallel invocation its own connection
  const result = await pool.query('SELECT * FROM users WHERE id = $1', [event.userId]);
  
  return {
    statusCode: 200,
    body: JSON.stringify(result.rows[0])
  };
};
```

## Node.js 22 基于回调的处理程序
<a name="lambda-managed-instances-nodejs-callback-handlers"></a>

使用 Node.js 22 时，您不能对 Lambda 托管实例使用基于回调的函数处理程序。只有 Lambda（默认）函数才支持基于回调的处理程序。对于 Node.js 24 及更高版本的运行时，基于回调的函数处理程序在 Lambda（默认）和 Lambda 托管实例中均已不受支持。

在使用 Lambda 托管实例时，请改用 `async` 函数处理程序。有关更多信息，请参阅[在 Node.js 中的定义 Lambda 函数处理程序](https://docs.aws.amazon.com/lambda/latest/dg/nodejs-handler.html)。

## 共享的 /tmp 目录
<a name="lambda-managed-instances-nodejs-shared-tmp"></a>

`/tmp` 目录在执行环境中为所有并发请求共享使用。对同一个文件进行并发写入可能会导致数据损坏，例如，如果另一个进程覆盖了该文件。要解决这个问题，要么为共享文件实施文件锁定机制，要么根据每次请求使用唯一的文件名以避免冲突。记得清理不再需要的文件，以免耗尽可用空间。

## 日志记录
<a name="lambda-managed-instances-nodejs-logging"></a>

在多并发系统中，日志交错（即来自不同请求的日志条目在日志中交错排列）是常见现象。使用 Lambda 托管实例的函数始终使用[高级日志记录控制](monitoring-logs.md#monitoring-cloudwatchlogs-advanced)引入的结构化 JSON 日志格式。此格式包括 `requestId`，使得日志条目能够与单个请求相关联。当您使用 `console` 记录器时，`requestId` 会自动包含在每个日志条目中。有关更多信息，请参阅 [在 Node.js 中使用 Lambda 高级日志记录控件](nodejs-logging.md#node-js-logging-advanced)。

常见的第三方日志库（例如 [Winston](https://github.com/winstonjs/winston)）通常支持通过控制台进行日志输出。

## 请求上下文
<a name="lambda-managed-instances-nodejs-request-context"></a>

使用 `context.awsRequestId` 将提供对当前请求的请求 ID 的异步安全性访问权限。

使用 `context.xRayTraceId` 访问 X-Ray 跟踪 ID。这为当前请求的跟踪 ID 提供了并发安全的访问权限。Lambda 不支持将 `_X_AMZN_TRACE_ID` 环境变量用于 Lambda 托管实例。使用 AWS SDK 时，X-Ray 跟踪 ID 会自动传播。

使用 `context.getRemainingTimeInMillis()` 检测超时。请参阅[错误处理和恢复](lambda-managed-instances-execution-environment.md#lambda-managed-instances-error-handling)了解更多信息。

## 初始化和关闭
<a name="lambda-managed-instances-nodejs-init-shutdown"></a>

函数初始化会在每个工作线程中发生一次。如果您的函数在初始化过程中发出日志，您可能会看到重复的日志条目。

对于带有扩展程序的 Lambda 函数，其执行环境在关闭时会发出一个 SIGTERM 信号。扩展程序使用此信号来触发清理任务，例如刷新缓冲区。带有扩展程序的 Lambda（默认）函数也可以使用 `process.on()` 订阅 SIGTERM 信号。使用 Lambda 托管实例的函数不支持此功能，因为 `process.on()` 不能与工作线程一起使用。要了解有关执行环境生命周期的更多信息，请参阅 [了解 Lambda 执行环境生命周期](lambda-runtime-environment.md)。

## 依赖项版本
<a name="lambda-managed-instances-nodejs-dependencies"></a>

Lambda 托管实例需要以下最低程序包版本：
+ AWS SDK for JavaScript v3：版本 3.933.0 或更高版本
+ AWS X-Ray SDK for Node.js：版本 3.12.0 或更高版本
+ 适用于 OpenTelemetry 的 AWS Distro - 适用于 JavaScript 的检测工具：版本 0.8.0 或更高版本
+ 适用于 AWS Lambda 的 Powertools（TypeScript）：版本 2.29.0 或更高版本

## Powertools for AWS Lambda (TypeScript)
<a name="lambda-managed-instances-nodejs-powertools"></a>

适用于 AWS Lambda 的 Powertools（TypeScript）与 Lambda 托管实例兼容，并提供用于日志记录、跟踪、指标等的实用工具。有关更多信息，请参阅[适用于 AWS Lambda 的 Powertools（TypeScript）](https://github.com/aws-powertools/powertools-lambda-typescript)。

## 后续步骤
<a name="lambda-managed-instances-nodejs-next-steps"></a>
+ 查看 [Lambda 托管实例的 Java 运行时](lambda-managed-instances-java-runtime.md)
+ 查看 [Lambda 托管实例的 Python 运行时](lambda-managed-instances-python-runtime.md)
+ 查看 [Lambda 托管实例的 .NET 运行时](lambda-managed-instances-dotnet-runtime.md)
+ 了解有关[扩展 Lambda 托管实例](lambda-managed-instances-scaling.md)的信息

# Lambda 托管实例的 Python 运行时
<a name="lambda-managed-instances-python-runtime"></a>

Lambda 运行时使用多个 Python 进程来处理并发请求。每个并发请求都在单独的进程中运行，且拥有自己的内存空间和初始化环境。每个进程一次只处理一个请求，并且同步进行。进程之间不会直接共享内存，因此全局变量、模块级别的缓存以及单例对象会在并发请求之间保持独立。

## 并发配置
<a name="lambda-managed-instances-python-concurrency-config"></a>

Lambda 向每个执行环境发送的最大并发请求数由函数配置中的 `PerExecutionEnvironmentMaxConcurrency` 设置控制。这是一项可选设置，其默认值因运行时而异。对于 Python 运行时而言，其默认设置为每个 vCPU 16 个并发请求，或者您也可以自行配置其他数值。该值还决定了 Python 运行时所使用的进程数量。Lambda 会根据每个执行环境吸收这些请求的容量，自动调整并发请求的数量，最高到配置的最大值。

**重要**  
使用基于进程的并发意味着每个运行时工作进程都会自行进行初始化操作。总内存使用量等于每个进程的内存占用量乘以并发进程的数量。如果您正在加载大型库或数据集，并且具有高并发数，则您将会占用大量内存。根据您的工作负载，您可能需要调整 CPU 与内存的比例，或者降低并发设置，以避免超出可用内存的容量。您可以在 CloudWatch 中使用 `MemoryUtilization` 指标来跟踪内存消耗情况。

## 为多并发构建函数
<a name="lambda-managed-instances-python-building"></a>

由于采用了基于进程的多并发模型，使用 Python 运行时的 Lambda 托管实例函数不会从多个调用中同时访问内存中的资源。您无需为了内存中的并发安全来应用编码实践。

## 共享的 /tmp 目录
<a name="lambda-managed-instances-python-shared-tmp"></a>

`/tmp` 目录在执行环境中为所有并发请求共享使用。对同一个文件进行并发写入可能会导致数据损坏，例如，如果另一个进程覆盖了该文件。要解决这个问题，要么为共享文件实施文件锁定机制，要么根据每个进程或每次请求使用唯一的文件名以避免冲突。记得清理不再需要的文件，以免耗尽可用空间。

## 日志记录
<a name="lambda-managed-instances-python-logging"></a>

在多并发系统中，日志交错（即来自不同请求的日志条目在日志中交错排列）是常见现象。

使用 Lambda 托管实例的函数始终使用[高级日志记录控制](monitoring-logs.md#monitoring-cloudwatchlogs-advanced)引入的结构化 JSON 日志格式。此格式包括 `requestId`，使得日志条目能够与单个请求相关联。当您在 Lambda 中使用 Python 标准库中的 `logging` 模块时，`requestId` 会自动包含在每个日志条目中。有关更多信息，请参阅[将 Lambda 高级日志记录控制与 Python 结合使用](https://docs.aws.amazon.com/lambda/latest/dg/python-logging.html#python-logging-advanced)。

## 请求上下文
<a name="lambda-managed-instances-python-request-context"></a>

使用 `context.aws_request_id` 访问当前请求的请求 ID。

使用 Python 运行时，您可以通过 `_X_AMZN_TRACE_ID` 环境变量来获取 Lambda 托管实例的 X-Ray 跟踪 ID。使用 AWS SDK 时，X-Ray 跟踪 ID 会自动传播。

使用 `context.get_remaining_time_in_millis()` 检测超时。请参阅[错误处理和恢复](lambda-managed-instances-execution-environment.md#lambda-managed-instances-error-handling)了解更多信息。

## 初始化和关闭
<a name="lambda-managed-instances-python-init-shutdown"></a>

函数初始化会在每个进程中发生一次。如果您的函数在初始化过程中发出日志，您可能会看到重复的日志条目。

对于带有扩展程序的 Lambda 函数，其执行环境在关闭时会发出一个 SIGTERM 信号。扩展程序使用此信号来触发清理任务，例如刷新缓冲区。您可以订阅 SIGTERM 事件来触发函数清理任务，例如关闭数据库连接。要了解有关执行环境生命周期的更多信息，请参阅 [了解 Lambda 执行环境生命周期](lambda-runtime-environment.md)。

## 依赖项版本
<a name="lambda-managed-instances-python-dependencies"></a>

Lambda 托管实例需要以下最低程序包版本：
+ 适用于 AWS Lambda 的 Powertools（Python）：版本 3.23.0 或更高版本

## Powertools for AWS Lambda (Python)
<a name="lambda-managed-instances-python-powertools"></a>

适用于 AWS Lambda 的 Powertools（Python）与 Lambda 托管实例兼容，并提供用于日志记录、跟踪、指标等的实用工具。有关更多信息，请参阅[适用于 AWS Lambda 的 Powertools（Python）](https://github.com/aws-powertools/powertools-lambda-python)。

## 后续步骤
<a name="lambda-managed-instances-python-next-steps"></a>
+ 查看 [Lambda 托管实例的 Java 运行时](lambda-managed-instances-java-runtime.md)
+ 查看 [Lambda 托管实例的 Node.js 运行时](lambda-managed-instances-nodejs-runtime.md)
+ 查看 [Lambda 托管实例的 .NET 运行时](lambda-managed-instances-dotnet-runtime.md)
+ 了解有关[扩展 Lambda 托管实例](lambda-managed-instances-scaling.md)的信息

# Lambda 托管实例的 .NET 运行时
<a name="lambda-managed-instances-dotnet-runtime"></a>

对于 .NET 运行时，Lambda 托管实例在每个执行环境中使用单个 .NET 进程。使用 .NET 任务处理多个并发请求。

## 并发配置
<a name="lambda-managed-instances-dotnet-concurrency-config"></a>

Lambda 向每个执行环境发送的最大并发请求数由函数配置中的 `PerExecutionEnvironmentMaxConcurrency` 设置控制。这是一项可选设置，其默认值因运行时而异。对于 .NET 运行时而言，其默认设置为每个 vCPU 32 个并发请求，或者您也可以自行配置其他数值。Lambda 会根据每个执行环境吸收这些请求的容量，自动调整并发请求的数量，最高到配置的最大值。

## 为多并发构建函数
<a name="lambda-managed-instances-dotnet-building"></a>

在使用 Lambda 托管实例时，您应像在任何其他多并发环境中一样，采用相同的并发安全措施。由于处理程序对象在所有任务中是共享的，因此任何可变状态都必须是线程安全的。这包括集合、数据库连接以及在请求处理过程中被修改的任何静态对象。

AWS SDK 客户端是线程安全的，且不需要特殊处理。

**示例：数据库连接池**

以下代码使用静态的数据库连接对象，该对象在并发请求之间共享。`SqlConnection` 对象不是线程安全的。

```
public class DBQueryHandler
{
    // Single connection shared across threads - NOT SAFE
    private SqlConnection connection;

    public DBQueryHandler()
    {
        connection = new SqlConnection("your-connection-string-here");
        connection.Open();
    }

    public string Handle(object input, ILambdaContext context)
    {
        using var cmd = connection.CreateCommand();
        cmd.CommandText = "SELECT ..."; // your query

        using var reader = cmd.ExecuteReader();

        ...
    }
}
```

要解决这个问题，请为每个请求使用从连接池中获取的单独连接。打开连接对象时，`Microsoft.Data.SqlClient` 之类的 ADO.NET 提供程序会自动支持连接池。

```
public class DBQueryHandler
{
    public DBQueryHandler()
    {
    }

    public string Handle(object input, ILambdaContext context)
    {
        using var connection = new SqlConnection("your-connection-string-here");
        connection.Open();
        using var cmd = connection.CreateCommand();
        cmd.CommandText = "SELECT ..."; // your query

        using var reader = cmd.ExecuteReader();

        ...
    }
}
```

**示例：集合**

标准的 .NET 集合不是线程安全的：

```
public class Handler
{
    private static List<string> items = new List<string>();
    private static Dictionary<string, object> cache = new Dictionary<string, object>();

    public string FunctionHandler(object input, ILambdaContext context)
    {
        items.Add(context.AwsRequestId);
        cache["key"] = input;

        return "Success";
    }
}
```

使用 `System.Collections.Concurrent` 命名空间中的集合以确保并发安全：

```
public class Handler
{
    private static ConcurrentBag<string> items = new ConcurrentBag<string>();
    private static ConcurrentDictionary<string, object> cache = new ConcurrentDictionary<string, object>();

    public string FunctionHandler(object input, ILambdaContext context)
    {
        items.Add(context.AwsRequestId);
        cache["key"] = input;

        return "Success";
    }
}
```

## 共享的 /tmp 目录
<a name="lambda-managed-instances-dotnet-shared-tmp"></a>

`/tmp` 目录在执行环境中为所有并发请求共享使用。对同一个文件进行并发写入可能会导致数据损坏，例如，如果另一个请求覆盖了该文件。要解决这个问题，要么为共享文件实施文件锁定机制，要么根据每次请求使用唯一的文件名以避免冲突。记得清理不再需要的文件，以免耗尽可用空间。

## 日志记录
<a name="lambda-managed-instances-dotnet-logging"></a>

在多并发系统中，日志交错（即来自不同请求的日志条目在日志中交错排列）是常见现象。使用 Lambda 托管实例的函数始终使用[高级日志记录控制](monitoring-logs.md#monitoring-cloudwatchlogs-advanced)引入的结构化 JSON 日志格式。此格式包括 `requestId`，使得日志条目能够与单个请求相关联。当您使用 `context.Logger` 对象生成日志时，`requestId` 会自动包含在每个日志条目中。有关更多信息，请参阅 [将 Lambda 高级日志记录控件与 .NET 结合使用](csharp-logging.md#csharp-logging-advanced)。

## 请求上下文
<a name="lambda-managed-instances-dotnet-request-context"></a>

使用 `context.AwsRequestId` 属性访问当前请求的请求 ID。

使用 `context.TraceId` 属性访问 X-Ray 跟踪 ID。这为当前请求的跟踪 ID 提供了并发安全的访问权限。Lambda 不支持将 `_X_AMZN_TRACE_ID` 环境变量用于 Lambda 托管实例。使用 AWS SDK 时，X-Ray 跟踪 ID 会自动传播。

使用 `ILambdaContext.RemainingTime` 检测超时。请参阅[错误处理和恢复](lambda-managed-instances-execution-environment.md#lambda-managed-instances-error-handling)了解更多信息。

## 初始化和关闭
<a name="lambda-managed-instances-dotnet-init-shutdown"></a>

函数初始化会在每个执行环境中发生一次。初始化期间创建的对象在请求之间共享。

对于带有扩展程序的 Lambda 函数，其执行环境在关闭时会发出一个 SIGTERM 信号。扩展程序使用此信号来触发清理任务，例如刷新缓冲区。您可以订阅 SIGTERM 事件来触发函数清理任务，例如关闭数据库连接。要了解有关执行环境生命周期的更多信息，请参阅 [了解 Lambda 执行环境生命周期](lambda-runtime-environment.md)。

## 依赖项版本
<a name="lambda-managed-instances-dotnet-dependencies"></a>

Lambda 托管实例需要以下最低程序包版本：
+ Amazon.Lambda.Core：版本 2.7.1 或更高版本
+ Amazon.Lambda.RuntimeSupport：版本 1.14.1 或更高版本
+ OpenTelemetry.Instrumentation.AWSLambda：版本 1.14.0 或更高版本
+ AWSXRayRecorder.Core：版本 2.16.0 或更高版本
+ AWSSDK.Core：版本 4.0.0.32 或更高版本

## Powertools for AWS Lambda (.NET)
<a name="lambda-managed-instances-dotnet-powertools"></a>

[适用于 AWS Lambda (.NET) 的 Powertools](https://docs.aws.amazon.com/powertools/dotnet/)和 [适用于 OpenTelemetry 的 AWS Distro - 适用于 .Net 的检测工具](https://github.com/aws-observability/aws-otel-dotnet-instrumentation)目前不支持 Lambda 托管实例。

## 后续步骤
<a name="lambda-managed-instances-dotnet-next-steps"></a>
+ 查看 [Lambda 托管实例的 Java 运行时](lambda-managed-instances-java-runtime.md)
+ 查看 [Lambda 托管实例的 Node.js 运行时](lambda-managed-instances-nodejs-runtime.md)
+ 查看 [Lambda 托管实例的 Python 运行时](lambda-managed-instances-python-runtime.md)
+ 了解有关[扩展 Lambda 托管实例](lambda-managed-instances-scaling.md)的信息

# Rust 对 Lambda 托管实例的支持
<a name="lambda-managed-instances-rust"></a>

## 并发配置
<a name="lambda-managed-instances-rust-concurrency-config"></a>

Lambda 向每个执行环境发送的最大并发请求数由函数配置中的 `PerExecutionEnvironmentMaxConcurrency` 设置控制。这是一个可选设置，Rust 的默认设置为每个 vCPU 8 个并发请求，您也可以自行配置其他数值。该值决定运行时产生的 Tokio 任务数量，在执行环境的生命周期内保持不变。每个工作线程一次仅处理一个进行中的请求，且每个工作线程不存在多路复用。Lambda 会根据每个执行环境吸收这些请求的容量，自动调整并发请求的数量，最高到配置的最大值。

## 为多并发构建函数
<a name="lambda-managed-instances-rust-building"></a>

在使用 Lambda 托管实例时，您应像在任何其他多线程环境中一样，采用相同的线程安全措施。由于处理程序对象在所有工作线程中是共享的，因此任何可变状态都必须是线程安全的。这包括集合、数据库连接以及在请求处理过程中被修改的任何静态对象。

要启用并发请求处理，请在 `Cargo.toml` 文件中添加 `concurrency-tokio` 功能标志。

```
[dependencies]  
lambda_runtime = { version = "1", features = ["concurrency-tokio"] }
```

`lambda_runtime::run_concurrent(…)` 入口点必须在 Tokio 运行时中调用，通常由主函数的 `#[tokio::main]` 属性提供。您的处理程序闭包必须实现 [https://doc.rust-lang.org/std/clone/trait.Clone.html](https://doc.rust-lang.org/std/clone/trait.Clone.html)\$1 [https://doc.rust-lang.org/std/marker/trait.Send.html](https://doc.rust-lang.org/std/marker/trait.Send.html)。这允许框架在多个异步任务中安全地共享处理程序。如果未满足这些限制，则不会编译您的代码。

当您需要在多次调用之间共享状态（例如数据库连接池、配置结构体）时，请将其封装在 [https://doc.rust-lang.org/std/sync/struct.Arc.html](https://doc.rust-lang.org/std/sync/struct.Arc.html) 中，并将 `Arc` 克隆到每次调用中。

所有 Rust 客户端的 AWS SDK 都是并发安全的，不需要特殊处理。

### 示例：AWS SDK 客户端
<a name="lambda-managed-instances-rust-example-sdk"></a>

以下示例使用 S3 客户端在每次调用时上传对象。客户端直接克隆到闭包中，而不需要 `Arc`：

```
let config = aws_config::load_defaults(BehaviorVersion::latest()).await;  
let s3_client = aws_sdk_s3::Client::new(&config);  
  
run_concurrent(service_fn(move |event: LambdaEvent<Request>| {  
    let s3_client = s3_client.clone(); // cheap clone, no Arc needed  
    async move {  
        s3_client.put_object()  
            .bucket(&event.payload.bucket)  
            .key(&event.payload.key)  
            .body(event.payload.body.into_bytes().into())  
            .send()  
            .await?;  
        Ok(Response { message: "uploaded".into() })  
    }  
}))  
.await
```

### 示例：数据库连接池
<a name="lambda-managed-instances-rust-example-db"></a>

当您的处理程序需要访问共享状态（例如客户端和配置）时，请将其封装在 [https://doc.rust-lang.org/std/sync/struct.Arc.html](https://doc.rust-lang.org/std/sync/struct.Arc.html) 中，并将 `Arc` 克隆到每次调用中：

```
#[derive(Debug)]  
struct AppState {  
    dynamodb_client: DynamoDbClient,  
    table_name: String,  
    cache_ttl: Duration,  
}  
  
let config = aws_config::load_defaults(BehaviorVersion::latest()).await;  
let state = Arc::new(AppState {  
    dynamodb_client: DynamoDbClient::new(&config),  
    table_name: std::env::var("TABLE_NAME").expect("TABLE_NAME must be set"),  
    cache_ttl: Duration::from_secs(300),  
});  
  
run_concurrent(service_fn(move |event: LambdaEvent<Request>| {  
    let state = state.clone();  
    async move { handle(event, state).await }  
}))  
.await
```

## 共享的 /tmp 目录
<a name="lambda-managed-instances-rust-tmp"></a>

`/tmp` 目录在执行环境中为所有并发调用共享使用。每次调用使用唯一的文档名（如包含请求 ID）或实施显式文档锁定，以避免数据损坏。

## 日志记录
<a name="lambda-managed-instances-rust-logging"></a>

在多并发系统中，日志交错（即来自不同请求的日志条目在日志中交错排列）是常见现象。通过 Lambda 的[高级日志控制](monitoring-logs.md#monitoring-cloudwatchlogs-advanced)，使用 Lambda 托管实例的函数支持结构化 JSON 日志格式。此格式包括 `requestId`，使得日志条目能够与单个请求相关联。有关更多信息，请参阅 [使用 Tracing crate 实现高级日志记录](rust-logging.md#rust-logging-tracing)。

## 请求上下文
<a name="lambda-managed-instances-rust-context"></a>

`Context` 对象直接传递给每个处理程序调用。使用 `event.context.request_id` 访问当前请求的请求 ID。

使用 `event.context.xray_trace_id` 访问 X-Ray 跟踪 ID。Lambda 不支持将 `_X_AMZN_TRACE_ID` 环境变量用于 Lambda 托管实例。使用 AWS SDK for Rust 时，X-Ray 跟踪 ID 会自动传播。

使用 `event.context.deadline` 检测超时 — 它包含以毫秒为单位的调用截止日期。

## 初始化和关闭
<a name="lambda-managed-instances-rust-lifecycle"></a>

函数初始化会在每个执行环境中发生一次。初始化期间创建的对象在请求之间共享。

对于带有扩展程序的 Lambda 函数，其执行环境在关闭时会发出一个 SIGTERM 信号。扩展程序使用此信号来触发清理任务，例如刷新缓冲区。 `lambda_runtime` 提供了一个辅助工具来简化优雅关闭信号处理的配置，即 [https://docs.rs/lambda_runtime/latest/lambda_runtime/fn.spawn_graceful_shutdown_handler.html](https://docs.rs/lambda_runtime/latest/lambda_runtime/fn.spawn_graceful_shutdown_handler.html)。要了解有关执行环境生命周期的更多信息，请参阅 [了解 Lambda 执行环境生命周期](lambda-runtime-environment.md)。

## 依赖项版本
<a name="lambda-managed-instances-rust-dependencies"></a>

Lambda 托管实例需要以下最低程序包版本：
+ `lambda_runtime`: 版本 1.1.1 或更高版本，已启用 `concurrency-tokio` 功能
+ 支持的最低 Rust 版本 (MSRV) 为 1.84.0。