

# 使用 Rust 构建 Lambda 函数
<a name="lambda-rust"></a>

由于 Rust 可编译为原生代码，因而无需专用运行时系统即可在 Lambda 上运行 Rust 代码。因此，请使用 [Rust 运行时系统客户端](https://github.com/aws/aws-lambda-rust-runtime)在本地生成项目，然后使用[仅 OS 运行时](runtimes-provided.md)将其部署到 Lambda。使用仅限操作系统的运行时时，Lambda 会自动使用最新补丁，确保操作系统保持更新状态。

**适用于 Rust 的工具和库**
+ [适用于 Rust 的 AWS SDK](https://docs.aws.amazon.com/sdk-for-rust/latest/dg/getting-started.html)：适用于 Rust 的 AWS SDK 提供用于与 Amazon Web Services 基础设施服务进行交互的 Rust API。
+  [适用于 Lambda 的 Rust 运行时客户端](https://github.com/aws/aws-lambda-rust-runtime)：Rust 运行时客户端可以轻松运行用 Rust 编写的 Lambda 函数。
+ [Cargo Lambda](https://www.cargo-lambda.info/guide/what-is-cargo-lambda.html)：这是 Cargo 命令行工具的第三方开源扩展程序，可简化 Rust Lambda 函数的构建和部署过程。
+ [Lambda HTTP](https://github.com/aws/aws-lambda-rust-runtime/tree/main/lambda-http)：此库提供包装程序，以处理 HTTP 事件。
+  [Lambda 扩展](https://github.com/aws/aws-lambda-rust-runtime/tree/main/lambda-extension)：此库支持使用 Rust 写入 Lambda 扩展。
+ [AWS Lambda 事件](https://crates.io/crates/aws_lambda_events)：此库提供常见事件源集成的类型定义。

**适用于 Rust 的 Lambda 应用程序示例**
+ [基本 Lambda 函数](https://github.com/aws/aws-lambda-rust-runtime/blob/main/examples/basic-lambda)：此 Rust 函数可演示如何处理基本事件。
+ [具有错误处理功能的 Lambda 函数](https://github.com/aws/aws-lambda-rust-runtime/blob/main/examples/basic-error-handling)：此 Rust 函数可演示如何在 Lambda 中处理自定义 Rust 错误。
+ [具有共享资源的 Lambda 函数](https://github.com/aws/aws-lambda-rust-runtime/blob/main/examples/basic-shared-resource)：此 Rust 项目用于初始化共享资源然后再创建 Lambda 函数。
+ [Lambda HTTP 事件](https://github.com/aws/aws-lambda-rust-runtime/blob/main/examples/http-basic-lambda)：此 Rust 函数可处理 HTTP 事件。
+ [包含 CORS 标头的 Lambda HTTP 事件](https://github.com/aws/aws-lambda-rust-runtime/blob/main//examples/http-cors)：此 Rust 函数使用 Tower 注入 CORS 标头。
+ [Lambda REST API](https://github.com/aws/aws-lambda-rust-runtime/tree/main/examples/http-axum-diesel)：此 REST API 可通过 Axum 和 Diesel 连接到 PostgreSQL 数据库。
+ [无服务器 Rust 演示](https://github.com/aws-samples/serverless-rust-demo/)：此 Rust 项目可演示如何使用 Lambda 的 Rust 库、日志记录、环境变量和 AWS SDK。
+ [基本 Lambda 扩展](https://github.com/aws/aws-lambda-rust-runtime/blob/main/examples/extension-basic)：此 Rust 扩展可演示如何处理基本扩展事件。
+ [Lambda Logs Amazon Data Firehose 扩展](https://github.com/aws/aws-lambda-rust-runtime/blob/main/examples/extension-logs-kinesis-firehose)：此 Rust 扩展可演示如何将 Lambda 日志发送到 Firehose。

**Topics**
+ [定义 Rust Lambda 函数处理程序](rust-handler.md)
+ [使用 Lambda 上下文对象检索 Rust 函数信息](rust-context.md)
+ [使用 Rust 处理 HTTP 事件](rust-http-events.md)
+ [使用 .zip 文件存档部署 Rust Lambda 函数](rust-package.md)
+ [使用 Rust Lambda 函数的层](rust-layers.md)
+ [Rust Lambda 函数日志记录和监控](rust-logging.md)

# 定义 Rust Lambda 函数处理程序
<a name="rust-handler"></a>

Lambda 函数*处理程序*是函数代码中处理事件的方法。当调用函数时，Lambda 运行处理程序方法。您的函数会一直运行，直到处理程序返回响应、退出或超时。

本页介绍如何使用 Rust Lambda 函数处理程序，包括项目初始化、命名约定和最佳实践。本页还包括 Rust Lambda 函数的示例，在示例中该函数接收订单信息，生成文本文件收据，然后将此文件放入 Amazon Simple Storage Service（S3）存储桶中。有关如何在编写函数后部署函数的信息，请参阅[使用 .zip 文件存档部署 Rust Lambda 函数](rust-package.md)。

**Topics**
+ [设置 Rust 处理程序项目](#rust-handler-setup)
+ [示例 Rust Lambda 函数代码](#rust-example-code)
+ [Rust 处理程序的有效类定义](#rust-handler-signatures)
+ [处理程序命名约定](#rust-example-naming)
+ [定义和访问输入事件对象](#rust-handler-input)
+ [访问和使用 Lambda 上下文对象](#rust-example-context)
+ [在处理程序中使用 适用于 Rust 的 AWS SDK](#rust-example-sdk-usage)
+ [评估环境变量](#rust-example-envvars)
+ [使用共享状态](#rust-shared-state)
+ [Rust Lambda 函数的代码最佳实践](#rust-best-practices)

## 设置 Rust 处理程序项目
<a name="rust-handler-setup"></a>

使用 Rust Lambda 函数时，涉及编写代码、对其进行编译，以及将编译后的构件部署到 Lambda。在 Rust 中设置 Lambda 处理程序项目的最简单方法是使用[适用于 Rust 的 AWS Lambda 运行时](https://github.com/aws/aws-lambda-rust-runtime)。尽管名称如此，但适用于 Rust 的 AWS Lambda 运行时并不是托管运行时，与适用于 Python、Java 或 Node.js 的 Lambda 中的托管运行时不同。相反，适用于 Rust 的 AWS Lambda 运行时是一个 crate（`lambda_runtime`），支持在 Rust 中编写 Lambda 函数并与 AWS Lambda 的执行环境交互。

使用以下命令安装 [Cargo Lambda](https://www.cargo-lambda.info/guide/what-is-cargo-lambda.html)，它是 Cargo 命令行工具的第三方开源扩展程序，可简化 Rust Lambda 函数的构建和部署过程：

```
cargo install cargo-lambda
```

成功安装 `cargo-lambda` 后，使用以下命令初始化新的 Rust Lambda 函数处理程序项目：

```
cargo lambda new example-rust
```

当您运行此命令时，命令行界面（CLI）会询问您有关 Lambda 函数的几个问题：
+ **HTTP 函数** – 如果您打算通过 [API 网关](services-apigateway.md)或[函数 URL](urls-configuration.md) 调用您的函数，请回答**是**。否则，请回答**否**。在本页的示例代码中，我们使用自定义 JSON 事件来调用函数，因此我们回答**否**。
+ **事件类型** – 如果您打算使用预定义的事件形状来调用您的函数，请选择正确的预期事件类型。否则，请将此选项留空。在本页的示例代码中，我们使用了自定义 JSON 事件来调用函数，因此我们将此选项留空。

成功运行命令后，进入项目的主目录：

```
cd example-rust
```

此命令会在 `src` 目录中生成 `generic_handler.rs` 文件和 `main.rs` 文件。`generic_handler.rs` 可用于自定义通用事件处理程序。`main.rs` 文件包含您的主应用程序逻辑。`Cargo.toml` 文件包含有关您程序包的元数据并列出了其外部依赖项。

## 示例 Rust Lambda 函数代码
<a name="rust-example-code"></a>

以下示例 Rust Lambda 函数代码接收有关订单的信息，生成文本文件接收，并将此文件放入 Amazon S3 存储桶中。

**Example `main.rs` Lambda 函数**  

```
use aws_sdk_s3::{Client, primitives::ByteStream};
use lambda_runtime::{run, service_fn, Error, LambdaEvent};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::env;

#[derive(Deserialize, Serialize)]
struct Order {
    order_id: String,
    amount: f64,
    item: String,
}

async fn function_handler(event: LambdaEvent<Value>) -> Result<String, Error> {
    let payload = event.payload;

    // Deserialize the incoming event into Order struct
    let order: Order = serde_json::from_value(payload)?;

    let bucket_name = env::var("RECEIPT_BUCKET")
        .map_err(|_| "RECEIPT_BUCKET environment variable is not set")?;

    let receipt_content = format!(
        "OrderID: {}\nAmount: ${:.2}\nItem: {}",
        order.order_id, order.amount, order.item
    );
    let key = format!("receipts/{}.txt", order.order_id);

    let config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
    let s3_client = Client::new(&config);

    upload_receipt_to_s3(&s3_client, &bucket_name, &key, &receipt_content).await?;

    Ok("Success".to_string())
}

async fn upload_receipt_to_s3(
    client: &Client,
    bucket_name: &str,
    key: &str,
    content: &str,
) -> Result<(), Error> {
    client
        .put_object()
        .bucket(bucket_name)
        .key(key)
        .body(ByteStream::from(content.as_bytes().to_vec()))  // Fixed conversion
        .content_type("text/plain")
        .send()
        .await?;

    Ok(())
}

#[tokio::main]
async fn main() -> Result<(), Error> {
    run(service_fn(function_handler)).await
}
```

此 `main.rs` 文件包含以下代码部分：
+ `use` 语句：使用这些语句可导入 Lambda 函数所需的 Rust crate 和方法。
+ `#[derive(Deserialize, Serialize)]`：在此 Rust 结构中定义预期输入事件的形状。
+ `async fn function_handler(event: LambdaEvent<Value>) -> Result<String, Error>`：这是包含主应用程序逻辑的**主处理程序方法**。
+ `async fn upload_receipt_to_s3 (...)`：这是主 `function_handler` 方法引用的帮助程序方法。
+ `#[tokio::main]`：这是一个标记 Rust 程序入口点的宏。它还会设置一个 [Tokio 运行时](https://docs.rs/tokio/latest/tokio/runtime/index.html)，允许您的 `main()` 方法使用 `async`/`await` 并异步运行。
+ `async fn main() -> Result<(), Error>`：`main()` 函数是代码的入口点。在其中，我们指定 `function_handler` 为主处理程序方法。

### Cargo.toml 文件示例
<a name="rust-cargo-toml"></a>

此函数随附以下 `Cargo.toml` 文件。

```
[package]
name = "example-rust"
version = "0.1.0"
edition = "2024"

[dependencies]
aws-config = "1.5.18"
aws-sdk-s3 = "1.78.0"
lambda_runtime = "0.13.0"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tokio = { version = "1", features = ["full"] }
```

要使此函数正常运行，其[执行角色](lambda-intro-execution-role.md)必须允许 `s3:PutObject` 操作。此外，请确保您定义了 `RECEIPT_BUCKET` 环境变量。成功调用后，Amazon S3 存储桶应包含接收文件。

## Rust 处理程序的有效类定义
<a name="rust-handler-signatures"></a>

在大多数情况下，您在 Rust 中定义的 Lambda 处理程序签名将采用以下格式：

```
async fn function_handler(event: LambdaEvent<T>) -> Result<U, Error>
```

对于此处理程序：
+ 此处理程序的名称为 `function_handler`。
+ 处理程序的单一输入是事件，类型为 `LambdaEvent<T>`。
  + `LambdaEvent` 是 `lambda_runtime` crate 里的包装程序。使用此包装程序可以访问上下文对象，其中包括特定于 Lambda 的元数据，例如调用的请求 ID。
  + `T` 是反序列化事件类型。例如，它可以是 `serde_json::Value`，允许处理程序接收任何通用 JSON 输入。或者，如果您的函数需要特定的预定义输入类型，它也可以是像 `ApiGatewayProxyRequest` 这样的类型。
+ 处理程序的返回类型为 `Result<U, Error>`。
  + `U` 是反序列化的输出类型。`U` 必须实现 `serde::Serialize` 特性，这样 Lambda 才能将返回值转换为 JSON。例如，`U` 可以是像 `String`、`serde_json::Value` 这样的简单类型，也可以是自定义结构，前提是它实现了 `Serialize`。当您的代码到达 Ok(U) 语句时，表示成功执行，您的函数会返回一个 `U` 类型的值。
  + 当您的代码遇到错误（即 `Err(Error)`）时，您的函数会在 Amazon CloudWatch 中记录错误并返回 `Error` 类型的错误响应。

在我们的示例中，处理程序签名如下所示：

```
async fn function_handler(event: LambdaEvent<Value>) -> Result<String, Error>
```

其他有效的处理程序签名可能具有以下特点：
+ 省略 `LambdaEvent` 包装程序 – 如果省略 `LambdaEvent`，则无法访问函数内的 Lambda 上下文对象。以下是该签名类型的示例：

  ```
  async fn handler(event: serde_json::Value) -> Result<String, Error>
  ```
+ 使用单位类型作为输入 – 对于 Rust，您可以使用单位类型来表示空输入。这通常用于具有定期计划调用的函数。以下是该签名类型的示例：

  ```
  async fn handler(_: ()) -> Result<Value, Error>
  ```

## 处理程序命名约定
<a name="rust-example-naming"></a>

Rust 中的 Lambda 处理程序并没有严格的命名限制。尽管您可以为处理程序使用任何名称，但 Rust 中的函数名称通常采用 `snake_case`。

对于较小的应用程序，例如在该示例中，您可以使用单个 `main.rs` 文件来包含所有代码。对于较大的项目，`main.rs` 应包含函数的入口点，但您可以使用其他文件将代码分成逻辑模块。例如，您可以使用以下文件结构：

```
/example-rust
│── src/
│   ├── main.rs        # Entry point
│   ├── handler.rs     # Contains main handler
│   ├── services.rs    # [Optional] Back-end service calls
│   ├── models.rs      # [Optional] Data models
│── Cargo.toml
```

## 定义和访问输入事件对象
<a name="rust-handler-input"></a>

JSON 是 Lambda 函数最常用且最标准的输入格式。在此示例中，该函数需要类似于下方的输入：

```
{
    "order_id": "12345",
    "amount": 199.99,
    "item": "Wireless Headphones"
}
```

在 Rust 中，您可以在结构中定义预期输入事件的形状。在此示例中，我们定义了以下结构来表示 `Order`：

```
#[derive(Deserialize, Serialize)]
struct Order {
    order_id: String,
    amount: f64,
    item: String,
}
```

此结构与预期的输入形状相匹配。在此示例中，`#[derive(Deserialize, Serialize)]` 宏会自动生成用于序列化和反序列化的代码。这意味着我们可以使用 `serde_json::from_value()` 方法将通用输入 JSON 类型反序列化到我们的结构中。处理程序的前几行对此进行了说明：

```
async fn function_handler(event: LambdaEvent<Value>) -> Result<String, Error> {
    let payload = event.payload;

    // Deserialize the incoming event into Order struct
    let order: Order = serde_json::from_value(payload)?;
    ...
}
```

然后，您可以访问该对象的字段。例如，`order.order_id` 从原始输入中检索 `order_id` 的值。

### 预定义的输入事件类型
<a name="rust-input-event-types"></a>

`aws_lambda_events` crate 中有许多预定义的输入事件类型。例如，如果您打算使用 API 网关来调用您的函数，包括以下导入：

```
use aws_lambda_events::event::apigw::ApiGatewayProxyRequest;
```

请确保您的主处理程序使用了以下签名：

```
async fn handler(event: LambdaEvent<ApiGatewayProxyRequest>) -> Result<String, Error> {
    let body = event.payload.body.unwrap_or_default();
    ...
}
```

有关其他预定义输入事件类型的更多信息，请参阅 [aws\$1lambda\$1events crate](https://crates.io/crates/aws_lambda_events)。

## 访问和使用 Lambda 上下文对象
<a name="rust-example-context"></a>

Lambda [上下文对象](rust-context.md)包含有关调用、函数和执行环境的信息。在 Rust 中，`LambdaEvent` 包装程序包括上下文对象。例如，您可以使用上下文对象通过以下代码来检索当前调用的请求 ID：

```
async fn function_handler(event: LambdaEvent<Value>) -> Result<String, Error> {
    let request_id = event.context.request_id;
    ...
}
```

有关上下文对象的更多信息，请参阅[使用 Lambda 上下文对象检索 Rust 函数信息](rust-context.md)。

## 在处理程序中使用 适用于 Rust 的 AWS SDK
<a name="rust-example-sdk-usage"></a>

通常，您将使用 Lambda 函数与其他 AWS 资源进行交互或对其进行更新。与此类资源最简单的交互方法是使用 [适用于 Rust 的 AWS SDK](https://docs.aws.amazon.com/sdk-for-rust/latest/dg/welcome.html)。

要向函数添加 SDK 依赖项，请将其添加到 `Cargo.toml` 文件。建议仅添加函数所需的库。在上述示例代码中，我们使用了 `aws_sdk_s3::Client`。在 `Cargo.toml` 文件中，您可以通过在 `[dependencies]` 部分下添加以下行来添加此依赖项：

```
aws-sdk-s3 = "1.78.0"
```

**注意**  
这可能不是最新的版本。为您的应用程序选择合适的版本。

然后，直接在代码中导入这些依赖项：

```
use aws_sdk_s3::{Client, primitives::ByteStream};
```

示例代码随即按如下方法初始化 Amazon S3 客户端：

```
let config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
let s3_client = Client::new(&config);
```

初始化 SDK 客户端后，您可以使用该客户端与其他 AWS 服务进行交互。示例代码在 `upload_receipt_to_s3` 帮助函数中调用 Amazon S3 `PutObject` API。

## 评估环境变量
<a name="rust-example-envvars"></a>

在处理程序代码中，您可以使用 `env::var` 方法引用任何[环境变量](configuration-envvars.md)。在此示例中，我们使用以下代码行引用已定义的 `RECEIPT_BUCKET` 环境变量：

```
let bucket_name = env::var("RECEIPT_BUCKET")
    .map_err(|_| "RECEIPT_BUCKET environment variable is not set")?;
```

## 使用共享状态
<a name="rust-shared-state"></a>

您可以声明独立于 Lambda 函数的处理程序代码的共享变量。这些变量可以帮助您在函数接收任何事件之前的 [Init 阶段](lambda-runtime-environment.md#runtimes-lifecycle-ib) 期间加载状态信息。例如，您可以通过更新 `main` 函数和处理程序签名来修改此页面上的代码，以便在初始化 Amazon S3 客户端时使用共享状态：

```
async fn function_handler(client: &Client, event: LambdaEvent<Value>) -> Result<String, Error> {
    ...
    upload_receipt_to_s3(client, &bucket_name, &key, &receipt_content).await?;
    ...
}

...
      
#[tokio::main]
async fn main() -> Result<(), Error> {
    let shared_config = aws_config::from_env().load().await;
    let client = Client::new(&shared_config);
    let shared_client = &client;
    lambda_runtime::run(service_fn(move |event: LambdaEvent<Request>| async move {
        handler(&shared_client, event).await
    }))
    .await
```

## Rust Lambda 函数的代码最佳实践
<a name="rust-best-practices"></a>

在构建 Lambda 函数时，请遵循以下列表中的指南，采用最佳编码实践：
+ **从核心逻辑中分离 Lambda 处理程序。**这样您可以创建更容易进行单元测试的函数。
+ **将依赖关系的复杂性降至最低。**首选在[执行环境](lambda-runtime-environment.md)启动时可以快速加载的更简单的框架。
+ **将部署包大小精简为只包含运行时必要的部分。**这样会减少调用前下载和解压缩部署程序包所需的时间。

**利用执行环境重用来提高函数性能。**连接软件开发工具包 (SDK) 客户端和函数处理程序之外的数据库，并在 `/tmp` 目录中本地缓存静态资产。由函数的同一实例处理的后续调用可重用这些资源。这样就可以通过缩短函数运行时间来节省成本。

为了避免调用之间潜在的数据泄露，请不要使用执行环境来存储用户数据、事件或其他具有安全影响的信息。如果您的函数依赖于无法存储在处理程序的内存中的可变状态，请考虑为每个用户创建单独的函数或单独的函数版本。

**使用 keep-alive 指令来维护持久连接。**Lambda 会随着时间的推移清除空闲连接。在调用函数时尝试重用空闲连接会导致连接错误。要维护您的持久连接，请使用与运行时关联的 keep-alive 指令。有关示例，请参阅[在 Node.js 中通过 Keep-Alive 重用连接](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/node-reusing-connections.html)。

**使用[环境变量](configuration-envvars.md)将操作参数传递给函数。**例如，您在写入 Amazon S3 存储桶时，不应对要写入的存储桶名称进行硬编码，而应将存储桶名称配置为环境变量。

**避免在 Lambda 函数中使用递归调用**，在这种情况下，函数会调用自己或启动可能再次调用该函数的进程。这可能会导致意想不到的函数调用量和升级成本。如果您看到意外的调用量，请立即将函数保留并发设置为 `0` 来限制对函数的所有调用，同时更新代码。

Lambda 函数代码中**不要使用非正式的非公有 API**。对于 AWS Lambda 托管式运行时，Lambda 会定期为 Lambda 的内部 API 应用安全性和功能更新。这些内部 API 更新可能不能向后兼容，会导致意外后果，例如，假设您的函数依赖于这些非公有 API，则调用会失败。请参阅 [API 参考](https://docs.aws.amazon.com/lambda/latest/api/welcome.html)以查看公开发布的 API 列表。

**编写幂等代码。**为您的函数编写幂等代码可确保以相同的方式处理重复事件。您的代码应该正确验证事件并优雅地处理重复事件。有关更多信息，请参阅[如何使我的 Lambda 函数具有幂等性？](https://aws.amazon.com/premiumsupport/knowledge-center/lambda-function-idempotent/)。

# 使用 Lambda 上下文对象检索 Rust 函数信息
<a name="rust-context"></a>

当 Lambda 运行函数时，会将上下文对象添加到[处理程序](rust-handler.md)接收的 LambdaEvent 中。此对象提供的属性包含有关调用、函数和执行环境的信息。

**上下文属性**
+  `request_id`：由 Lambda 服务生成的 AWS 请求 ID。
+  `deadline`：当前调用的执行截止时间（以毫秒为单位）。
+  `invoked_function_arn`：正在调用的 Lambda 函数的 Amazon 资源名称（ARN）。
+  `xray_trace_id`：当前调用的 AWS X-Ray 跟踪 ID。
+  `client_content`：AWS Mobile SDK 发送的客户端上下文对象。除非使用 AWS Mobile SDK 调用函数，否则此字段为空。
+  `identity`：已调用函数的 Amazon Cognito 身份。除非使用由 Amazon Cognito 身份池颁发的 AWS 凭证向 Lambda API 发出调用请求，否则此字段为空。
+  `env_config`：来自本地环境变量的 Lambda 函数配置。此属性包括函数名称、内存分配、版本和日志流等信息。

## 访问调用上下文信息
<a name="rust-context-invoke"></a>

Lambda 函数可以访问有关其环境和调用请求的元数据。函数处理程序接收的 `LambaEvent` 对象包含 `context` 元数据：

```
use lambda_runtime::{service_fn, LambdaEvent, Error};
use serde_json::{json, Value};

async fn handler(event: LambdaEvent<Value>) -> Result<Value, Error> {
    let invoked_function_arn = event.context.invoked_function_arn;
    Ok(json!({ "message": format!("Hello, this is function {invoked_function_arn}!") }))
}

#[tokio::main]
async fn main() -> Result<(), Error> {
    lambda_runtime::run(service_fn(handler)).await
}
```

# 使用 Rust 处理 HTTP 事件
<a name="rust-http-events"></a>

Amazon API Gateway API、应用程序负载均衡器和 [Lambda 函数 URL](urls-configuration.md) 都可将 HTTP 事件发送到 Lambda。您可以使用 crates.io 中的 [aws\$1lambda\$1events](https://crates.io/crates/aws_lambda_events) crate 处理来自这些来源的事件。

**Example – 处理 API Gateway 代理请求**  
请注意以下几点：  
+ `use aws_lambda_events::apigw::{ApiGatewayProxyRequest, ApiGatewayProxyResponse}`：[aws\$1lambda\$1events](https://crates.io/crates/aws-lambda-events) crate 包含多个 Lambda 事件。要缩短编译时间，请使用功能标志激活所需事件。示例：`aws_lambda_events = { version = "0.8.3", default-features = false, features = ["apigw"] }`。
+ `use http::HeaderMap`：此导入要求您将 [http](https://crates.io/crates/http) crate 添加到依赖项中。

```
use aws_lambda_events::apigw::{ApiGatewayProxyRequest, ApiGatewayProxyResponse};
use http::HeaderMap;
use lambda_runtime::{service_fn, Error, LambdaEvent};

async fn handler(
    _event: LambdaEvent<ApiGatewayProxyRequest>,
) -> Result<ApiGatewayProxyResponse, Error> {
    let mut headers = HeaderMap::new();
    headers.insert("content-type", "text/html".parse().unwrap());
    let resp = ApiGatewayProxyResponse {
        status_code: 200,
        multi_value_headers: headers.clone(),
        is_base64_encoded: false,
        body: Some("Hello AWS Lambda HTTP request".into()),
        headers,
    };
    Ok(resp)
}

#[tokio::main]
async fn main() -> Result<(), Error> {
    lambda_runtime::run(service_fn(handler)).await
}
```

[适用于 Lambda 的 Rust 运行时系统客户端](https://github.com/aws/aws-lambda-rust-runtime)还提供对这些事件类型的抽象，允许您使用本机 HTTP 类型，无论哪个服务发送事件。以下代码等同于前面的示例，可以结合 Lambda 函数 URL、应用程序负载均衡器和 API Gateway 直接使用。

**注意**  
[lambda\$1http](https://crates.io/crates/lambda_http) crate 将使用下方的 [lambda\$1runtime](https://crates.io/crates/lambda_runtime) crate。无需单独导入 `lambda_runtime`。

**Example – 处理 HTTP 请求**  

```
use lambda_http::{service_fn, Error, IntoResponse, Request, RequestExt, Response};

async fn handler(event: Request) -> Result<impl IntoResponse, Error> {
    let resp = Response::builder()
        .status(200)
        .header("content-type", "text/html")
        .body("Hello AWS Lambda HTTP request")
        .map_err(Box::new)?;
    Ok(resp)
}

#[tokio::main]
async fn main() -> Result<(), Error> {
    lambda_http::run(service_fn(handler)).await
}
```

关于如何使用 `lambda_http` 的另一个示例，请参阅 AWS Labs GitHub 存储库上的 [http-axum 代码示例](https://github.com/aws/aws-lambda-rust-runtime/blob/main/examples/http-axum/src/main.rs)。

**适用于 Rust 的示例 HTTP Lambda 事件**
+ [Lambda HTTP 事件](https://github.com/aws/aws-lambda-rust-runtime/tree/main/examples/http-basic-lambda)：此 Rust 函数可处理 HTTP 事件。
+ [包含 CORS 标头的 Lambda HTTP 事件](https://github.com/aws/aws-lambda-rust-runtime/blob/main/examples/http-cors)：此 Rust 函数使用 Tower 注入 CORS 标头。
+ [具有共享资源的 Lambda HTTP 事件](https://github.com/aws/aws-lambda-rust-runtime/tree/main/examples/basic-shared-resource)：此 Rust 函数使用在创建函数处理程序之前初始化的共享资源。

# 使用 .zip 文件存档部署 Rust Lambda 函数
<a name="rust-package"></a>

本页将介绍如何编译 Rust 函数，然后使用 [Cargo Lambda](https://www.cargo-lambda.info/guide/what-is-cargo-lambda.html) 将已编译的二进制文件部署到 AWS Lambda。此外，还会显示如何使用 AWS Command Line Interface 和 AWS Serverless Application Model CLI 部署已编译的二进制文件。

**Topics**
+ [先决条件](#rust-package-prerequisites)
+ [在 macOS、Windows 或 Linux 上构建 Rust 函数](#rust-package-build)
+ [使用 Cargo Lambda 部署 Rust 函数二进制文件](#rust-deploy-cargo)
+ [使用 Cargo Lambda 调用 Rust 函数](#rust-invoke-function)

## 先决条件
<a name="rust-package-prerequisites"></a>
+ [Rust](https://www.rust-lang.org/tools/install)
+ [AWS CLI 版本 2](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)

## 在 macOS、Windows 或 Linux 上构建 Rust 函数
<a name="rust-package-build"></a>

以下步骤将演示如何使用 Rust 为您的第一个 Lambda 函数创建项目，并使用 [Cargo Lambda](https://www.cargo-lambda.info/) 对其进行编译，作为 Cargo 命令行工具的第三方开源扩展程序，Cargo Lambda 可简化 Rust Lambda 函数的构建和部署过程。

1. 安装 [Cargo Lambda](https://www.cargo-lambda.info/guide/what-is-cargo-lambda.html)，这是 Cargo 命令行工具的第三方开源扩展程序，可简化 Rust Lambda 函数的构建和部署过程。

   ```
   cargo install cargo-lambda
   ```

   有关其他安装选项，请参阅 Cargo Lambda 文档中的[安装](https://www.cargo-lambda.info/guide/installation.html)。

1. 创建程序包结构。此命令可在 `src/main.rs` 中创建一些基本的函数代码。您可以使用此代码进行测试，也可以将其替换为您自己的代码。

   ```
   cargo lambda new my-function
   ```

1. 在程序包的根目录中，运行 [build](https://www.cargo-lambda.info/commands/build.html) 子命令以编译函数中的代码。

   ```
   cargo lambda build --release
   ```

   （可选）如果要在 Lambda 上使用 AWS Graviton2，请添加 `--arm64` 标志以针对 ARM CPU 编译代码。

   ```
   cargo lambda build --release --arm64
   ```

1. 在部署 Rust 函数之前，请在计算机上配置 AWS 凭证。

   ```
   aws configure
   ```

## 使用 Cargo Lambda 部署 Rust 函数二进制文件
<a name="rust-deploy-cargo"></a>

使用 [deploy](https://www.cargo-lambda.info/commands/deploy.html) 子命令将已编译的二进制文件部署到 Lambda。此命令可创建[执行角色](lambda-intro-execution-role.md)，然后创建 Lambda 函数。要指定现有的执行角色，请使用 [--iam-role 标志](https://www.cargo-lambda.info/commands/deploy.html#iam-roles)。

```
cargo lambda deploy my-function
```

### 使用 AWS CLI 部署 Rust 函数二进制文件
<a name="rust-deploy-aws-cli"></a>

您也可以使用 AWS CLI 部署二进制文件。

1. 使用 [build](https://www.cargo-lambda.info/commands/build.html) 子命令构建 .zip 部署包。

   ```
   cargo lambda build --release --output-format zip
   ```

1. 要将 .zip 包部署到 Lambda，请运行 [create-function](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/create-function.html) 命令。
   + 对于 `--runtime`，请指定 `provided.al2023`。这是[仅限操作系统的运行时](runtimes-provided.md)。仅限操作系统的运行时用于将编译的二进制文件和自定义运行时部署到 Lambda。
   + 对于 `--role`，指定[执行角色](lambda-intro-execution-role.md)的 ARN。

   ```
   aws lambda create-function \
        --function-name my-function \
        --runtime provided.al2023 \
        --role arn:aws:iam::111122223333:role/lambda-role \
        --handler rust.handler \
        --zip-file fileb://target/lambda/my-function/bootstrap.zip
   ```

### 使用 AWS SAM CLI 部署 Rust 函数二进制文件
<a name="rust-deploy-sam-cli"></a>

您也可以使用 AWS SAM CLI 部署二进制文件。

1. 使用资源和属性定义创建 AWS SAM 模板。对于 `Runtime`，请指定 `provided.al2023`。这是[仅限操作系统的运行时](runtimes-provided.md)。仅限操作系统的运行时用于将编译的二进制文件和自定义运行时部署到 Lambda。

   有关使用 AWS SAM 部署 Lambda 函数的更多信息，请参阅《AWS Serverless Application Model 开发人员指南》**中的 [AWS::Serverless::Function](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html)。  
**Example Rust 二进制文件的 SAM 资源和属性定义**  

   ```
   AWSTemplateFormatVersion: '2010-09-09'
   Transform: AWS::Serverless-2016-10-31
   Description: SAM template for Rust binaries
   Resources:
     RustFunction:
       Type: AWS::Serverless::Function 
       Properties:
         CodeUri: target/lambda/my-function/
         Handler: rust.handler
         Runtime: provided.al2023
   Outputs:
     RustFunction:
       Description: "Lambda Function ARN"
       Value: !GetAtt RustFunction.Arn
   ```

1. 使用 [build](https://www.cargo-lambda.info/commands/build.html) 子命令编译函数。

   ```
   cargo lambda build --release
   ```

1. 使用 [sam deploy](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-cli-command-reference-sam-deploy.html) 命令将函数部署到 Lambda。

   ```
   sam deploy --guided
   ```

有关使用 AWS SAM CLI 构建 Rust 函数的更多信息，请参阅《AWS Serverless Application Model 开发人员指南》**中的[使用 Cargo Lambda 构建 Rust Lambda 函数](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/building-rust.html)。

## 使用 Cargo Lambda 调用 Rust 函数
<a name="rust-invoke-function"></a>

使用 [invoke](https://www.cargo-lambda.info/commands/invoke.html) 子命令，通过负载测试函数。

```
cargo lambda invoke --remote --data-ascii '{"command": "Hello world"}' my-function
```

### 使用 AWS CLI 调用 Rust 函数
<a name="rust-invoke-cli"></a>

您也可以使用 AWS CLI 调用函数。

```
aws lambda invoke --function-name my-function --cli-binary-format raw-in-base64-out --payload '{"command": "Hello world"}' /tmp/out.txt
```

如果使用 **cli-binary-format** 版本 2，则 AWS CLI 选项是必需的。要将其设为默认设置，请运行 `aws configure set cli-binary-format raw-in-base64-out`。有关更多信息，请参阅*版本 2 的 AWS Command Line Interface 用户指南*中的 [AWS CLI 支持的全局命令行选项](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-options.html#cli-configure-options-list)。

# 使用 Rust Lambda 函数的层
<a name="rust-layers"></a>

不建议使用[层](chapter-layers.md)来管理用 Rust 编写的 Lambda 函数的依赖项。这是因为 Rust 中的 Lambda 函数编译成单个可执行文件，您在部署函数时将其提供给 Lambda。这个可执行文件包含您编译的函数代码及其所有依赖项。使用层不仅会使此过程复杂化，还会导致冷启动时间增加，因为函数需要在初始化阶段将额外的程序集手动加载到内存中。

要在 Rust 处理程序中使用外部依赖项，请直接将其包含在部署包中。这样就可以简化部署过程，还可以利用内置的 Rust 编译器优化。有关如何在函数中导入和使用依赖项（如适用于 Rust 的 AWS SDK）的示例，请参阅[定义 Rust Lambda 函数处理程序](rust-handler.md)。

# Rust Lambda 函数日志记录和监控
<a name="rust-logging"></a>

AWS Lambda 将代表您自动监控 Lambda 函数并将日志记录发送至 Amazon CloudWatch。您的 Lambda 函数带有一个 CloudWatch Logs 日志组以及函数的每个实例的日志流。Lambda 运行时环境会将每个调用的详细信息发送到日志流，然后中继函数代码的日志和其他输出。有关更多信息，请参阅 [将 Lambda 函数日志发送到 CloudWatch Logs](monitoring-cloudwatchlogs.md)。有关配置日志格式的信息，请参阅 [配置 JSON 和纯文本日志格式](monitoring-cloudwatchlogs-logformat.md)。本页将介绍如何从 Lambda 函数的代码生成日志输出。

## 创建写入日志的函数
<a name="rust-logging-function"></a>

要从函数代码输出日志，您可以使用写入到 `stdout` 或 `stderr` 的任何日志记录函数，例如 `println!` 宏。以下示例使用 `println!` 在函数处理程序启动时和完成之前输出消息。

```
use lambda_runtime::{service_fn, LambdaEvent, Error};
use serde_json::{json, Value};
async fn handler(event: LambdaEvent<Value>) -> Result<Value, Error> {
    println!("Rust function invoked");
    let payload = event.payload;
    let first_name = payload["firstName"].as_str().unwrap_or("world");
    println!("Rust function responds to {}", &first_name);
    Ok(json!({ "message": format!("Hello, {first_name}!") }))
}

#[tokio::main]
async fn main() -> Result<(), Error> {
    lambda_runtime::run(service_fn(handler)).await
}
```

## 使用 Tracing crate 实现高级日志记录
<a name="rust-logging-tracing"></a>

[跟踪](https://crates.io/crates/tracing)是一个框架，用于分析 Rust 程序以收集基于事件的结构化诊断信息。该框架提供了用于自定义日志记录输出级别和格式的实用程序，例如创建结构化 JSON 日志消息。要使用此框架，您必须在实现函数处理程序之前将其初始化为 `subscriber`。然后，您可以使用 `debug`、`info` 和 `error` 等跟踪宏来指定每个场景所需的日志记录级别。

**Example – 使用 Tracing crate**  
请注意以下几点：  
+ `tracing_subscriber::fmt().json()`：如果包含此选项，则日志将采用 JSON 格式。要使用此选项，您必须将 `json` 功能包含在 `tracing-subscriber` 依赖项中（例如 `tracing-subscriber = { version = "0.3.11", features = ["json"] }`）。
+ `#[tracing::instrument(skip(event), fields(req_id = %event.context.request_id))]`：每次调用处理程序时，此注释都会生成一个跨度。该跨度会将请求 ID 添加到每个日志行。
+ `{ %first_name }`：此构造会将 `first_name` 字段添加到使用该字段的日志行。此字段的值与同名变量对应。

```
use lambda_runtime::{service_fn, Error, LambdaEvent};
use serde_json::{json, Value};
#[tracing::instrument(skip(event), fields(req_id = %event.context.request_id))]
async fn handler(event: LambdaEvent<Value>) -> Result<Value, Error> {
    tracing::info!("Rust function invoked");
    let payload = event.payload;
    let first_name = payload["firstName"].as_str().unwrap_or("world");
    tracing::info!({ %first_name }, "Rust function responds to event");
    Ok(json!({ "message": format!("Hello, {first_name}!") }))
}

#[tokio::main]
async fn main() -> Result<(), Error> {
    tracing_subscriber::fmt().json()
        .with_max_level(tracing::Level::INFO)
        // this needs to be set to remove duplicated information in the log.
        .with_current_span(false)
        // this needs to be set to false, otherwise ANSI color codes will
        // show up in a confusing manner in CloudWatch logs.
        .with_ansi(false)
        // disabling time is handy because CloudWatch will add the ingestion time.
        .without_time()
        // remove the name of the function from every log entry
        .with_target(false)
        .init();
    lambda_runtime::run(service_fn(handler)).await
}
```

调用此 Rust 函数时，它会输出类似于以下内容的两个日志行：

```
{"level":"INFO","fields":{"message":"Rust function invoked"},"spans":[{"req_id":"45daaaa7-1a72-470c-9a62-e79860044bb5","name":"handler"}]}
{"level":"INFO","fields":{"message":"Rust function responds to event","first_name":"David"},"spans":[{"req_id":"45daaaa7-1a72-470c-9a62-e79860044bb5","name":"handler"}]}
```