

# 使用来自其 AWS 他服务的事件调用 Lambda
<a name="lambda-services"></a>

某些 AWS 服务可以使用*触发器*直接调用 Lambda 函数。这些服务将事件推送到 Lambda，并在指定事件发生时立即调用该函数。触发器适用于离散事件和实时处理。当您[使用 Lambda 控制台创建触发器](#lambda-invocation-trigger)时，控制台会与相应的 AWS 服务交互以配置该服务的事件通知。触发器实际上由生成事件的服务而不是 Lambda 存储和管理。

事件使用 JSON 格式的数据结构。JSON 结构因生成它的服务和事件类型而异，但它们都包含函数处理事件所需的数据。

一个函数可具有多个触发器。每个触发器都可以作为一个客户端独立调用您的函数，Lambda 传递到您的函数的每个事件仅具有一个触发器的数据。Lambda 将事件文档转换为一个对象，并将该对象传递给函数处理程序。

取决于服务，事件驱动调用可以是[同步的](invocation-sync.md)，也可以是[异步的](invocation-async.md)。
+ 对于同步调用，生成事件的服务服务等待来自您的函数的响应。该服务定义函数需要在响应中返回的数据。该服务控制错误策略，例如是否重试错误。
+ 对于异步调用，Lambda 先将事件排队，然后再将事件传递给您的函数。当 Lambda 将事件排队时，它会立即向生成事件的服务发送成功响应。函数处理事件后，Lambda 不会返回事件生成服务的响应。

## 创建触发器
<a name="lambda-invocation-trigger"></a>

创建触发器最简单的方法是使用 Lambda 控制台。使用控制台创建触发器时，Lambda 会自动将所需的权限添加到函数[基于资源的策略](access-control-resource-based.md)。

**要使用 Lambda 控制台创建触发器**

1. 打开 Lamba 控制台的[函数](https://console.aws.amazon.com/lambda/home#/functions)页面。

1. 选择要为其创建触发器的函数。

1. 在**函数概述**窗格中，选择**添加触发器**。

1. 选择要调用函数的 AWS 服务。

1. 填写**触发器配置**窗格中的选项，然后选择**添加**。根据您选择调用函数的 AWS 服务，触发器配置选项会有所不同。

## 可以调用 Lambda 函数的服务
<a name="listing-of-services-and-links-to-more-information"></a>

下表列出了可以调用 Lambda 函数的服务。


****  

| 服务 | 调用方法 | 
| --- | --- | 
|  [Amazon Managed Streaming for Apache Kafka](with-msk.md)  |  [事件源映射](invocation-eventsourcemapping.md)  | 
|  [自行管理的 Apache Kafka](with-kafka.md)  |  [事件源映射](invocation-eventsourcemapping.md)  | 
|  [Amazon API Gateway](services-apigateway.md)  |  事件驱动；同步调用  | 
|  [AWS CloudFormation](services-cloudformation.md)  |  事件驱动；异步调用  | 
|  [Amazon CloudWatch Logs](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/SubscriptionFilters.html#LambdaFunctionExample)  |  事件驱动；异步调用  | 
|  [AWS CodeCommit](https://docs.aws.amazon.com/codecommit/latest/userguide/how-to-notify-lambda-cc.html)  |  事件驱动；异步调用  | 
|  [AWS CodePipeline](https://docs.aws.amazon.com/codepipeline/latest/userguide/actions-invoke-lambda-function.html)  |  事件驱动；异步调用  | 
|  [Amazon Cognito](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-events.html)  |  事件驱动；同步调用  | 
|  [AWS Config](governance-config.md)  |  事件驱动；异步调用  | 
|  [Amazon Connect](https://docs.aws.amazon.com/connect/latest/adminguide/connect-lambda-functions.html)  |  事件驱动；同步调用  | 
|  [Amazon DocumentDB](with-documentdb.md)  |  [事件源映射](invocation-eventsourcemapping.md)  | 
|  [Amazon DynamoDB](with-ddb.md)  |  [事件源映射](invocation-eventsourcemapping.md)  | 
|  [Elastic Load Balancing（应用程序负载均衡器）](services-alb.md)  |  事件驱动；同步调用  | 
|  [Amazon EventBridge (CloudWatch Events)](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-what-is.html)  |  事件驱动；异步调用（事件总线）、同步或异步调用（管道和计划）  | 
|  [AWS IoT](services-iot.md)  |  事件驱动；异步调用  | 
|  [Amazon Kinesis](with-kinesis.md)  |  [事件源映射](invocation-eventsourcemapping.md)  | 
|  [Amazon Data Firehose](https://docs.aws.amazon.com/firehose/latest/dev/data-transformation.html)  |  事件驱动；同步调用  | 
|  [Amazon Lex](https://docs.aws.amazon.com/lexv2/latest/dg/lambda.html)  |  事件驱动；同步调用  | 
|  [Amazon MQ](with-mq.md)  |  [事件源映射](invocation-eventsourcemapping.md)  | 
|  [Amazon Simple Email Service](https://docs.aws.amazon.com/ses/latest/dg/receiving-email-action-lambda.html)  |  事件驱动；异步调用  | 
|  [Amazon Simple Notification Service](with-sns.md)  |  事件驱动；异步调用  | 
|  [Amazon Simple Queue Service](with-sqs.md)  |  [事件源映射](invocation-eventsourcemapping.md)  | 
|  [Amazon Simple Storage Service（Amazon S3）](with-s3.md)  |  事件驱动；异步调用  | 
|  [Amazon Simple Storage Service 批处理](services-s3-batch.md)  |  事件驱动；同步调用  | 
|  [Secrets Manager](https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotate-secrets_lambda.html)  |  密钥轮换  | 
|  [AWS Step Functions](https://docs.aws.amazon.com/step-functions/latest/dg/connect-lambda.html)  |  事件驱动；同步或异步调用  | 
|  [Amazon VPC Lattice](https://docs.aws.amazon.com/vpc-lattice/latest/ug/lambda-functions.html)  |  事件驱动；同步调用  | 

# 将 Lambda 与 Apache Kafka 结合使用
<a name="with-kafka-esm"></a>

Lambda 支持将 [Apache Kafka](https://kafka.apache.org/) 作为[事件源](invocation-eventsourcemapping.md)。Apache Kafka 是开源事件流平台，旨在处理高吞吐量、实时数据管道和流应用程序。将 Lambda 与 Apache Kafka 结合使用主要有两种方法：
+ [结合 Amazon MSK 使用 Lambda](with-msk.md)：Amazon Managed Streaming for Apache Kafka（Amazon MSK）是一项由 AWS 完全托管的服务。Amazon MSK 可帮助自动管理您的 Kafka 基础设施，包括预置、修补和扩展。
+ [将 Lambda 与自行管理的 Apache Kafka 结合使用](with-kafka.md)：在 AWS 术语中，自行管理的群集包括非 AWS 托管 Kafka 集群。例如，您仍然可以将 Lambda 与由非 AWS 云提供商（例如 [Confluent Cloud](https://www.confluent.io/confluent-cloud/) 或 [Redpanda](https://www.redpanda.com/)）托管的 Kafka 集群结合使用。

在 Amazon MSK 和自行管理的 Apache Kafka 之间做出决定时，请考虑您的运营需求和控制要求。如果您希望 AWS 以最小的运营开销快速帮助您管理可扩展、可随时投入生产的 Kafka 设置，那么 Amazon MSK 是更好的选择。它简化了安全性、监控和高可用性，帮助您专注于应用程序开发而不是基础设施管理。另一方面，自行管理的 Apache Kafka 更适合在非 AWS 托管环境（包括本地集群）上运行的使用案例。

**Topics**
+ [

# 结合 Amazon MSK 使用 Lambda
](with-msk.md)
+ [

# 将 Lambda 与自行管理的 Apache Kafka 结合使用
](with-kafka.md)
+ [

# Lambda 中的 Apache Kafka 事件轮询器扩展模式
](kafka-scaling-modes.md)
+ [

# Lambda 中 Apache Kafka 轮询和流的起始位置
](kafka-starting-positions.md)
+ [

# Lambda 中可自定义的使用者组 ID
](kafka-consumer-group-id.md)
+ [

# 从 Amazon MSK 和自托管式 Apache Kafka 事件源中筛选事件
](kafka-filtering.md)
+ [

# 在 Lambda 中使用带有 Kafka 事件源的架构注册表
](services-consume-kafka-events.md)
+ [

# 低延迟处理 Kafka 事件源
](with-kafka-low-latency.md)
+ [

# 为 Kafka 事件源配置错误处理控件
](kafka-retry-configurations.md)
+ [

# 捕获 Amazon MSK 和自托管式 Apache Kafka 事件源的丢弃批次
](kafka-on-failure.md)
+ [

# 使用 Kafka 主题作为失败时的目标
](kafka-on-failure-destination.md)
+ [

# Kafka 事件源映射日志记录
](esm-logging.md)
+ [

# Kafka 事件源映射错误的故障排除
](with-kafka-troubleshoot.md)

# 结合 Amazon MSK 使用 Lambda
<a name="with-msk"></a>

[Amazon Managed Streaming for Apache Kafka（Amazon MSK）](https://docs.aws.amazon.com/msk/latest/developerguide/what-is-msk.html)是一项完全托管式服务，可用于构建并运行使用 Apache Kafka 来处理流数据的应用程序。Amazon MSK 简化了 Kafka 集群的设置、扩展和管理。Amazon MSK 还可以更轻松地配置您的应用程序以适用于多个可用区和保证 AWS Identity and Access Management (IAM) 的安全性。

本章说明如何将 Amazon MSK 集群用作 Lambda 函数的事件源。将 Amazon MSK 与 Lambda 集成的一般过程包括以下步骤：

1. **[集群和网络设置](with-msk-cluster-network.md)**：首先，设置您的 [Amazon MSK 集群](https://docs.aws.amazon.com/msk/latest/developerguide/what-is-msk.html)。这包括允许 Lambda 访问集群的正确联网配置。

1. **[事件源映射设置](with-msk-configure.md)**：然后，创建 Lambda 需要的[事件源映射](invocation-eventsourcemapping.md)资源，以将 Amazon MSK 集群安全地连接到您的函数。

1. **[函数和权限设置](with-msk-permissions.md)**：最后，请确保您的函数设置正确，并且在其[执行角色](lambda-intro-execution-role.md)中具有必要的权限。

**注意**  
现在，您可以直接使用 Lambda 或 Amazon MSK 控制台来创建和管理您的 Amazon MSK 事件源映射。这两个控制台都提供了自动处理必要 Lambda 执行角色权限的设置功能，从而能够实现更简便的配置流程。

有关如何设置与 Amazon MSK 集群的 Lambda 集成的示例，请参阅[教程：使用 Amazon MSK 事件源映射调用 Lambda 函数](services-msk-tutorial.md)、AWS Compute Blog 上的 [Using Amazon MSK as an event source for AWS Lambda](https://aws.amazon.com/blogs/compute/using-amazon-msk-as-an-event-source-for-aws-lambda/) 和 Amazon MSK Labs 中的 [Amazon MSK Lambda Integration](https://amazonmsk-labs.workshop.aws/en/msklambda.html)。

**Topics**
+ [

## 事件示例
](#msk-sample-event)
+ [

# 为 Lambda 配置 Amazon MSK 集群和 Amazon VPC 网络
](with-msk-cluster-network.md)
+ [

# 为 Amazon MSK 事件源映射配置 Lambda 权限
](with-msk-permissions.md)
+ [

# 为 Lambda 配置 Amazon MSK 事件源
](with-msk-configure.md)
+ [

# 教程：使用 Amazon MSK 事件源映射调用 Lambda 函数
](services-msk-tutorial.md)

## 事件示例
<a name="msk-sample-event"></a>

Lambda 调用函数时会在事件参数中发送一批消息。事件负载包含一个消息数组。每个数组项目都包含 Amazon MSK 主题和分区标识符的详细信息，以及时间戳和 base64 编码的消息。

```
{
   "eventSource":"aws:kafka",
   "eventSourceArn":"arn:aws:kafka:us-east-1:123456789012:cluster/vpc-2priv-2pub/751d2973-a626-431c-9d4e-d7975eb44dd7-2",
   "bootstrapServers":"b-2.demo-cluster-1.a1bcde.c1.kafka.us-east-1.amazonaws.com:9092,b-1.demo-cluster-1.a1bcde.c1.kafka.us-east-1.amazonaws.com:9092",
   "records":{
      "mytopic-0":[
         {
            "topic":"mytopic",
            "partition":0,
            "offset":15,
            "timestamp":1545084650987,
            "timestampType":"CREATE_TIME",
            "key":"abcDEFghiJKLmnoPQRstuVWXyz1234==",
            "value":"SGVsbG8sIHRoaXMgaXMgYSB0ZXN0Lg==",
            "headers":[
               {
                  "headerKey":[
                     104,
                     101,
                     97,
                     100,
                     101,
                     114,
                     86,
                     97,
                     108,
                     117,
                     101
                  ]
               }
            ]
         }
      ]
   }
}
```

# 为 Lambda 配置 Amazon MSK 集群和 Amazon VPC 网络
<a name="with-msk-cluster-network"></a>

要将 AWS Lambda 函数连接到 Amazon MSK 集群，您需要正确配置集群及其所在的 [Amazon Virtual Private Cloud（VPC）](https://docs.aws.amazon.com/vpc/latest/userguide/what-is-amazon-vpc.html)。此页面介绍如何配置集群和 VPC。如果您的集群和 VPC 已正确配置，请参阅[为 Lambda 配置 Amazon MSK 事件源](with-msk-configure.md)配置事件源映射。

**Topics**
+ [

## Lambda 和 MSK 集成的网络配置要求概述
](#msk-network-requirements)
+ [

## 为 MSK 事件源配置 NAT 网关
](#msk-nat-gateway)
+ [

## 为 MSK 事件源配置 AWS PrivateLink 端点
](#msk-vpc-privatelink)

## Lambda 和 MSK 集成的网络配置要求概述
<a name="msk-network-requirements"></a>

Lambda 和 MSK 集成所需的联网配置取决于应用程序的网络架构。此集成涉及三种主要资源：Amazon MSK 集群、Lambda 函数和 Lambda 事件源映射。每种资源都位于不同的 VPC 中：
+ 您的 Amazon MSK 集群通常位于您管理的 VPC 的私有子网中。
+ 您的 Lambda 函数位于 Lambda 拥有的 AWS 托管 VPC 中。
+ 您的 Lambda 事件源映射位于 Lambda 拥有的另一个 AWS 托管 VPC 中，与包含函数的 VPC 分开。

[事件源映射](invocation-eventsourcemapping.md)是 MSK 集群和 Lambda 函数之间的中间资源。事件源映射有两个主要任务。首先，它会轮询您的 MSK 集群以获取新消息。然后，它将使用这些消息调用 Lambda 函数。由于这三种资源位于不同的 VPC 中，因此轮询和调用操作都需要跨 VPC 网络调用。

事件源映射的网络配置要求取决于其使用的是[预置模式](invocation-eventsourcemapping.md#invocation-eventsourcemapping-provisioned-mode)还是按需模式，如下图所示：

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/MSK-esm-network-overview.png)


在这两种模式下，Lambda 事件源映射以相同的方式轮询您的 MSK 集群以获取新消息。为了在事件源映射和 MSK 集群之间建立连接，Lambda 会在您的私有子网中创建 [Hyperplane ENI](configuration-vpc.md#configuration-vpc-enis)（或重复使用现有的 Hyperplane ENI，如果可用）来建立安全连接。如图所示，此 Hyperplane ENI 使用 MSK 集群的子网和安全组配置，而不是 Lambda 函数。

轮询集群的消息后，Lambda 在每种模式下以不同的方式调用函数：
+ 在预置模式下，Lambda 会自动处理事件源映射 VPC 和函数 VPC 之间的连接。因此，您不需要任何其他联网组件即可成功调用函数。
+ 在按需模式下，您的 Lambda 事件源映射通过客户托管 VPC 的路径调用函数。因此，您需要在 VPC 的公有子网中配置 [NAT 网关](https://docs.aws.amazon.com/vpc/latest/userguide/vpc-nat-gateway.html)，或者在 VPC 的私有子网中配置 [AWS PrivateLink](https://docs.aws.amazon.com/vpc/latest/privatelink/what-is-privatelink.html) 端点，以提供对 Lambda、[AWS Security Token Service（STS）](https://docs.aws.amazon.com/STS/latest/APIReference/welcome.html)以及可选 [AWS Secrets Manager](https://docs.aws.amazon.com/secretsmanager/latest/userguide/intro.html) 的访问权限。正确配置其中任一选项都可以在您的 VPC 和 Lambda 托管的运行时 VPC 之间建立连接，这是调用您的函数所必需的。

NAT 网关允许私有子网中的资源访问公共互联网。使用此配置意味着您的流量在调用 Lambda 函数之前会遍历互联网。AWS PrivateLink 端点允许私有子网安全地连接到 AWS 服务或其他私有 VPC 资源，而无需通过公共互联网。有关如何配置这些资源的详细信息，请参阅[为 MSK 事件源配置 NAT 网关](#msk-nat-gateway)或[为 MSK 事件源配置 AWS PrivateLink 端点](#msk-vpc-privatelink)。

到目前为止，我们假设您的 MSK 集群位于 VPC 内的私有子网中，这种情况更常见。但是，即使您的 MSK 集群位于 VPC 内的公有子网中，您也必须配置 AWS PrivateLink 端点以启用安全连接。下表根据您配置 MSK 集群和 Lambda 事件源映射的方式总结了联网配置要求：


| MSK 集群位置（位于客户托管的 VPC 中） | Lambda 事件源映射扩展模式 | 所需的联网配置 | 
| --- | --- | --- | 
|  私有子网  |  按需模式  |  NAT 网关（位于 VPC 的公有子网中），或 AWS PrivateLink 端点（位于 VPC 的私有子网中），以启用对 Lambda、AWS STS 以及可选的 Secrets Manager 的访问。  | 
|  公有子网  |  按需模式  |  AWS PrivateLink 端点（位于 VPC 的公有子网中），以启用对 Lambda、AWS STS 以及可选的 Secrets Manager 的访问。  | 
|  私有子网  |  预调配模式  |  无  | 
|  公有子网  |  预调配模式  |  无  | 

此外，与您的 MSK 集群关联的安全组必须允许通过正确端口的流量。请确保已配置以下安全组规则：
+ **入站规则**：允许默认代理端口的所有流量。MSK 使用的端口取决于集群上的身份验证类型：`9098` 用于 IAM 身份验证、`9096` 用于 SASL/SCRAM 和 `9094` 用于 TLS。或者，您可以使用自引用安全组规则允许来自同一安全组内的实例进行访问。
+ **出站规则**：如果您的函数需要与其他 AWS 服务进行通信，则允许端口 `443` 上的所有流量向外部目标传输。或者，如果您不需要与其他 AWS 服务通信，则可以使用自引用的安全组规则来限制对代理的访问权限。
+ **Amazon VPC 端点入站规则**：如果您正在使用 Amazon VPC 端点，则与该端点关联的安全组必须允许来自集群安全组的端口 `443` 上的入站流量。

## 为 MSK 事件源配置 NAT 网关
<a name="msk-nat-gateway"></a>

您可以配置 NAT 网关，以允许事件源映射轮询来自集群的消息，并通过 VPC 的路径调用该函数。仅当您的事件源映射使用按需模式，并且您的集群位于 VPC 的私有子网中时，才需要这样做。如果您的集群位于 VPC 的公有子网中，或者您的事件源映射使用预置模式，则无需配置 NAT 网关。

[NAT 网关](https://docs.aws.amazon.com/vpc/latest/userguide/vpc-nat-gateway.html)允许私有子网中的资源访问公共互联网。如果您需要与 Lambda 的私有连接，请改为参阅[为 MSK 事件源配置 AWS PrivateLink 端点](#msk-vpc-privatelink)。

配置 NAT 网关后，必须配置相应的路由表。这允许来自私有子网的流量通过 NAT 网关路由到公共互联网。

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/MSK-NAT-Gateway.png)


以下步骤将指导您使用控制台配置 NAT 网关。根据需要对每个可用区（AZ）重复这些步骤。

**配置 NAT 网关和正确的路由（控制台）**

1. 按照[创建 NAT 网关](https://docs.aws.amazon.com/vpc/latest/userguide/nat-gateway-working-with.html)中的步骤进行操作，注意以下几点：
   + NAT 网关应始终位于公有子网中。创建具有[公有连接](https://docs.aws.amazon.com/vpc/latest/userguide/vpc-nat-gateway.html)的 NAT 网关。
   + 如果您的 MSK 集群跨多个可用区复制，则请为每个可用区创建一个 NAT 网关。例如，在每个可用区中，您的 VPC 应有一个包含集群的私有子网和一个包含 NAT 网关的公有子网。对于具有三个可用区的设置，您将拥有三个私有子网、三个公有子网和三个 NAT 网关。

1. 创建 NAT 网关后，打开 [Amazon VPC 控制台](https://console.aws.amazon.com/vpc/)并在左侧菜单中选择**路由表**。

1. 选择**创建路由表**。

1. 将此路由表与包含 MSK 集群的 VPC 关联。（可选）输入路由表的名称。

1. 选择**创建路由表**。

1. 选择刚刚创建的路由表。

1. 在**子网关联**选项卡下，选择**编辑子网关联**。
   + 将此路由表与包含 MSK 集群的私有子网关联。

1. 选择 **Edit routes (编辑路由)**。

1. 选择**添加路由**：

   1. 对于 **Destination (目标)**，选择 `0.0.0.0/0`。

   1. 对于**目标**，选择 **NAT 网关**。

   1. 在搜索框中，选择已在步骤 1 中创建的 NAT 网关。这应该是与包含您的 MSK 集群的私有子网（您在步骤 6 中与此路由表关联的私有子网）位于同一可用区的 NAT 网关。

1. 选择**保存更改**。

## 为 MSK 事件源配置 AWS PrivateLink 端点
<a name="msk-vpc-privatelink"></a>

您可以配置 AWS PrivateLink 端点来轮询来自集群的消息，并通过 VPC 的路径调用该函数。这些端点应允许 MSK 集群访问以下各项：
+ Lambda 服务
+ [AWS Security Token Service（STS）](https://docs.aws.amazon.com/STS/latest/APIReference/welcome.html)
+ （可选）[AWS Secrets Manager](https://docs.aws.amazon.com/secretsmanager/latest/userguide/intro.html) 服务。如果集群身份验证所需的密钥存储在 Secrets Manager 中，则需要此项。

仅当事件源映射使用按需模式时，才需要配置 PrivateLink 端点。如果事件源映射使用预置模式，则 Lambda 会为您建立所需的连接。

PrivateLink 端点允许通过 AWS PrivateLink 安全、私密地访问 AWS 服务。或者，要配置 NAT 网关以允许您的 MSK 集群访问公共互联网，请参阅[为 MSK 事件源配置 NAT 网关](#msk-nat-gateway)。

配置 VPC 端点后，您的 MSK 集群应该可以直接和私有地访问 Lambda、STS 以及可选的 Secrets Manager。

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/MSK-PrivateLink-Endpoints.png)


以下步骤将指导您使用控制台配置 PrivateLink 端点。根据需要对每个端点（Lambda、STS、Secrets Manager）重复这些步骤。

**配置 VPC PrivateLink 端点（控制台）**

1. 打开 [Amazon VPC 控制台](https://console.aws.amazon.com/vpc/)，然后在左侧菜单中选择**端点**。

1. 选择**创建端点**。

1. （可选）输入端点的名称。

1. 对于**类型**，选择 **AWS 服务**。

1. 在**服务**下，开始键入服务的名称。例如，要创建连接到 Lambda 的端点，请在搜索框中键入 `lambda`。

1. 在结果中，您应该看到当前区域中的服务端点。例如，在美国东部（弗吉尼亚州北部）区域中，应该看到 `com.amazonaws.us-east-2.lambda`。选择此服务。

1. 在**网络设置**下，选择包含 MSK 集群的 VPC。

1. 在**子网**下，选择 MSK 集群所在的可用区。
   + 对于每个可用区，在**子网 ID** 下，选择包含 MSK 集群的私有子网。

1. 在**安全组**下，请选择与 MSK 集群关联的安全组。

1. 选择**创建端点**。

默认情况下，Amazon VPC 端点具有开放的 IAM 策略，允许对资源进行广泛访问。最佳实践是，将这些策略限制为使用该端点执行所需的操作。例如，对于 Secrets Manager 端点，您可以修改其策略，使其仅允许函数的执行角色访问密钥。

**Example VPC 端点策略 – Secrets Manager 端点**  

```
{
    "Statement": [
        {
            "Action": "secretsmanager:GetSecretValue",
            "Effect": "Allow",
            "Principal": {
                "AWS": [
                    "arn:aws::iam::123456789012:role/my-role"
                ]
            },
            "Resource": "arn:aws::secretsmanager:us-west-2:123456789012:secret:my-secret"
        }
    ]
}
```

对于 AWS STS 和 Lambda 端点，您可以将调用主体限制为 Lambda 服务主体。但是，请务必在这些政策中使用 `"Resource": "*"`。

**Example VPC 端点策略 – AWS STS 端点**  

```
{
    "Statement": [
        {
            "Action": "sts:AssumeRole",
            "Effect": "Allow",
            "Principal": {
                "Service": [
                    "lambda.amazonaws.com"
                ]
            },
            "Resource": "*"
        }
    ]
}
```

**Example VPC 端点策略 – Lambda 端点**  

```
{
    "Statement": [
        {
            "Action": "lambda:InvokeFunction",
            "Effect": "Allow",
            "Principal": {
                "Service": [
                    "lambda.amazonaws.com"
                ]
            },
            "Resource": "*"
        }
    ]
}
```

# 为 Amazon MSK 事件源映射配置 Lambda 权限
<a name="with-msk-permissions"></a>

要访问 Amazon MSK 集群，您的函数和事件源映射需要具有执行各种 Amazon MSK API 操作的权限。为函数的[执行角色](lambda-intro-execution-role.md)添加这些权限。如果您的用户需要访问权限，请将所需权限添加到用户或角色的身份策略中。

[AWSLambdaMSKExecutionRole](https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AWSLambdaMSKExecutionRole.html) 托管策略包含 Amazon MSK Lambda 事件源映射所需的最低权限。要简化权限流程，您可以执行以下操作：
+ 将 [AWSLambdaMSKExecutionRole](https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AWSLambdaMSKExecutionRole.html) 托管策略附加到您的执行角色。
+ 让 Lambda 控制台为您生成权限。当您在[控制台中创建 Amazon MSK 事件源映射](msk-esm-create.md#msk-console)时，Lambda 会评估您的执行角色，并在发现任何权限缺失的情况时向您发出提醒。选择**生成权限**以自动更新您的执行角色。如果您手动创建或修改了执行角色的策略，或者这些策略被附加到多个角色上，则此方法将不起作用。请注意，在使用高级功能（例如[失败时的目标](kafka-on-failure.md)或 [AWS Glue 架构注册表](services-consume-kafka-events.md)）时，您的执行角色可能仍需要额外的权限。

**Topics**
+ [

## 所需的权限
](#msk-required-permissions)
+ [

## 可选权限
](#msk-optional-permissions)

## 所需的权限
<a name="msk-required-permissions"></a>

您的 Lambda 函数执行角色必须具备以下所需的 Amazon MSK 事件源映射权限。这些权限包含在 [AWSLambdaMSKExecutionRole](https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AWSLambdaMSKExecutionRole.html) 托管策略中。

### CloudWatch Logs 权限
<a name="msk-basic-permissions"></a>

以下权限使 Lambda 能够在 Amazon CloudWatch Logs 中创建和存储日志。
+ [logs:CreateLogGroup](https://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_CreateLogGroup.html)
+ [logs:CreateLogStream](https://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_CreateLogStream.html)
+ [logs:PutLogEvents](https://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_PutLogEvents.html)

### MSK 集群权限
<a name="msk-cluster-permissions"></a>

以下权限使 Lambda 能够代表您访问 Amazon MSK 集群：
+ [kafka:DescribeCluster](https://docs.aws.amazon.com/msk/1.0/apireference/clusters-clusterarn.html)
+ [kafka:DescribeClusterV2](https://docs.aws.amazon.com/MSK/2.0/APIReference/v2-clusters-clusterarn.html)
+ [kafka:GetBootstrapBrokers](https://docs.aws.amazon.com/msk/1.0/apireference/clusters-clusterarn-bootstrap-brokers.html)

我们建议使用 [kafka:DescribeClusterV2](https://docs.aws.amazon.com/MSK/2.0/APIReference/v2-clusters-clusterarn.html) 而不是 [kafka:DescribeCluster](https://docs.aws.amazon.com/msk/1.0/apireference/clusters-clusterarn.html)。v2 权限适用于预置集群和无服务器 Amazon MSK 集群。您的策略中只需其中一项权限即可。

### VPC 权限
<a name="msk-vpc-permissions"></a>

以下权限使 Lambda 能够在连接到您的 Amazon MSK 集群时创建和管理网络接口：
+ [ec2:CreateNetworkInterface](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_CreateNetworkInterface.html)
+ [ec2:DescribeNetworkInterfaces](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeNetworkInterfaces.html)
+ [ ec2:DescribeVpcs](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeVpcs.html)
+ [ ec2:DeleteNetworkInterface](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DeleteNetworkInterface.html)
+ [ ec2:DescribeSubnets](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeSubnets.html)
+ [ ec2:DescribeSecurityGroups](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeSecurityGroups.html)

## 可选权限
<a name="msk-optional-permissions"></a>

 您的 Lambda 函数还可能需要权限来：
+ 访问跨账户的 Amazon MSK 集群。要进行跨账户事件源映射，您在执行角色中需要 [kafka:DescribeVpcConnection](https://docs.aws.amazon.com/msk/1.0/apireference/vpc-connection-arn.html)。创建跨账户事件源映射的 IAM 主体需要 [kafka:ListVpcConnections](https://docs.aws.amazon.com/msk/1.0/apireference/vpc-connections.html)。
+ 访问您的 SCRAM 密钥（如果使用 [SASL/SCRAM 身份验证](msk-cluster-auth.md#msk-sasl-scram)）。这样，您的函数就可以通过用户名和密码来连接到 Kafka。
+ 如果您使用 SASL/SCRAM 或 [mTLS](msk-cluster-auth.md#msk-mtls) 身份验证，则请描述您的 Secrets Manager 密钥。这样，您的函数就可以检索安全连接所需的凭证或证书。
+ 如果您的 AWS Secrets Manager 密钥使用 AWS KMS 客户托管密钥加密，请访问您的 AWS KMS 客户托管密钥。
+ 如果您使用的是带身份验证的架构注册表，请访问您的架构注册表密钥：
  + 对于 AWS Glue 架构注册表：您的函数需要 `glue:GetRegistry` 和 `glue:GetSchemaVersion` 权限。这样，您的函数就可以查找和使用存储在 AWS Glue 中的消息格式规则。
  + 对于带有 `BASIC_AUTH` 或 `CLIENT_CERTIFICATE_TLS_AUTH` 的 [Confluent 架构注册表](https://docs.confluent.io/platform/current/schema-registry/security/index.html)：您的函数需要访问密钥（包含身份验证凭证）的 `secretsmanager:GetSecretValue` 权限。这样，您的函数就可以检索访问 Confluent 架构注册表所需的用户名/密码或证书。
  + 对于私有 CA 证书：您的函数需要访问密钥（包含证书）的 secretsmanager:GetSecretValue 权限。这样，您的函数就可以验证使用自定义证书的架构注册表的身份。
+ 如果您对事件源映射使用 IAM 身份验证，则可以访问 Kafka 集群的使用者组，并从指定的主题中轮询消息。

 这些密钥对应以下所需权限：
+ [kafka:ListScramSecrets](https://docs.aws.amazon.com/msk/1.0/apireference/clusters-clusterarn-scram-secrets.html) - 让您可以列出用于 Kafka 身份验证的 SCRAM 密钥
+ [secretsmanager:GetSecretValue](https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_GetSecretValue.html) - 让您可以从 Secrets Manager 中检索密钥
+ [kms:Decrypt](https://docs.aws.amazon.com/kms/latest/APIReference/API_Decrypt.html) - 让您可以使用 AWS KMS 对加密数据进行解密
+ [glue:GetRegistry](https://docs.aws.amazon.com/glue/latest/webapi/API_GetRegistry.html) - 让您可以访问 AWS Glue 架构注册表
+ [glue:GetSchemaVersion](https://docs.aws.amazon.com/glue/latest/webapi/API_GetSchemaVersion.html) - 让您可以从 AWS Glue 架构注册表中检索特定的架构版本
+ [kafka-cluster:Connect](https://docs.aws.amazon.com/service-authorization/latest/reference/list_apachekafkaapisforamazonmskclusters.html)：授予连接和验证集群的权限
+ [kafka-cluster:AlterGroup](https://docs.aws.amazon.com/service-authorization/latest/reference/list_apachekafkaapisforamazonmskclusters.html)：授予加入集群上群组的权限，相当于 Apache Kafka 的 READ GROUP ACL
+ [kafka-cluster:DescribeGroup](https://docs.aws.amazon.com/service-authorization/latest/reference/list_apachekafkaapisforamazonmskclusters.html)：授予描述集群上的群组的权限，相当于 Apache Kafka 的 DESCRIBE GROUP ACL
+ [kafka-cluster:DescribeTopic](https://docs.aws.amazon.com/service-authorization/latest/reference/list_apachekafkaapisforamazonmskclusters.html)：授予描述集群上的主题的权限，相当于 Apache Kafka 的 DESCRIBE TOPIC ACL
+ [kafka-cluster:ReadData](https://docs.aws.amazon.com/service-authorization/latest/reference/list_apachekafkaapisforamazonmskclusters.html)：授予从集群上的主题中读取数据的权限，相当于 Apache Kafka 的 READ TOPIC ACL

 此外，如果您想将失败调用的记录发送到失败时的目标，则需要根据目标类型获得以下权限：
+ 对于 Amazon SQS 目标：[sqs:SendMessage](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_SendMessage.html) - 让您可以向 Amazon SQS 队列发送消息
+ 对于 Amazon SNS 目标：[sns:Publish](https://docs.aws.amazon.com/sns/latest/api/API_Publish.html) - 让您可以向Amazon SNS 主题发布消息
+ 对于 Amazon S3 存储桶目标：[s3:PutObject](https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html) 和 [s3:ListBucket](https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListBucket.html) - 让您可以在 Amazon S3 存储桶中写入和列出对象

有关身份验证和授权错误的故障排除，请参阅[Kafka 事件源映射错误的故障排除](with-kafka-troubleshoot.md)

# 为 Lambda 配置 Amazon MSK 事件源
<a name="with-msk-configure"></a>

要使用 Amazon MSK 集群作为 Lambda 函数的事件源，您需要创建连接这两个资源的[事件源映射](invocation-eventsourcemapping.md)。此页面介绍如何为 Amazon MSK 创建事件源映射。

此页面假设您已经正确配置 MSK 集群及其所在的 [Amazon Virtual Private Cloud（VPC）](https://docs.aws.amazon.com/vpc/latest/userguide/what-is-amazon-vpc.html)。如果您需要设置集群或 VPC，请参阅[为 Lambda 配置 Amazon MSK 集群和 Amazon VPC 网络](with-msk-cluster-network.md)。要配置错误处理的重试行为，请参阅 [为 Kafka 事件源配置错误处理控件](kafka-retry-configurations.md)。

**Topics**
+ [

## 将 Amazon MSK 集群用作事件源
](#msk-esm-overview)
+ [

# 在 Lambda 中配置 Amazon MSK 集群身份验证方法
](msk-cluster-auth.md)
+ [

# 为 Amazon MSK 事件源创建 Lambda 事件源映射
](msk-esm-create.md)
+ [

# 在 Lambda 中创建跨账户事件源映射
](msk-cross-account.md)
+ [

# Lambda 中的所有 Amazon MSK 事件源配置参数
](msk-esm-parameters.md)

## 将 Amazon MSK 集群用作事件源
<a name="msk-esm-overview"></a>

当您添加 Apache Kafka 或 Amazon MSK 集群作为 Lambda 函数的触发器时，该集群将用作[事件源](invocation-eventsourcemapping.md)。

Lambda 根据您指定的[起始位置](kafka-starting-positions.md)，从您在 [CreateEventSourceMapping](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html) 请求中指定为 `Topics` 的 Kafka 主题读取事件数据。成功进行处理后，会将 Kafka 主题提交给 Kafka 集群。

Lambda 按顺序读取每个 Kafka 主题分区的消息。单个 Lambda 负载可以包含来自多个分区的消息。当有更多记录可用时，Lambda 根据您在 [CreateEventSourceMapping](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html) 请求中指定的 BatchSize 值，继续对记录进行批处理，直到函数赶上主题的速度。

Lambda 处理各个批次后，会提交该批次中消息的偏移量。如果函数为批处理中的任何消息返回错误，Lambda 将重试整批消息，直到处理成功或消息过期为止。您可以将所有重试都失败的记录发送到失败时的目标，以供日后处理。

**注意**  
尽管 Lambda 函数的最大超时限制通常为 15 分钟，但 Amazon MSK、自行管理的 Apache Kafka、Amazon DocumentDB、Amazon MQ for ActiveMQ 和 RabbitMQ 的事件源映射，仅支持最大超时限制为 14 分钟的函数。

# 在 Lambda 中配置 Amazon MSK 集群身份验证方法
<a name="msk-cluster-auth"></a>

Lambda 需要访问 Amazon MSK 集群、检索记录和执行其他任务的权限。Amazon MSK 支持多种方法来使用 MSK 集群进行身份验证。

**Topics**
+ [

## 未经身份验证的访问
](#msk-unauthenticated)
+ [

## SASL/SCRAM 身份验证
](#msk-sasl-scram)
+ [

## 双向 TLS 身份验证
](#msk-mtls)
+ [

## IAM 身份验证
](#msk-iam-auth)
+ [

## Lambda 如何选择引导代理
](#msk-bootstrap-brokers)

## 未经身份验证的访问
<a name="msk-unauthenticated"></a>

如果没有客户端会通过互联网访问集群，则可以使用未经身份验证访问。

## SASL/SCRAM 身份验证
<a name="msk-sasl-scram"></a>

Lambda 支持使用 SHA-512 哈希函数和传输层安全性协议（TLS）加密进行[简单身份验证和安全层/加盐质疑应答身份验证机制（SASL/SCRAM）](https://docs.aws.amazon.com/msk/latest/developerguide/msk-password-tutorial.html)身份验证。为使 Lambda 连接到集群，请将身份验证凭证（用户名和密码）存储在 Secrets Manager 密钥中，并在配置事件源映射时引用此密钥。

有关使用 Secrets Manager 的更多信息，请参阅《Amazon Managed Streaming for Apache Kafka Developer Guide》**中的 [Sign-in credentials authentication with Secrets Manager](https://docs.aws.amazon.com/msk/latest/developerguide/msk-password.html)。

**注意**  
Amazon MSK 不支持 SASL/PLAIN 身份验证。

## 双向 TLS 身份验证
<a name="msk-mtls"></a>

双向 TLS（mTLS）在客户端和服务器之间提供双向身份验证。客户端将证书发送给服务器，以便服务器验证客户端。服务器还将服务器证书发送给客户端，以便客户端验证服务器。

对于 Amazon MSK 与 Lambda 的集成，MSK 集群充当服务器，而 Lambda 充当客户端。
+ 要让 Lambda 验证 MSK 集群，您可以在 Secrets Manager 中将客户端证书配置为密钥，并在事件源映射配置中引用该证书。客户端证书必须由服务器信任存储中的证书颁发机构 (CA) 签名。
+ MSK 集群还会向 Lambda 发送服务器证书。服务器证书必须由 AWS 信任存储中的证书颁发机构（CA）签名。

Amazon MSK 不支持自签名服务器证书。Amazon MSK 中的所有代理都使用由 [Amazon Trust Services CA](https://www.amazontrust.com/repository/) 签名的[公有证书](https://docs.aws.amazon.com/msk/latest/developerguide/msk-encryption.html)，默认情况下 Lambda 信任这些证书。

### 配置 mTLS 密钥
<a name="mtls-auth-secret"></a>

CLIENT\$1CERTIFICATE\$1TLS\$1AUTH 密钥需要证书字段和私有密钥字段。对于加密的私有密钥，密钥需要私有密钥密码。证书和私有密钥必须采用 PEM 格式。

**注意**  
Lambda 支持 [PBES1](https://datatracker.ietf.org/doc/html/rfc2898/#section-6.1)（而不是 PBES2）私有密钥加密算法。

证书字段必须包含证书列表，首先是客户端证书，然后是任何中间证书，最后是根证书。每个证书都必须按照以下结构在新行中启动：

```
-----BEGIN CERTIFICATE-----  
        <certificate contents>
-----END CERTIFICATE-----
```

Secrets Manager 支持最多包含 65536 字节的密钥，这为长证书链提供了充足的空间。

私有密钥必须采用 [PKCS \$18](https://datatracker.ietf.org/doc/html/rfc5208) 格式，并具有以下结构：

```
-----BEGIN PRIVATE KEY-----  
         <private key contents>
-----END PRIVATE KEY-----
```

对于加密的私有密钥，请使用以下结构：

```
-----BEGIN ENCRYPTED PRIVATE KEY-----  
          <private key contents>
-----END ENCRYPTED PRIVATE KEY-----
```

以下示例显示使用加密私有密钥进行 mTLS 身份验证的密钥内容。对于加密的私有密钥，您可以在密钥中包含私有密钥密码。

```
{
 "privateKeyPassword": "testpassword",
 "certificate": "-----BEGIN CERTIFICATE-----
MIIE5DCCAsygAwIBAgIRAPJdwaFaNRrytHBto0j5BA0wDQYJKoZIhvcNAQELBQAw
...
j0Lh4/+1HfgyE2KlmII36dg4IMzNjAFEBZiCRoPimO40s1cRqtFHXoal0QQbIlxk
cmUuiAii9R0=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFgjCCA2qgAwIBAgIQdjNZd6uFf9hbNC5RdfmHrzANBgkqhkiG9w0BAQsFADBb
...
rQoiowbbk5wXCheYSANQIfTZ6weQTgiCHCCbuuMKNVS95FkXm0vqVD/YpXKwA/no
c8PH3PSoAaRwMMgOSA2ALJvbRz8mpg==
-----END CERTIFICATE-----",
 "privateKey": "-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIFKzBVBgkqhkiG9w0BBQ0wSDAnBgkqhkiG9w0BBQwwGgQUiAFcK5hT/X7Kjmgp
...
QrSekqF+kWzmB6nAfSzgO9IaoAaytLvNgGTckWeUkWn/V0Ck+LdGUXzAC4RxZnoQ
zp2mwJn2NYB7AZ7+imp0azDZb+8YG2aUCiyqb6PnnA==
-----END ENCRYPTED PRIVATE KEY-----"
}
```

有关适用于 Amazon MSK 的 mTLS 的更多信息，请参阅《Amazon Managed Streaming for Apache Kafka Developer Guide》**中的 [Mutual TLS client authentication for Amazon MSK](https://docs.aws.amazon.com/msk/latest/developerguide/msk-authentication.html)。

## IAM 身份验证
<a name="msk-iam-auth"></a>

您可以使用 AWS Identity and Access Management（IAM）来验证连接到 MSK 集群的客户端的身份。使用 IAM 身份验证时，Lambda 依靠函数[执行角色](lambda-intro-execution-role.md)中的权限连接到集群、检索记录以及执行其他所需操作。有关包含必要权限的策略示例，请参阅《Amazon Managed Streaming for Apache Kafka Developer Guide》**中的 [Create authorization policies for the IAM role](https://docs.aws.amazon.com/msk/latest/developerguide/create-iam-access-control-policies.html)。

如果 IAM 身份验证在您的 MSK 集群上处于活动状态，并且您没有提供密钥，则 Lambda 自动默认使用 IAM 身份验证。

有关 Amazon MSK 中的 IAM 身份验证的更多信息，请参阅 [IAM access control](https://docs.aws.amazon.com/msk/latest/developerguide/iam-access-control.html)。

## Lambda 如何选择引导代理
<a name="msk-bootstrap-brokers"></a>

Lambda 根据集群上可用的身份验证方法以及您是否提供用于身份验证的密钥来选择[引导代理](https://docs.aws.amazon.com/msk/latest/developerguide/msk-get-bootstrap-brokers.html)。如果您为 mTLS 或 SASL/SCRAM 提供了密钥，Lambda 将自动选择该身份验证方法。如果不提供密钥，Lambda 会选择在集群上处于活动状态的最强身份验证方法。下面是 Lambda 选择代理的优先级顺序，从最强到最弱的身份验证：
+ mTLS（为 mTLS 提供的密钥）
+ SASL/SCRAM（为 SASL/SCRAM 提供的密钥）
+ SASL IAM（未提供密钥，IAM 身份验证处于活动状态）
+ 未经身份验证的 TLS（未提供密钥，IAM 身份验证未处于活动状态）
+ 纯文本（未提供密钥，IAM 身份验证和未经身份验证的 TLS 均未处于活动状态）

**注意**  
如果 Lambda 无法连接到最安全的代理类型，则 Lambda 不会尝试连接到其他（较弱）代理类型。如果希望 Lambda 选择较弱的代理类型，请停用集群上所有更强的身份验证方法。

# 为 Amazon MSK 事件源创建 Lambda 事件源映射
<a name="msk-esm-create"></a>

要创建事件源映射，您可以使用 Lambda 控制台、[AWS Command Line Interface (CLI)](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) 或 [AWS SDK](https://aws.amazon.com/getting-started/tools-sdks/)。

**注意**  
创建事件源映射时，Lambda 会在包含 MSK 集群的私有子网中创建 [Hyperplane ENI](configuration-vpc.md#configuration-vpc-enis)，从而允许 Lambda 建立安全连接。此 Hyperplane ENI 使用 MSK 集群的子网和安全组配置，而不是 Lambda 函数。

以下控制台步骤添加 Amazon MSK 集群作为 Lambda 函数的触发器。这将在后台创建一个事件源映射资源。

**将 Amazon MSK 触发器添加到 Lambda 函数（控制台）**

1. 打开 Lambda 控制台的[函数页面](https://console.aws.amazon.com/lambda/home#/functions)。

1. 选择您要为其添加 Amazon MSK 触发器的 Lambda 函数的名称。

1. 在 **Function overview**（函数概览）下，选择 **Add trigger**（添加触发器）。

1. 在**触发器配置**下，选择 **MSK**。

1. 要指定 Kafka 集群详细信息，请执行以下操作：

   1. 对于 **MSK cluster**（MSK 集群），选择您的集群。

   1. 对于**主题名称**，输入要从中使用消息的 Kafka 主题的名称。

   1. 对于**使用者组 ID**，输入要加入的 Kafka 使用者组的 ID（如适用）。有关更多信息，请参阅 [Lambda 中可自定义的使用者组 ID](kafka-consumer-group-id.md)。

1. 对于**集群身份验证**，请进行必要的配置。有关集群身份验证的更多信息，请参阅[在 Lambda 中配置 Amazon MSK 集群身份验证方法](msk-cluster-auth.md)。
   + 如果您希望 Lambda 在建立连接时对 MSK 集群执行身份验证，请开启**使用身份验证**。建议进行身份验证。
   + 如果使用身份验证，对于**身份验证方法**，选择要使用的身份验证方法。
   + 如果您使用身份验证，对于 **Secrets Manager 密钥**，选择包含访问集群所需身份验证凭证的 Secrets Manager 密钥。

1. 在**事件轮询器配置**下，进行必要的配置。
   + 选择**激活触发器**以在创建后立即启用该触发器。
   + 选择是否要为事件源映射**配置预置模式**。有关更多信息，请参阅 [Lambda 中的 Apache Kafka 事件轮询器扩展模式](kafka-scaling-modes.md)。
     + 如果您配置了预置模式，输入**最少事件轮询器**的值、**最多事件轮询器**的值以及 PollerGroupName 的可选值，以指定同一个事件源 VPC 中的多个 ESM 的分组。
   + 对于**起始位置**，选择您希望 Lambda 如何开始从流中读取。有关更多信息，请参阅 [Lambda 中 Apache Kafka 轮询和流的起始位置](kafka-starting-positions.md)。

1. 在**批处理**下，进行必要的配置。有关批处理的更多信息，请参阅[批处理行为](invocation-eventsourcemapping.md#invocation-eventsourcemapping-batching)。

   1. 对于 **Batch size**（批处理大小），输入要在单个批次中接收的最大消息数。

   1. 对于**批处理时段**，输入 Lambda 在调用函数之前收集记录所花费的最大秒数。

1. 在**筛选**下，进行必要的配置。有关筛选的更多信息，请参阅 [从 Amazon MSK 和自托管式 Apache Kafka 事件源中筛选事件](kafka-filtering.md)。
   + 在**筛选条件**中，添加筛选条件定义以确定是否处理事件。

1. 在**故障处理**下，进行必要的配置。有关故障处理的更多信息，请参阅[捕获 Amazon MSK 和自托管式 Apache Kafka 事件源的丢弃批次](kafka-on-failure.md)。
   + 对于**故障目标**，指定故障时目标的 ARN。

1. 对于**标签**，输入要与此事件源映射关联的标签。

1. 要创建触发器，请选择 **Add**（添加）。

您还可以使用 AWS CLI 中的 [create-event-source-mapping](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/create-event-source-mapping.html) 命令创建事件源映射。以下示例创建事件源映射，将 Lambda 函数 `my-msk-function` 映射到 `AWSKafkaTopic` 主题，从 `LATEST` 消息开始。此命令还使用 [SourceAccessConfiguration](https://docs.aws.amazon.com/lambda/latest/api/API_SourceAccessConfiguration.html) 对象指示 Lambda 在连接到集群时使用 [SASL/SCRAM](msk-cluster-auth.md#msk-sasl-scram) 身份验证。

```
aws lambda create-event-source-mapping \
  --event-source-arn arn:aws:kafka:us-east-1:111122223333:cluster/my-cluster/fc2f5bdf-fd1b-45ad-85dd-15b4a5a6247e-2 \
  --topics AWSKafkaTopic \
  --starting-position LATEST \
  --function-name my-kafka-function
  --source-access-configurations '[{"Type": "SASL_SCRAM_512_AUTH","URI": "arn:aws:secretsmanager:us-east-1:111122223333:secret:my-secret"}]'
```

如果集群使用 [mTLS 身份验证](msk-cluster-auth.md#msk-mtls)，请包含指定 `CLIENT_CERTIFICATE_TLS_AUTH` 的 [SourceAccessConfiguration](https://docs.aws.amazon.com/lambda/latest/api/API_SourceAccessConfiguration.html) 对象以及 Secrets Manager 密钥 ARN。如下面的命令所示：

```
aws lambda create-event-source-mapping \
  --event-source-arn arn:aws:kafka:us-east-1:111122223333:cluster/my-cluster/fc2f5bdf-fd1b-45ad-85dd-15b4a5a6247e-2 \
  --topics AWSKafkaTopic \
  --starting-position LATEST \
  --function-name my-kafka-function
  --source-access-configurations '[{"Type": "CLIENT_CERTIFICATE_TLS_AUTH","URI": "arn:aws:secretsmanager:us-east-1:111122223333:secret:my-secret"}]'
```

当集群使用 [IAM 身份验证](msk-cluster-auth.md#msk-iam-auth)时，您不需要 [SourceAccessConfiguration](https://docs.aws.amazon.com/lambda/latest/api/API_SourceAccessConfiguration.html) 对象。如下面的命令所示：

```
aws lambda create-event-source-mapping \
  --event-source-arn arn:aws:kafka:us-east-1:111122223333:cluster/my-cluster/fc2f5bdf-fd1b-45ad-85dd-15b4a5a6247e-2 \
  --topics AWSKafkaTopic \
  --starting-position LATEST \
  --function-name my-kafka-function
```

# 在 Lambda 中创建跨账户事件源映射
<a name="msk-cross-account"></a>

您可以使用[多 VPC 私有连接](https://docs.aws.amazon.com/msk/latest/developerguide/aws-access-mult-vpc.html)将 Lambda 函数连接到不同 AWS 账户 中的预置 MSK 集群。多 VPC 连接使用 AWS PrivateLink，可将所有流量保持在 AWS 网络内。

**注意**  
您无法为无服务器 MSK 集群创建跨账户事件源映射。

要创建跨账户事件源映射，必须先[为 MSK 集群配置多 VPC 连接](https://docs.aws.amazon.com/msk/latest/developerguide/aws-access-mult-vpc.html#mvpc-cluster-owner-action-turn-on)。创建事件源映射时，请使用托管 VPC 连接 ARN 而非集群 ARN，如以下示例所示。[CreateEventSourceMapping](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html) 操作也因 MSK 集群使用的身份验证类型而异。

**Example — 为使用 IAM 身份验证的集群创建跨账户事件源映射**  
当集群使用[基于 IAM 角色的身份验证](msk-cluster-auth.md#msk-iam-auth)时，您不需要 [SourceAccessConfiguration](https://docs.aws.amazon.com/lambda/latest/api/API_SourceAccessConfiguration.html) 对象。示例：  

```
aws lambda create-event-source-mapping \
  --event-source-arn arn:aws:kafka:us-east-1:111122223333:vpc-connection/444455556666/my-cluster-name/51jn98b4-0a61-46cc-b0a6-61g9a3d797d5-7 \
  --topics AWSKafkaTopic \
  --starting-position LATEST \
  --function-name my-kafka-function
```

**Example — 为使用 SASL/SCRAM 身份验证的集群创建跨账户事件源映射**  
如果集群使用 [SASL/SCRAM 身份验证](msk-cluster-auth.md#msk-sasl-scram)，则必须包含指定 `SASL_SCRAM_512_AUTH` 的 [SourceAccessConfiguration](https://docs.aws.amazon.com/lambda/latest/api/API_SourceAccessConfiguration.html) 对象以及 Secrets Manager 密钥 ARN。  
有两种方法可以通过 SASL/SCRAM 身份验证将密钥用于跨账户 Amazon MSK 事件源映射：  
+ 在 Lambda 函数账户中创建密钥并将其与集群密钥同步。[创建轮换](https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets.html)以使两个密钥保持同步。此选项允许您控制来自函数账户的密钥。
+ 使用与 MSK 集群关联的密钥。此密钥必须可用于跨账户存取 Lambda 函数账户。有关更多信息，请参阅[不同账户中用户对 AWS Secrets Manager 密钥的访问权限](https://docs.aws.amazon.com/secretsmanager/latest/userguide/auth-and-access_examples_cross.html)。

```
aws lambda create-event-source-mapping \
  --event-source-arn arn:aws:kafka:us-east-1:111122223333:vpc-connection/444455556666/my-cluster-name/51jn98b4-0a61-46cc-b0a6-61g9a3d797d5-7 \
  --topics AWSKafkaTopic \
  --starting-position LATEST \
  --function-name my-kafka-function \
  --source-access-configurations '[{"Type": "SASL_SCRAM_512_AUTH","URI": "arn:aws:secretsmanager:us-east-1:444455556666:secret:my-secret"}]'
```

**Example — 为使用 mTLS 身份验证的集群创建跨账户事件源映射**  
如果集群使用 [mTLS 身份验证](msk-cluster-auth.md#msk-mtls)，则必须包含指定 `CLIENT_CERTIFICATE_TLS_AUTH` 的 [SourceAccessConfiguration](https://docs.aws.amazon.com/lambda/latest/api/API_SourceAccessConfiguration.html) 对象以及 Secrets Manager 密钥 ARN。密钥可以存储在集群账户或 Lambda 函数账户中。  

```
aws lambda create-event-source-mapping \
  --event-source-arn arn:aws:kafka:us-east-1:111122223333:vpc-connection/444455556666/my-cluster-name/51jn98b4-0a61-46cc-b0a6-61g9a3d797d5-7 \
  --topics AWSKafkaTopic \
  --starting-position LATEST \
  --function-name my-kafka-function \
  --source-access-configurations '[{"Type": "CLIENT_CERTIFICATE_TLS_AUTH","URI": "arn:aws:secretsmanager:us-east-1:444455556666:secret:my-secret"}]'
```

# Lambda 中的所有 Amazon MSK 事件源配置参数
<a name="msk-esm-parameters"></a>

所有 Lambda 事件源类型共享相同的 [CreateEventSourceMapping](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html) 和 [UpdateEventSourceMapping](https://docs.aws.amazon.com/lambda/latest/api/API_UpdateEventSourceMapping.html) API 操作。但是，只有部分参数适用于 Amazon MSK，如下表中所示。


| 参数 | 必需 | 默认值 | 备注 | 
| --- | --- | --- | --- | 
|  AmazonManagedKafkaEventSourceConfig  |  N  |  包含 ConsumerGroupId 字段，该字段默认为唯一值。  |  只能在 Create（创建）设置  | 
|  BatchSize  |  否  |  100  |  最大值：10000  | 
|  DestinationConfig  |  N  |  不适用  |  [捕获 Amazon MSK 和自托管式 Apache Kafka 事件源的丢弃批次](kafka-on-failure.md)  | 
|  启用  |  N  |  True  |    | 
|  BisectBatchOnFunctionError  |  N  |  False  |  [为 Kafka 事件源配置错误处理控件](kafka-retry-configurations.md)  | 
|  FunctionResponseTypes  |  N  |  不适用  |  [为 Kafka 事件源配置错误处理控件](kafka-retry-configurations.md)  | 
|  MaximumRecordAgeInSeconds  |  N  |  -1（不限次数）  |  [为 Kafka 事件源配置错误处理控件](kafka-retry-configurations.md)  | 
|  MaximumRetryAttempts  |  N  |  -1（不限次数）  |  [为 Kafka 事件源配置错误处理控件](kafka-retry-configurations.md)  | 
|  EventSourceArn  |  Y  | 不适用 |  只能在 Create（创建）设置  | 
|  FilterCriteria  |  N  |  不适用  |  [控制 Lambda 向您的函数发送的事件](invocation-eventfiltering.md)  | 
|  FunctionName  |  是  |  不适用  |    | 
|  KMSKeyArn  |  N  |  不适用  |  [筛选条件的加密](invocation-eventfiltering.md#filter-criteria-encryption)  | 
|  MaximumBatchingWindowInSeconds  |  N  |  500 毫秒  |  [批处理行为](invocation-eventsourcemapping.md#invocation-eventsourcemapping-batching)  | 
|  ProvisionedPollersConfig  |  N  |  `MinimumPollers`：如果未指定，则默认值为 1 `MaximumPollers`：如果未指定，则默认值为 200 `PollerGroupName`：不适用  |  [预置模式](kafka-scaling-modes.md#kafka-provisioned-mode)  | 
|  SourceAccessConfigurations  |  否  |  无凭证  |  事件源的 SASL/SCRAM 或 CLIENT\$1CERTIFICATE\$1TLS\$1AUTH (MutualTLS) 身份验证凭证  | 
|  StartingPosition  |  Y  | 不适用 |  AT\$1TIMESTAMP、TRIM\$1HORIZON 或 LATEST 只能在 Create（创建）设置  | 
|  StartingPositionTimestamp  |  N  |  不适用  |  当 StartingPosition 设置为 AT\$1TIMESTAMP 时，为必需项  | 
|  标签  |  N  |  不适用  |  [在事件源映射上使用标签](tags-esm.md)  | 
|  主题  |  Y  | 不适用 |  Kafka 主题名称 只能在 Create（创建）设置  | 

**注意**  
当您指定 `PollerGroupName` 时，同一 Amazon VPC 中的多个 ESM 可以共享事件轮询器单元（EPU）容量。您可以使用此选项来优化 ESM 的预置模式成本。ESM 分组的要求：  
ESM 必须位于同一 Amazon VPC 中
每个轮询器组最多 100 个 ESM
一个组中所有 ESM 的总轮询器数量上限不能超过 2000
您可以更新 `PollerGroupName` 以将 ESM 移动到其他组，也可以通过将 `PollerGroupName` 设置为空字符串（""）从某个组中移除 ESM。

# 教程：使用 Amazon MSK 事件源映射调用 Lambda 函数
<a name="services-msk-tutorial"></a>

在本教程中，您将执行以下操作：
+ 在与现有 Amazon MSK 集群相同的 AWS 账户中创建 Lambda 函数。
+ 为 Lambda 配置联网和身份验证，以便与 Amazon MSK 通信。
+ 设置 Lambda Amazon MSK 事件源映射，其在主题中出现事件时运行您的 Lambda 函数。

完成这些步骤后，当事件发送到 Amazon MSK 时，您将能够设置 Lambda 函数，以使用自己的自定义 Lambda 代码自动处理这些事件。

 **您可以用这项功能做什么？** 

**示例解决方案：使用 MSK 事件源映射向您的客户提供实时比分。**

请考虑以下应用场景：您的公司托管了一个 Web 应用程序，您的客户可以在其中查看有关直播活动（例如体育比赛）的信息。比赛的信息更新将通过 Amazon MSK 上的 Kafka 主题提供给您的团队。您想设计一个解决方案，使用来自 MSK 主题的更新在您开发的应用程序中向客户提供直播活动的更新视图。您已决定采用以下设计方法：您的客户端应用程序将与在 AWS 中托管的无服务器后端进行通信。客户端将使用 Amazon API Gateway WebSocket API 通过 websocket 会话进行连接。

在此解决方案中，您需要一个组件来读取 MSK 事件，执行一些自定义逻辑为应用程序层准备这些事件，然后将该信息转发到 API Gateway API。您可以使用 AWS Lambda 实现此组件，方法是在 Lambda 函数中提供自定义逻辑，然后使用 AWS Lambda Amazon MSK 事件源映射对其进行调用。

有关使用 Amazon API Gateway WebSocket API 实施解决方案的更多信息，请参阅 API Gateway 文档中的 [WebSocket API 教程](https://docs.aws.amazon.com/apigateway/latest/developerguide/websocket-api-chat-app.html)。

## 先决条件
<a name="w2aad101c23c15c35c19"></a>

具有以下预配置资源的 AWS 账户：

**要满足这些先决条件，建议按照 Amazon MSK 文档中的 [Getting started using Amazon MSK](https://docs.aws.amazon.com//msk/latest/developerguide/getting-started.html) 进行操作。**
+ Amazon MSK 集群。请参阅 *Getting started using Amazon MSK* 中的 [Create an Amazon MSK cluster](https://docs.aws.amazon.com//msk/latest/developerguide/create-cluster.html)。
+ 以下配置：
  + 确保在集群安全设置中**启用****基于 IAM 角色的身份验证**。这会将您的 Lambda 函数限制为仅访问所需的 Amazon MSK 资源，从而提高安全性。默认情况下会对新的 Amazon MSK 集群启用此设置。
  + 确保集群网络设置中的**公有访问**已关闭。通过限制处理数据的中介数量，限制 Amazon MSK 集群对互联网的访问，以提高您的安全性。默认情况下会对新的 Amazon MSK 集群启用此设置。
+ 您的 Amazon MSK 集群中用于此解决方案的 Kafka 主题。请参阅 *Getting started using Amazon MSK* 中的 [Create a topic](https://docs.aws.amazon.com//msk/latest/developerguide/create-topic.html)。
+ 设置 Kafka 管理主机以从您的 Kafka 集群检索信息并将 Kafka 事件发送到您的主题进行测试，例如安装了 Kafka 管理 CLI 和 Amazon MSK IAM 库的 Amazon EC2 实例。请参阅 *Getting started using Amazon MSK* 中的 [Create a client machine](https://docs.aws.amazon.com//msk/latest/developerguide/create-client-machine.html)。

设置完这些资源后，请从您的 AWS 账户中收集以下信息，以确认您已准备好继续。
+ Amazon MSK 集群的名称。您可以在 Amazon MSK 控制台中找到这些信息。
+ 集群 UUID，您的 Amazon MSK 集群 ARN 的一部分，您可以在 Amazon MSK 控制台中找到它。按照 Amazon MSK 文档中 [Listing clusters](https://docs.aws.amazon.com/msk/latest/developerguide/msk-list-clusters.html) 中的步骤查找此信息。
+ 与您的 Amazon MSK 集群关联的安全组。您可以在 Amazon MSK 控制台中找到这些信息。在以下步骤中，这些安全组称为 *clusterSecurityGroups*。
+ 包含 Amazon MSK 集群的 Amazon VPC 的 ID。您可以通过在 Amazon MSK 控制台中识别与您的 Amazon MSK 集群关联的子网，然后在 Amazon VPC 控制台中识别与该子网关联的 Amazon VPC 来找到此信息。
+ 解决方案中使用的 Kafka 主题名称。您可以通过从 Kafka 管理主机使用 Kafka `topics` CLI 调用您的 Amazon MSK 集群来找到此信息。有关主题 CLI 的更多信息，请参阅 Kafka 文档中的 [Adding and removing topics](https://kafka.apache.org/documentation/#basic_ops_add_topic)。
+ 您的 Kafka 主题使用者组的名称，适合您的 Lambda 函数使用。Lambda 可以自动创建该组，因此您无需使用 Kafka CLI 创建该组。如果您确实需要管理使用者组，了解有关使用者组 CLI 的更多信息，则请参阅 Kafka 文档中的 [Managing Consumer Groups](https://kafka.apache.org/documentation/#basic_ops_consumer_group)。

您 AWS 账户中有以下权限：
+ 创建和管理 Lambda 函数的权限。
+ 创建 IAM 策略并将其与您的 Lambda 函数关联的权限。
+ 在托管您的 Amazon MSK 集群的 Amazon VPC 中创建 Amazon VPC 端点和更改网络配置的权限。

### 安装 AWS Command Line Interface
<a name="install_aws_cli"></a>

如果您尚未安装 AWS Command Line Interface，请按照[安装或更新最新版本的 AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) 中的步骤进行安装。

本教程需要命令行终端或 Shell 来运行命令。在 Linux 和 macOS 中，可使用您首选的 Shell 和程序包管理器。

**注意**  
在 Windows 中，操作系统的内置终端不支持您经常与 Lambda 一起使用的某些 Bash CLI 命令（例如 `zip`）。[安装 Windows Subsystem for Linux](https://docs.microsoft.com/en-us/windows/wsl/install-win10)，获取 Ubuntu 和 Bash 与 Windows 集成的版本。

## 为 Lambda 配置网络连接以与 Amazon MSK 通信
<a name="w2aad101c23c15c35c21"></a>

 使用 AWS PrivateLink 连接 Lambda 和 Amazon MSK。您可以通过在 Amazon VPC 控制台中创建接口 Amazon VPC 端点来实现此目的。有关联网配置的更多信息，请参阅 [为 Lambda 配置 Amazon MSK 集群和 Amazon VPC 网络](with-msk-cluster-network.md)。

当 Amazon MSK 事件源映射代表 Lambda 函数运行时，它会担任 Lambda 函数的执行角色。此 IAM 角色授权映射访问受 IAM 保护的资源，例如您的 Amazon MSK 集群。尽管这些组件共享执行角色，但 Amazon MSK 映射和您的 Lambda 函数对各自的任务有不同的连接要求，如下图所示。

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/msk_tut_network.png)


您的事件源映射属于 Amazon MSK 集群安全组。在此联网步骤中，从您的 Amazon MSK 集群 VPC 创建 Amazon VPC 端点，将事件源映射连接到 Lambda 和 STS 服务。保护这些端点，以接受来自您的 Amazon MSK 集群安全组的流量。然后，调整 Amazon MSK 集群安全组，以允许事件源映射与 Amazon MSK 集群进行通信。

 您可以使用 AWS 管理控制台 配置以下步骤。

**配置接口 Amazon VPC 端点以连接 Lambda 和 Amazon MSK**

1. 为您的接口 Amazon VPC 端点创建一个安全组 *endpointSecurityGroup*，以允许来自 *clusterSecurityGroups* 端口 443 的入站 TCP 流量。按照 Amazon EC2 文档中[创建安全组](https://docs.aws.amazon.com//AWSEC2/latest/UserGuide/working-with-security-groups.html#creating-security-group)的步骤创建安全组。然后，按照 Amazon EC2 文档中[向安全组添加规则](https://docs.aws.amazon.com//AWSEC2/latest/UserGuide/working-with-security-groups.html#adding-security-group-rule)的步骤添加相应的规则。

   **使用以下信息创建安全组：**

   添加入站规则时，为 *clusterSecurityGroups* 中的每个安全组创建一条规则。对于每条规则：
   + 对于**类型**，选择 **HTTPS**。
   + 对于**源**，选择其中一个 *clusterSecurityGroups*。

1.  创建一个端点，将 Lambda 服务连接到包含 Amazon MSK 集群的 Amazon VPC。按照[创建接口端点](https://docs.aws.amazon.com//vpc/latest/privatelink/create-interface-endpoint.html)中的步骤进行操作。

   **使用以下信息创建接口端点：**
   + 对于**服务名称**，选择 `com.amazonaws.regionName.lambda`，其中 *regionName* 托管您的 Lambda 函数。
   + 对于 **VPC**，选择包含您的 Amazon MSK 集群的 Amazon VPC。
   + 对于**安全组**，选择您之前创建的 *endpointSecurityGroup*。
   + 对于**子网**，选择托管您的 Amazon MSK 集群的子网。
   + 对于**策略**，请提供以下策略文档，其保护端点以供 Lambda 服务主体用于执行 `lambda:InvokeFunction` 操作。

     ```
     {
         "Statement": [
             {
                 "Action": "lambda:InvokeFunction",
                 "Effect": "Allow",
                 "Principal": {
                     "Service": [
                         "lambda.amazonaws.com"
                     ]
                 },
                 "Resource": "*"
             }
         ]
     }
     ```
   + 确保**启用 DNS 名称**保持设置状态。

1.  创建一个端点，将 AWS STS 服务连接到包含 Amazon MSK 集群的 Amazon VPC。按照[创建接口端点](https://docs.aws.amazon.com//vpc/latest/privatelink/create-interface-endpoint.html)中的步骤进行操作。

   **使用以下信息创建接口端点：**
   + 对于**服务名称**，选择 AWS STS。
   + 对于 **VPC**，选择包含您的 Amazon MSK 集群的 Amazon VPC。
   + 对于**安全组**，选择 *endpointSecurityGroup*。
   + 对于**子网**，选择托管您的 Amazon MSK 集群的子网。
   + 对于**策略**，请提供以下策略文档，其保护端点以供 Lambda 服务主体用于执行 `sts:AssumeRole` 操作。

     ```
     {
         "Statement": [
             {
                 "Action": "sts:AssumeRole",
                 "Effect": "Allow",
                 "Principal": {
                     "Service": [
                         "lambda.amazonaws.com"
                     ]
                 },
                 "Resource": "*"
             }
         ]
     }
     ```
   + 确保**启用 DNS 名称**保持设置状态。

1. 对于与您的 Amazon MSK 集群关联的每个安全组（即 *clusterSecurityGroups*），允许执行以下操作：
   + 允许端口 9098 上到所有 *clusterSecurityGroups*（包括其自身内部）的所有入站和出站 TCP 流量。
   + 允许端口 443 上的所有出站 TCP 流量。

   默认安全组规则允许部分流量，因此，如果您的集群连接到单个安全组，并且该组有默认规则，则不需要其他规则。要调整安全组规则，请按照 Amazon EC2 文档中[向安全组添加规则](https://docs.aws.amazon.com//AWSEC2/latest/UserGuide/working-with-security-groups.html#adding-security-group-rule)的步骤进行操作。

   **使用以下信息向您的安全组添加规则：**
   + 对于端口 9098 的每条入站规则或出站规则，请提供
     + 对于 **Type (类型)**，选择 **Custom TCP (自定义 TCP)**。
     + 对于**端口范围**，请提供 9098。
     + 对于**源**，提供其中一个 *clusterSecurityGroups*。
   + 对于端口 443 的每条入站规则的**类型**，选择 **HTTPS**。

## 为 Lambda 创建 IAM 角色，以从您的 Amazon MSK 主题中读取
<a name="w2aad101c23c15c35c23"></a>

确定 Lambda 从 Amazon MSK 主题读取的身份验证要求，然后在策略中进行定义。创建一个角色 *lambdaAuthRole*，授权 Lambda 使用这些权限。使用 `kafka-cluster` IAM 操作在您的 Amazon MSK 集群上授权操作。然后，授权 Lambda 执行发现和连接到您的 Amazon MSK 集群所需的 Amazon MSK `kafka` 和 Amazon EC2 操作以及 CloudWatch 操作，这样 Lambda 便可记录其所执行的操作。

**描述 Lambda 从 Amazon MSK 读取的身份验证要求**

1. 编写一个 IAM 策略文档（JSON 文档）*clusterAuthPolicy*，允许 Lambda 使用您的 Kafka 使用者组从 Amazon MSK 集群中的 Kafka 主题进行读取。Lambda 要求在读取时设置一个 Kafka 使用者组。

   修改以下模板以符合您的先决条件：

------
#### [ JSON ]

****  

   ```
   {
       "Version":"2012-10-17",		 	 	 
       "Statement": [
           {
               "Effect": "Allow",
               "Action": [
                   "kafka-cluster:Connect",
                   "kafka-cluster:DescribeGroup",
                   "kafka-cluster:AlterGroup",
                   "kafka-cluster:DescribeTopic",
                   "kafka-cluster:ReadData",
                   "kafka-cluster:DescribeClusterDynamicConfiguration"
               ],
               "Resource": [
                   "arn:aws:kafka:us-east-1:111122223333:cluster/mskClusterName/cluster-uuid",
                   "arn:aws:kafka:us-east-1:111122223333:topic/mskClusterName/cluster-uuid/mskTopicName",
                   "arn:aws:kafka:us-east-1:111122223333:group/mskClusterName/cluster-uuid/mskGroupName"
               ]
           }
       ]
   }
   ```

------

   有关更多信息，请参阅 [为 Amazon MSK 事件源映射配置 Lambda 权限](with-msk-permissions.md)。编写策略时：
   + 将 *us-east-1* 和 *111122223333* 替换为 Amazon MSK 集群的 AWS 区域 和 AWS 账户。
   + 对于 *mskClusterName*，提供您的 Amazon MSK 集群的名称。
   + 对于 *cluster-uuid*，提供您的 Amazon MSK 集群的 ARN 中 UUID。
   + 对于 *mskTopicName*，提供您的 Kafka 主题的名称。
   + 对于 *mskGroupName*，提供您的 Kafka 使用者组的名称。

1. 确定 Lambda 发现和连接您的 Amazon MSK 集群所需的 Amazon MSK、Amazon EC2 和 CloudWatch 权限，并记录这些事件。

   `AWSLambdaMSKExecutionRole` 托管策略宽松地定义所需的权限。在以下步骤中使用该策略。

   在生产环境中，评测 `AWSLambdaMSKExecutionRole` 以根据最低权限原则限制您的执行角色策略，然后为您的角色编写一个策略来取代此托管策略。

有关 IAM 策略语言的详细信息，请参阅 [IAM 文档](https://docs.aws.amazon.com//iam/)。

现在，您已经编写了策略文档，请创建一个 IAM 策略，这样便可将其附加到您的角色中。您可以使用控制台按以下步骤完成此操作。

**从策略文档创建 IAM 策略**

1. 登录 AWS 管理控制台，然后通过以下网址打开 IAM 控制台：[https://console.aws.amazon.com/iam/](https://console.aws.amazon.com/iam/)。

1. 在左侧的导航窗格中，选择**策略**。

1. 选择**创建策略**。

1. 在**策略编辑器**部分，选择 **JSON** 选项。

1. 粘贴 *clusterAuthPolicy*。

1. 向策略添加完权限后，选择**下一步**。

1. 在**查看和创建**页面上，为创建的策略键入**策略名称**和**描述**（可选）。查看**此策略中定义的权限**以查看策略授予的权限。

1. 选择**创建策略**可保存新策略。

有关更多信息，请参阅 IAM 文档中的[创建 IAM 策略](https://docs.aws.amazon.com//IAM/latest/UserGuide/access_policies_create.html)。

既然您已经有了适当的 IAM 策略，请创建一个角色并将其附加到该角色。您可以使用控制台按以下步骤完成此操作。

**在 IAM 控制台中创建执行角色**

1. 在 IAM 控制台中，打开 [Roles](https://console.aws.amazon.com/iam/home#/roles)（角色）页面。

1. 请选择 **Create role**（创建角色）。

1. 在**可信实体类型**下，选择 **AWS 服务**。

1. 在 **Use case（使用案例）**下，选择 **Lambda**。

1. 选择**下一步**。

1. 选择以下策略：
   + *clusterAuthPolicy*
   + `AWSLambdaMSKExecutionRole`

1. 选择**下一步**。

1. 对于**角色名称**，输入 *lambdaAuthRole*，然后选择**创建角色**。

有关更多信息，请参阅 [使用执行角色定义 Lambda 函数权限](lambda-intro-execution-role.md)。

## 创建 Lambda 函数以从您的 Amazon MSK 主题中读取
<a name="w2aad101c23c15c35c25"></a>

创建配置为使用您的 IAM 角色的 Lambda 函数。您可以使用控制台创建您的 Lambda 函数。

**使用您的身份验证配置创建 Lambda 函数**

1.  打开 Lambda 控制台并从标题中选择**创建函数**。

1. 选择**从头开始编写**。

1. 对于**函数名称**，请提供您选择的相应名称。

1. 对于**运行时**，选择**最新支持**版本的 `Node.js` 以使用本教程中提供的代码。

1. 选择**更改默认执行角色**。

1. 选择**使用现有角色**。

1. 对于**现有角色**，选择 *lambdaAuthRole*。

在生产环境中，您通常需要向 Lambda 函数的执行角色添加更多策略，以便有效地处理您的 Amazon MSK 事件。有关向角色添加策略的更多信息，请参阅 IAM 文档中的[添加或删除身份权限](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_manage-attach-detach.html#add-policies-console)。

## 创建到 Lambda 函数的事件源映射
<a name="w2aad101c23c15c35c27"></a>

您的 Amazon MSK 事件源映射为 Lambda 服务提供了必要的信息，以在发生相应 Amazon MSK 事件时调用您的 Lambda。您可以使用控制台创建 Amazon MSK 映射。创建 Lambda 触发器，然后事件源映射会自动设置。

**创建 Lambda 触发器（和事件源映射）**

1. 导航到您的 Lambda 函数的概述页面。

1. 在函数概述部分中，选择左下角的**添加触发器**。

1. 在**选择源**下拉列表中，选择 **Amazon MSK**。

1. 请勿设置**身份验证**。

1. 对于 **MSK 集群**，选择集群的名称。

1. 对于**批次大小**，输入 1。此步骤使该功能更易于测试，但并非生产中的理想值。

1. 对于**主题名称**，输入 Kafka 主题名称。

1. 对于**使用者组 ID**，请提供您的 Kafka 使用者组的 ID。

## 更新您的 Lambda 函数以读取流数据
<a name="w2aad101c23c15c35c29"></a>

 Lambda 通过事件方法参数提供有关 Kafka 事件的信息。有关 Amazon MSK 事件的示例结构，请参阅 [事件示例](with-msk.md#msk-sample-event)。在您了解如何解读 Lambda 转发的 Amazon MSK 事件后，您可以修改您的 Lambda 函数代码以使用其提供的信息。

 向您的 Lambda 函数提供以下代码，用于记录 Lambda Amazon MSK 事件的内容以进行测试：

------
#### [ .NET ]

**适用于 .NET 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-msk-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
通过 .NET 将 Amazon MSK 事件与 Lambda 结合使用。  

```
using System.Text;
using Amazon.Lambda.Core;
using Amazon.Lambda.KafkaEvents;


// Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]

namespace MSKLambda;

public class Function
{
    
    
    /// <param name="input">The event for the Lambda function handler to process.</param>
    /// <param name="context">The ILambdaContext that provides methods for logging and describing the Lambda environment.</param>
    /// <returns></returns>
    public void FunctionHandler(KafkaEvent evnt, ILambdaContext context)
    {

        foreach (var record in evnt.Records)
        {
            Console.WriteLine("Key:" + record.Key); 
            foreach (var eventRecord in record.Value)
            {
                var valueBytes = eventRecord.Value.ToArray();    
                var valueText = Encoding.UTF8.GetString(valueBytes);
                
                Console.WriteLine("Message:" + valueText);
            }
        }
    }
    

}
```

------
#### [ Go ]

**适用于 Go 的 SDK V2**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-msk-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
通过 Go 将 Amazon MSK 事件与 Lambda 结合使用。  

```
package main

import (
	"encoding/base64"
	"fmt"

	"github.com/aws/aws-lambda-go/events"
	"github.com/aws/aws-lambda-go/lambda"
)

func handler(event events.KafkaEvent) {
	for key, records := range event.Records {
		fmt.Println("Key:", key)

		for _, record := range records {
			fmt.Println("Record:", record)

			decodedValue, _ := base64.StdEncoding.DecodeString(record.Value)
			message := string(decodedValue)
			fmt.Println("Message:", message)
		}
	}
}

func main() {
	lambda.Start(handler)
}
```

------
#### [ Java ]

**适用于 Java 的 SDK 2.x**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-msk-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
通过 Java 将 Amazon MSK 事件与 Lambda 结合使用。  

```
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.KafkaEvent;
import com.amazonaws.services.lambda.runtime.events.KafkaEvent.KafkaEventRecord;

import java.util.Base64;
import java.util.Map;

public class Example implements RequestHandler<KafkaEvent, Void> {

    @Override
    public Void handleRequest(KafkaEvent event, Context context) {
        for (Map.Entry<String, java.util.List<KafkaEventRecord>> entry : event.getRecords().entrySet()) {
            String key = entry.getKey();
            System.out.println("Key: " + key);

            for (KafkaEventRecord record : entry.getValue()) {
                System.out.println("Record: " + record);

                byte[] value = Base64.getDecoder().decode(record.getValue());
                String message = new String(value);
                System.out.println("Message: " + message);
            }
        }

        return null;
    }
}
```

------
#### [ JavaScript ]

**SDK for JavaScript（v3）**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-msk-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
通过 JavaScript 将 Amazon MSK 事件与 Lambda 结合使用。  

```
exports.handler = async (event) => {
    // Iterate through keys
    for (let key in event.records) {
      console.log('Key: ', key)
      // Iterate through records
      event.records[key].map((record) => {
        console.log('Record: ', record)
        // Decode base64
        const msg = Buffer.from(record.value, 'base64').toString()
        console.log('Message:', msg)
      }) 
    }
}
```
通过 TypeScript 将 Amazon MSK 事件与 Lambda 结合使用。  

```
import { MSKEvent, Context } from "aws-lambda";
import { Buffer } from "buffer";
import { Logger } from "@aws-lambda-powertools/logger";

const logger = new Logger({
  logLevel: "INFO",
  serviceName: "msk-handler-sample",
});

export const handler = async (
  event: MSKEvent,
  context: Context
): Promise<void> => {
  for (const [topic, topicRecords] of Object.entries(event.records)) {
    logger.info(`Processing key: ${topic}`);

    // Process each record in the partition
    for (const record of topicRecords) {
      try {
        // Decode the message value from base64
        const decodedMessage = Buffer.from(record.value, 'base64').toString();

        logger.info({
          message: decodedMessage
        });
      }
      catch (error) {
        logger.error('Error processing event', { error });
        throw error;
      }
    };
  }
}
```

------
#### [ PHP ]

**适用于 PHP 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-msk-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
通过 PHP 将 Amazon MSK 事件与 Lambda 结合使用。  

```
<?php
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

// using bref/bref and bref/logger for simplicity

use Bref\Context\Context;
use Bref\Event\Kafka\KafkaEvent;
use Bref\Event\Handler as StdHandler;
use Bref\Logger\StderrLogger;

require __DIR__ . '/vendor/autoload.php';

class Handler implements StdHandler
{
    private StderrLogger $logger;
    public function __construct(StderrLogger $logger)
    {
        $this->logger = $logger;
    }

    /**
     * @throws JsonException
     * @throws \Bref\Event\InvalidLambdaEvent
     */
    public function handle(mixed $event, Context $context): void
    {
        $kafkaEvent = new KafkaEvent($event);
        $this->logger->info("Processing records");
        $records = $kafkaEvent->getRecords();

        foreach ($records as $record) {
            try {
                $key = $record->getKey();
                $this->logger->info("Key: $key");

                $values = $record->getValue();
                $this->logger->info(json_encode($values));

                foreach ($values as $value) {
                    $this->logger->info("Value: $value");
                }
                
            } catch (Exception $e) {
                $this->logger->error($e->getMessage());
            }
        }
        $totalRecords = count($records);
        $this->logger->info("Successfully processed $totalRecords records");
    }
}

$logger = new StderrLogger();
return new Handler($logger);
```

------
#### [ Python ]

**适用于 Python 的 SDK（Boto3）**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-msk-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
通过 Python 将 Amazon MSK 事件与 Lambda 结合使用。  

```
import base64

def lambda_handler(event, context):
    # Iterate through keys
    for key in event['records']:
        print('Key:', key)
        # Iterate through records
        for record in event['records'][key]:
            print('Record:', record)
            # Decode base64
            msg = base64.b64decode(record['value']).decode('utf-8')
            print('Message:', msg)
```

------
#### [ Ruby ]

**适用于 Ruby 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-msk-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
通过 Ruby 将 Amazon MSK 事件与 Lambda 结合使用。  

```
require 'base64'

def lambda_handler(event:, context:)
  # Iterate through keys
  event['records'].each do |key, records|
    puts "Key: #{key}"

    # Iterate through records
    records.each do |record|
      puts "Record: #{record}"

      # Decode base64
      msg = Base64.decode64(record['value'])
      puts "Message: #{msg}"
    end
  end
end
```

------
#### [ Rust ]

**适用于 Rust 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-msk-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
通过 Rust 将 Amazon MSK 事件与 Lambda 结合使用。  

```
use aws_lambda_events::event::kafka::KafkaEvent;
use lambda_runtime::{run, service_fn, tracing, Error, LambdaEvent};
use base64::prelude::*;
use serde_json::{Value};
use tracing::{info};

/// Pre-Requisites:
/// 1. Install Cargo Lambda - see https://www.cargo-lambda.info/guide/getting-started.html
/// 2. Add packages tracing, tracing-subscriber, serde_json, base64
///
/// This is the main body for the function.
/// Write your code inside it.
/// There are some code example in the following URLs:
/// - https://github.com/awslabs/aws-lambda-rust-runtime/tree/main/examples
/// - https://github.com/aws-samples/serverless-rust-demo/

async fn function_handler(event: LambdaEvent<KafkaEvent>) -> Result<Value, Error> {

    let payload = event.payload.records;

    for (_name, records) in payload.iter() {

        for record in records {

         let record_text = record.value.as_ref().ok_or("Value is None")?;
         info!("Record: {}", &record_text);

         // perform Base64 decoding
         let record_bytes = BASE64_STANDARD.decode(record_text)?;
         let message = std::str::from_utf8(&record_bytes)?;
         
         info!("Message: {}", message);
        }

    }

    Ok(().into())
}

#[tokio::main]
async fn main() -> Result<(), Error> {

    // required to enable CloudWatch error logging by the runtime
    tracing::init_default_subscriber();
    info!("Setup CW subscriber!");

    run(service_fn(function_handler)).await
}
```

------

您可以使用控制台向 Lambda 提供函数代码。

**要使用控制台代码编辑器更新函数代码。**

1. 打开 Lambda 控制台的[“函数”页面](https://console.aws.amazon.com/lambda/home#/functions)，然后选择函数。

1. 选择**代码**选项卡。

1. 在**代码源**窗格中，选择源代码文件并在集成的代码编辑器中对其进行编辑。

1. 在**部署**部分，选择**部署**以更新函数的代码：  
![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/getting-started-tutorial/deploy-console.png)

## 测试您的 Lambda 函数以验证其是否连接到您的 Amazon MSK 主题
<a name="w2aad101c23c15c35c31"></a>

现在，您可以通过查看 CloudWatch 事件日志来验证事件源是否正在调用您的 Lambda。

**验证是否正在调用您的 Lambda 函数**

1. 使用您的 Kafka 管理主机通过 CLI `kafka-console-producer` 生成 Kafka 事件。有关更多信息，请参阅 Kafka 文档中的 [Write some events into the topic](https://kafka.apache.org/documentation/#quickstart_send)。发送足够的事件以填充上一步中定义的事件源映射批次大小所定义的批次，否则 Lambda 将等待更多信息来调用。

1. 如果您的函数运行，则 Lambda 会将发生的事件写入 CloudWatch。在控制台中，导航到您的 Lambda 函数的详细信息页面。

1. 选择 **Configuration（配置）**选项卡。

1. 在侧栏中，选择**监控和操作工具**。

1. 在**日志记录配置**下确定 **CloudWatch 日志组**。日志组应以 `/aws/lambda` 开头。选择日志组的链接。

1. 在 CloudWatch 控制台中，检查**日志事件**，查看 Lambda 已发送到日志流的日志事件。确定是否存在包含来自 Kafka 事件消息的日志事件，如下图所示。如果有，则表示您已成功使用 Lambda 事件源映射将 Lambda 函数连接到 Amazon MSK。  
![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/msk_tut_log.png)

# 将 Lambda 与自行管理的 Apache Kafka 结合使用
<a name="with-kafka"></a>

本主题介绍了如何将 Lambda 与自行管理的 Kafka 集群结合使用。在AWS术语中，自行管理的群集包括非AWS托管 Kafka 集群。例如，可以通过 [Confluent Cloud](https://www.confluent.io/confluent-cloud/) 或 [Redpanda](https://www.redpanda.com/) 等云提供程序来托管 Kafka 集群。

本章说明如何将自托管式 Apache Kafka 集群用作 Lambda 函数的事件源。将自托管式 Apache Kafka 与 Lambda 集成的一般过程包括以下步骤：

1. **[集群和网络设置](with-kafka-cluster-network.md)** — 首先，使用正确的网络配置设置自托管式 Apache Kafka 集群，以允许 Lambda 访问集群。

1. **[事件源映射设置](with-kafka-configure.md)**：然后，创建 Lambda 需要的[事件源映射](invocation-eventsourcemapping.md)资源，以将 Apache Kafka 集群安全地连接到您的函数。

1. **[函数和权限设置](with-kafka-permissions.md)**：最后，请确保您的函数设置正确，并且在其[执行角色](lambda-intro-execution-role.md)中具有必要的权限。

Apache Kafka 作为事件源，运行方式与使用 Amazon Simple Queue Service (Amazon SQS) 或 Amazon Kinesis 相似。Lambda 在内部轮询来自事件源的新消息，然后同步调用目标 Lambda 函数。Lambda 批量读取消息，并将这些消息作为事件有效负载提供给您的函数。最大批处理大小是可配置的（默认值为 100 条消息）。有关更多信息，请参阅 [批处理行为](invocation-eventsourcemapping.md#invocation-eventsourcemapping-batching)。

要优化自我管理的 Apache Kafka 事件源映射的吞吐量，请配置预调配模式。在预调配模式下，您可以定义分配给事件源映射的事件轮询器的最小和最大数量。这可以提高事件源映射处理意外消息激增的能力。有关更多信息，请参阅[预调配模式](kafka-scaling-modes.md#kafka-provisioned-mode)。

**警告**  
Lambda 事件源映射至少处理每个事件一次，有可能出现重复处理记录的情况。为避免与重复事件相关的潜在问题，我们强烈建议您将函数代码设为幂等性。要了解更多信息，请参阅 AWS 知识中心的[如何让我的 Lambda 函数保持幂等性](https://repost.aws/knowledge-center/lambda-function-idempotent)。

对于基于 Kafka 的事件源，Lambda 支持处理控制参数，例如批处理时段和批处理大小。有关更多信息，请参阅 [批处理行为](invocation-eventsourcemapping.md#invocation-eventsourcemapping-batching)。

有关如何使用自行管理的 Kafka 作为事件源的示例，请参阅AWS计算博客上的[使用自行托管的 Apache Kafka 作为 AWS Lambda 事件源](https://aws.amazon.com/blogs/compute/using-self-hosted-apache-kafka-as-an-event-source-for-aws-lambda/)。

**Topics**
+ [

## 事件示例
](#smaa-sample-event)
+ [

# 为 Lambda 配置自托管式 Apache Kafka 集群和网络
](with-kafka-cluster-network.md)
+ [

# 配置 Lambda 执行角色权限
](with-kafka-permissions.md)
+ [

# 为 Lambda 配置自托管式 Apache Kafka 事件源
](with-kafka-configure.md)

## 事件示例
<a name="smaa-sample-event"></a>

当 Lambda 调用 Lambda 函数时，它会在事件参数中发送一批消息。事件负载包含一个消息数组。每个数组项目都包含 Kafka 主题和 Kafka 分区标识符的详细信息，以及时间戳和 base64 编码的消息。

```
{
   "eventSource": "SelfManagedKafka",
   "bootstrapServers":"b-2.demo-cluster-1.a1bcde.c1.kafka.us-east-1.amazonaws.com:9092,b-1.demo-cluster-1.a1bcde.c1.kafka.us-east-1.amazonaws.com:9092",
   "records":{
      "mytopic-0":[
         {
            "topic":"mytopic",
            "partition":0,
            "offset":15,
            "timestamp":1545084650987,
            "timestampType":"CREATE_TIME",
            "key":"abcDEFghiJKLmnoPQRstuVWXyz1234==",
            "value":"SGVsbG8sIHRoaXMgaXMgYSB0ZXN0Lg==",
            "headers":[
               {
                  "headerKey":[
                     104,
                     101,
                     97,
                     100,
                     101,
                     114,
                     86,
                     97,
                     108,
                     117,
                     101
                  ]
               }
            ]
         }
      ]
   }
}
```

# 为 Lambda 配置自托管式 Apache Kafka 集群和网络
<a name="with-kafka-cluster-network"></a>

要将 Lambda 函数连接到自托管式 Apache Kafka 集群，需要正确配置集群及其所处的网络。此页面介绍如何配置集群和网络。如果您的集群和网络已正确配置，请参阅[为 Lambda 配置自托管式 Apache Kafka 事件源](with-kafka-configure.md)配置事件源映射。

**Topics**
+ [

## 自托管式 Apache Kafka 集群设置
](#kafka-cluster-setup)
+ [

## 配置网络安全
](#services-kafka-vpc-config)

## 自托管式 Apache Kafka 集群设置
<a name="kafka-cluster-setup"></a>

可使用云提供商（例如 [Confluent Cloud](https://www.confluent.io/confluent-cloud/) 或 [Redpanda](https://www.redpanda.com/)），托管自托管式 Apache Kafka 集群，或者在自己的基础设施上运行该集群。确保集群配置正确，并且可以从 Lambda 事件源映射要连接的网络访问。

## 配置网络安全
<a name="services-kafka-vpc-config"></a>

要通过事件源映射向 Lambda 提供对自主管理的 Apache Kafka 的完全访问权限，集群必须使用公有端点（公有 IP 地址），或者您必须提供对您在其中创建了集群的 Amazon VPC 的访问权限。

将自行管理的 Apache Kafka 与 Lambda 结合使用时，创建 [AWS PrivateLink VPC 端点](https://docs.aws.amazon.com/vpc/latest/privatelink/create-interface-endpoint.html)，以便您的函数可以访问 Amazon VPC 中的资源。

**注意**  
对于使用事件轮询器的默认（按需）模式的事件源映射函数，需要 AWS PrivateLink VPC 端点。如果事件源映射使用[预调配模式](invocation-eventsourcemapping.md#invocation-eventsourcemapping-provisioned-mode)，则无需配置 AWS PrivateLink VPC 端点。

创建端点以提供对以下资源的访问权限：
+  Lambda：为 Lambda 服务主体创建端点。
+  AWS STS：为 AWS STS 创建端点，以便服务主体代您代入角色。
+  Secrets Manager：如果集群使用 Secrets Manager 来存储凭证，请为 Secrets Manager 创建端点。

也可以在 Amazon VPC 中的每个公有子网上配置 NAT 网关。有关更多信息，请参阅[为连接到 VPC 的 Lambda 函数启用互联网访问权限](configuration-vpc-internet.md)。

为自主管理的 Apache Kafka 创建事件源映射时，Lambda 会检查为 Amazon VPC 配置的子网和安全组是否已经存在弹性网络接口（ENI）。如果 Lambda 发现现有 ENI，则会尝试重用这些 ENI。否则，Lambda 会创建新的 ENI 来连接到事件源并调用函数。

**注意**  
Lambda 函数始终在 Lambda 服务拥有的 Amazon VPC 中运行。函数的 VPC 配置不会影响事件源映射。只有事件源的网络配置才能决定 Lambda 连接到事件源的方式。

为包含集群的 Amazon VPC 配置安全组。默认情况下，自主管理的 Apache Kafka 使用以下端口：`9092`。
+ 入站规则：允许与事件源关联安全组的默认代理端口的所有流量。或者，您可以使用自引用安全组规则允许来自同一安全组内的实例进行访问。
+ 出站规则 – 如果您的函数需要与 AWS 服务进行通信，则允许端口 `443` 上的所有流量向外部目标传输。或者，如果您不需要与其他 AWS 服务通信，也可以使用自引用的安全组规则来限制对代理的访问权限。
+ Amazon VPC 端点入站规则：如果您正在使用 Amazon VPC 端点，则与 Amazon VPC 端点关联的安全组，必须允许来自集群安全组的端口 `443` 上的入站流量。

如果集群使用身份验证，您还可以限制 Secrets Manager 端点的端点策略。要调用 Secrets Manager API，Lambda 会使用函数角色而非 Lambda 服务主体。

**Example VPC 端点策略：Secrets Manager 端点**  

```
{
      "Statement": [
          {
              "Action": "secretsmanager:GetSecretValue",
              "Effect": "Allow",
              "Principal": {
                  "AWS": [
                      "arn:aws::iam::123456789012:role/my-role"
                  ]
              },
              "Resource": "arn:aws::secretsmanager:us-west-2:123456789012:secret:my-secret"
          }
      ]
  }
```

当您使用 Amazon VPC 端点时，AWS 会使用端点的弹性网络接口（ENI）路由 API 调用来调用函数。Lambda 服务主体需要针对使用这些 ENI 的任何角色和函数调用 `lambda:InvokeFunction`。

默认情况下，Amazon VPC 端点具有开放的 IAM 策略，允许对资源进行广泛访问。最佳实践是，将这些策略限制为使用该端点执行所需的操作。为确保事件源映射能够调用 Lambda 函数，VPC 端点策略必须允许 Lambda 服务主体调用 `sts:AssumeRole` 和 `lambda:InvokeFunction`。将 VPC 端点策略限制为仅允许来自组织内部的 API 调用，会导致事件源映射无法正常运行，因此这些策略中需要 `"Resource": "*"`。

以下 VPC 端点策略示例展示了如何向 AWS STS 的 Lambda 服务主体和 Lambda 端点授予所需的访问权限。

**Example VPC 端点策略 – AWS STS 端点**  

```
{
      "Statement": [
          {
              "Action": "sts:AssumeRole",
              "Effect": "Allow",
              "Principal": {
                  "Service": [
                      "lambda.amazonaws.com"
                  ]
              },
              "Resource": "*"
          }
      ]
    }
```

**Example VPC 端点策略 – Lambda 端点**  

```
{
      "Statement": [
          {
              "Action": "lambda:InvokeFunction",
              "Effect": "Allow",
              "Principal": {
                  "Service": [
                      "lambda.amazonaws.com"
                  ]
              },
              "Resource": "*"
          }
      ]
  }
```

# 配置 Lambda 执行角色权限
<a name="with-kafka-permissions"></a>

除了[访问自行托管的 Kafka 集群](kafka-cluster-auth.md)外，您的 Lambda 函数还需要执行各种 API 操作的权限。您可以为函数的[执行角色](lambda-intro-execution-role.md)添加这些权限。如果您的用户需要访问任何 API 操作，请将所需权限添加到 AWS Identity and Access Management（IAM）用户或角色的身份策略中。

**Topics**
+ [

## 所需的 Lambda 函数权限
](#smaa-api-actions-required)
+ [

## 可选的 Lambda 函数权限
](#smaa-api-actions-optional)
+ [

## 向执行角色添加权限
](#smaa-permissions-add-policy)
+ [

## 使用 IAM policy 授予用户访问权限
](#smaa-permissions-add-users)

## 所需的 Lambda 函数权限
<a name="smaa-api-actions-required"></a>

要在 Amazon CloudWatch Logs 中创建日志并将日志存储到日志组，Lambda 函数必须在它的执行角色中具有以下权限：
+ [logs:CreateLogGroup](https://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_CreateLogGroup.html)
+ [logs:CreateLogStream](https://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_CreateLogStream.html)
+ [logs:PutLogEvents](https://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_PutLogEvents.html)

## 可选的 Lambda 函数权限
<a name="smaa-api-actions-optional"></a>

您的 Lambda 函数还可能需要权限来：
+ 描述您的 Secrets Manager 密钥。
+ 访问 AWS Key Management Service（AWS KMS）客户管理的密钥。
+ 访问 Amazon VPC。
+ 将失败调用的记录发送到目标。

### Secrets Manager 和 AWS KMS 权限
<a name="smaa-api-actions-secrets"></a>

根据您为 Kafka 代理配置的访问控制类型，Lambda 函数可能需要访问您的 Secrets Manager 密钥或解密 AWS KMS 客户管理的密钥的权限。要连接到这些资源，函数的执行角色必须具有以下权限：
+ [secretsmanager:GetSecretValue](https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_GetSecretValue.html)
+ [kms:Decrypt](https://docs.aws.amazon.com/kms/latest/APIReference/API_Decrypt.html)

### VPC 权限
<a name="smaa-api-actions-vpc"></a>

如果只有 VPC 内的用户才能访问您自行托管的 Apache Kafka 集群，则 Lambda 函数必须具有访问 Amazon VPC 资源的权限。这些资源包括您的 VPC、子网、安全组和网络接口。要连接到这些资源，函数的执行角色必须具有以下权限：
+ [ec2:CreateNetworkInterface](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_CreateNetworkInterface.html)
+ [ec2:DescribeNetworkInterfaces](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeNetworkInterfaces.html)
+ [ ec2:DescribeVpcs](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeVpcs.html)
+ [ ec2:DeleteNetworkInterface](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DeleteNetworkInterface.html)
+ [ ec2:DescribeSubnets](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeSubnets.html)
+ [ ec2:DescribeSecurityGroups](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeSecurityGroups.html)

## 向执行角色添加权限
<a name="smaa-permissions-add-policy"></a>

要访问自行管理的 Apache Kafka 集群使用的其他AWS服务，Lambda 需使用您在 Lambda 函数[执行角色](lambda-intro-execution-role.md)中定义的权限策略。

默认情况下，Lambda 无权为自行管理的 Apache Kafka 集群执行必需或可选操作。您必须在 [IAM 信任策略](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_update-role-trust-policy.html)中为您的执行角色创建和定义这些操作。此示例演示了如何创建允许 Lambda 访问您的 Amazon VPC 资源的策略。

------
#### [ JSON ]

****  

```
{
        "Version":"2012-10-17",		 	 	 
        "Statement":[
           {
              "Effect":"Allow",
              "Action":[
                 "ec2:CreateNetworkInterface",
                 "ec2:DescribeNetworkInterfaces",
                 "ec2:DescribeVpcs",
                 "ec2:DeleteNetworkInterface",
                 "ec2:DescribeSubnets",
                 "ec2:DescribeSecurityGroups"
              ],
              "Resource":"*"
           }
        ]
     }
```

------

## 使用 IAM policy 授予用户访问权限
<a name="smaa-permissions-add-users"></a>

默认情况下，用户和角色无权执行[事件源 API 操作](invocation-eventsourcemapping.md#event-source-mapping-api)。要向组织或账户中的用户授予访问权限，您可以创建或更新基于身份的策略。有关更多信息，请参阅 *IAM 用户指南*中的[使用策略控制对AWS资源的访问权限](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_controlling.html)。

有关身份验证和授权错误的故障排除，请参阅[Kafka 事件源映射错误的故障排除](with-kafka-troubleshoot.md)

# 为 Lambda 配置自托管式 Apache Kafka 事件源
<a name="with-kafka-configure"></a>

要使自托管式 Apache Kafka 集群作为 Lambda 函数的事件源，您需要创建连接这两个资源的[事件源映射](invocation-eventsourcemapping.md)。此页面介绍如何为自托管式 Apache Kafka 创建事件源映射。

此页面假设您已经正确配置 Kafka 集群及其所在的网络。如果您需要设置集群或网络，请参阅[为 Lambda 配置自托管式 Apache Kafka 集群和网络](with-kafka-cluster-network.md)。

**Topics**
+ [

## 将自托管式 Apache Kafka 集群作为事件源
](#kafka-esm-overview)
+ [

# 在 Lambda 中配置集群身份验证方法
](kafka-cluster-auth.md)
+ [

# 为自托管式 Apache Kafka 事件源创建 Lambda 事件源映射
](kafka-esm-create.md)
+ [

# Lambda 中的所有自托管式 Apache Kafka 事件源配置参数
](kafka-esm-parameters.md)

## 将自托管式 Apache Kafka 集群作为事件源
<a name="kafka-esm-overview"></a>

当您添加 Apache Kafka 或 Amazon MSK 集群作为 Lambda 函数的触发器时，该集群将用作[事件源](invocation-eventsourcemapping.md)。

Lambda 根据您指定的[起始位置](kafka-starting-positions.md)，从您在 [CreateEventSourceMapping](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html) 请求中指定为 `Topics` 的 Kafka 主题读取事件数据。成功进行处理后，会将 Kafka 主题提交给 Kafka 集群。

Lambda 按顺序读取每个 Kafka 主题分区的消息。单个 Lambda 负载可以包含来自多个分区的消息。当有更多记录可用时，Lambda 根据您在 [CreateEventSourceMapping](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html) 请求中指定的 BatchSize 值，继续对记录进行批处理，直到函数赶上主题的速度。

Lambda 处理各个批次后，会提交该批次中消息的偏移量。如果函数为批处理中的任何消息返回错误，Lambda 将重试整批消息，直到处理成功或消息过期为止。您可以将所有重试都失败的记录发送到失败时的目标，以供日后处理。

**注意**  
尽管 Lambda 函数的最大超时限制通常为 15 分钟，但 Amazon MSK、自行管理的 Apache Kafka、Amazon DocumentDB、Amazon MQ for ActiveMQ 和 RabbitMQ 的事件源映射，仅支持最大超时限制为 14 分钟的函数。

# 在 Lambda 中配置集群身份验证方法
<a name="kafka-cluster-auth"></a>

Lambda 支持多种方法来使用自行管理的 Apache Kafka 集群进行身份验证。请确保将 Kafka 集群配置为使用支持的下列身份验证方法之一：有关 Kafka 安全的更多信息，请参阅 Kafka 文档的[安全](http://kafka.apache.org/documentation.html#security)部分。

## SASL/SCRAM 身份验证
<a name="smaa-auth-sasl"></a>

Lambda 支持使用传输层安全性协议（TLS）加密 (`SASL_SSL`) 进行简单身份验证和安全层/加盐质疑应答身份验证机制（SASL/SCRAM）身份验证。Lambda 发送已加密凭据以使用集群进行身份验证。Lambda 不支持使用明文 (`SASL_PLAINTEXT`) 的 SASL/SCRAM。有关 SASL/SCRAM 身份验证的更多信息，请参阅 [RFC 5802](https://tools.ietf.org/html/rfc5802)。

Lambda 还支持 SASL/PLAIN 身份验证。由于此机制使用明文凭证，因此与服务器的连接必须使用 TLS 加密以确保凭证受到保护。

为了 SASL 身份验证，需要将登录凭证作为密钥存储在 AWS Secrets Manager 中。有关使用 Secrets Manager 的更多信息，请参阅《*AWS Secrets Manager 用户指南*》中的 [Create an AWS Secrets Manager secret](https://docs.aws.amazon.com/secretsmanager/latest/userguide/create_secret.html)。

**重要**  
要使用 Secrets Manager 进行身份验证，密钥必须存储在 Lambda 函数所在的同一 AWS 区域中。

## 双向 TLS 身份验证
<a name="smaa-auth-mtls"></a>

双向 TLS（mTLS）在客户端和服务器之间提供双向身份验证。客户端向服务器发送证书以便服务器验证客户端，而服务器又向客户端发送证书以便客户端验证服务器。

在自行托管的 Apache Kafka 中，Lambda 充当客户端。您可以配置客户端证书（作为 Secrets Manager 中的密钥），以使用 Kafka 代理对 Lambda 进行身份验证。客户端证书必须由服务器信任存储中的 CA 签名。

Kafka 集群向 Lambda 发送服务器证书，以便使用 Lambda 对 Kafka 代理进行身份验证。服务器证书可以是公有 CA 证书。也可以是私有 CA/自签名证书。公有 CA 证书必须由 Lambda 信任存储中的证书颁发机构（CA）签名。对于私有 CA /自签名证书，您可以配置服务器根 CA 证书（作为 Secrets Manager 中的密钥）。Lambda 使用根证书来验证 Kafka 代理。

有关 mTLS 的更多信息，请参阅[为作为事件源的 Amazon MSK 引入双向 TLS 身份验证](https://aws.amazon.com/blogs/compute/introducing-mutual-tls-authentication-for-amazon-msk-as-an-event-source)。

## 配置客户端证书密钥
<a name="smaa-auth-secret"></a>

CLIENT\$1CERTIFICATE\$1TLS\$1AUTH 密钥需要证书字段和私有密钥字段。对于加密的私有密钥，密钥需要私有密钥密码。证书和私有密钥必须采用 PEM 格式。

**注意**  
Lambda 支持 [PBES1](https://datatracker.ietf.org/doc/html/rfc2898/#section-6.1)（而不是 PBES2）私有密钥加密算法。

证书字段必须包含证书列表，首先是客户端证书，然后是任何中间证书，最后是根证书。每个证书都必须按照以下结构在新行中启动：

```
-----BEGIN CERTIFICATE-----  
            <certificate contents>
-----END CERTIFICATE-----
```

Secrets Manager 支持最多包含 65536 字节的密钥，这为长证书链提供了充足的空间。

私有密钥必须采用 [PKCS \$18](https://datatracker.ietf.org/doc/html/rfc5208) 格式，并具有以下结构：

```
-----BEGIN PRIVATE KEY-----  
             <private key contents>
-----END PRIVATE KEY-----
```

对于加密的私有密钥，请使用以下结构：

```
-----BEGIN ENCRYPTED PRIVATE KEY-----  
              <private key contents>
-----END ENCRYPTED PRIVATE KEY-----
```

以下示例显示使用加密私有密钥进行 mTLS 身份验证的密钥内容。对于加密的私有密钥，可以在密钥中包含私有密钥密码。

```
{"privateKeyPassword":"testpassword",
"certificate":"-----BEGIN CERTIFICATE-----
MIIE5DCCAsygAwIBAgIRAPJdwaFaNRrytHBto0j5BA0wDQYJKoZIhvcNAQELBQAw
...
j0Lh4/+1HfgyE2KlmII36dg4IMzNjAFEBZiCRoPimO40s1cRqtFHXoal0QQbIlxk
cmUuiAii9R0=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFgjCCA2qgAwIBAgIQdjNZd6uFf9hbNC5RdfmHrzANBgkqhkiG9w0BAQsFADBb
...
rQoiowbbk5wXCheYSANQIfTZ6weQTgiCHCCbuuMKNVS95FkXm0vqVD/YpXKwA/no
c8PH3PSoAaRwMMgOSA2ALJvbRz8mpg==
-----END CERTIFICATE-----",
"privateKey":"-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIFKzBVBgkqhkiG9w0BBQ0wSDAnBgkqhkiG9w0BBQwwGgQUiAFcK5hT/X7Kjmgp
...
QrSekqF+kWzmB6nAfSzgO9IaoAaytLvNgGTckWeUkWn/V0Ck+LdGUXzAC4RxZnoQ
zp2mwJn2NYB7AZ7+imp0azDZb+8YG2aUCiyqb6PnnA==
-----END ENCRYPTED PRIVATE KEY-----"
}
```

## 配置服务器根 CA 证书密钥
<a name="smaa-auth-ca-cert"></a>

如果您的 Kafka 代理使用 TLS 加密（具有由私有 CA 签名的证书），则创建此密钥。您可以将 TLS 加密用于 VPC、SASL/SCRAM、SASL/PLAIN 或 mTLS 身份验证。

服务器根 CA 证书密钥需要一个字段，其中包含 PEM 格式的 Kafka 代理的根 CA 证书。以下示例显示密钥的结构。

```
{"certificate":"-----BEGIN CERTIFICATE-----
MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMx
EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT
HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVs
ZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRlIEF1dG...
-----END CERTIFICATE-----"
}
```

# 为自托管式 Apache Kafka 事件源创建 Lambda 事件源映射
<a name="kafka-esm-create"></a>

要创建事件源映射，您可以使用 Lambda 控制台、[AWS Command Line Interface (CLI)](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) 或 [AWS SDK](https://aws.amazon.com/getting-started/tools-sdks/)。

以下控制台步骤添加自托管式 Apache Kafka 集群作为 Lambda 函数的触发器。这将在后台创建一个事件源映射资源。

## 先决条件
<a name="kafka-esm-prereqs"></a>
+ 自行管理的 Apache Kafka 集群。Lambda 支持 Apache Kafka 版本 0.10.1.0 及更高版本。
+ 一个有权访问您自行管理的 Kafka 集群所用 AWS 资源的[执行角色](lambda-intro-execution-role.md)。

## 添加自行管理的 Kafka 集群（控制台）
<a name="kafka-esm-console"></a>

按照以下步骤将自行管理的 Apache Kafka 集群和 Kafka 主题添加为 Lambda 函数的触发器。

**将 Apache Kafka 触发器添加到 Lambda 函数（控制台）**

1. 打开 Lamba 控制台的 [Functions（函数）页面。](https://console.aws.amazon.com/lambda/home#/functions)

1. 选择 Lambda 函数的名称。

1. 在 **Function overview**（函数概览）下，选择 **Add trigger**（添加触发器）。

1. 在 **Trigger configuration**（触发配置）下，执行以下操作：

   1. 选择 **Apache Kafka** 触发类型。

   1. 对于 **Bootstrap servers**（Bootstrap 引导服务器），输入集群中 Kafka 代理的主机和端口对地址，然后选择 **Add**（添加）。对集群中的每个 Kafka 代理重复此操作。

   1. 对于 **Topic name**（主题名称），输入用于在集群中存储记录的 Kafka 主题的名称。

   1. 如果您配置了预置模式，输入**最少事件轮询器**的值、**最多事件轮询器**的值以及 PollerGroupName 的可选值，以指定同一个事件源 VPC 中的多个 ESM 的分组。

   1. （可选）对于 **Batch size**（批处理大小），输入要在单个批次中接收的最大记录数。

   1. 对于 **Batch window（批处理时段）**，输入 Lambda 在调用函数之前收集记录所花费的最大秒数。

   1. （可选）对于 **Consumer group ID（使用者组 ID）**，输入要加入的 Kafka 使用者组的 ID。

   1. （可选）对于**起始位置**，选择**最新**即可从最新记录开始读取流，选择**最早**即可从最早的可用记录开始读取流，选择**在时间戳处**即可从指定的时间戳开始读取流。

   1. （可选）对于 **VPC**，请为您的 Kafka 集群选择 Amazon VPC。然后，选择 **VPC subnets**（VPC 子网）和 **VPC security groups**（VPC 安全组）。

      如果仅 VPC 内部的用户访问代理，则需要此设置。

      

   1. （可选）对于 **Authentication**（身份验证），请选择 **Add**（添加），然后执行以下操作：

      1. 选择集群中 Kafka 代理的访问权限或身份验证协议。
         + 如果 Kafka 代理使用 SASL/PLAIN 身份验证，请选择 **BASIC\$1AUTH**。
         + 如果代理使用 SASL/SCRAM 身份验证，请选择其中一个 **SASL\$1SCRAM** 协议。
         + 如果要配置 mTLS 身份验证，请选择 **CLIENT\$1CERTIFICATE\$1TLS\$1AUTH** 协议。

      1. 对于 SASL/SCRAM 或 mTLS 身份验证，请选择包含 Kafka 集群凭据的 Secrets Manager 私有密钥。

   1. （可选）对于 **Encryption**（加密），如果您的 Kafka 代理使用由私有 CA 签名的证书，请选择包含根 CA 证书的 Secrets Manager 密钥，Kafka 代理使用该证进行 TLS 加密。

      此设置适用于 SASL/SCRAM 或 SASL/PLAIN 的 TLS 加密，以及 mTLS 身份验证。

   1. 要在禁用状态下创建触发器以进行测试（推荐），请清除 **Enable trigger**（启用触发器）。或者，要立即启用该触发器，请选择 **Enable trigger**（启用触发器）。

1. 要创建触发器，请选择 **Add**（添加）。

## 添加自行管理的 Kafka 集群 (AWS CLI)
<a name="kafka-esm-cli"></a>

使用以下示例 AWS CLI 命令为 Lambda 函数创建和查看自行管理的 Apache Kafka 触发器。

### 使用 SASL/SCRAM
<a name="kafka-esm-cli-create"></a>

如果 Kafka 用户通过互联网访问您的 Kafka 代理，则需要指定为 SASL/SCRAM 身份验证创建的 Secrets Manager。以下示例使用 [create-event-source-mapping](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/create-event-source-mapping.html) AWS CLI 命令将名为 `my-kafka-function` 的 Lambda 函数映射至名为 `AWSKafkaTopic` 的 Kafka 主题。

```
aws lambda create-event-source-mapping \ 
  --topics AWSKafkaTopic \
  --source-access-configuration Type=SASL_SCRAM_512_AUTH,URI=arn:aws:secretsmanager:us-east-1:111122223333:secret:MyBrokerSecretName \
  --function-name arn:aws:lambda:us-east-1:111122223333:function:my-kafka-function \
  --self-managed-event-source '{"Endpoints":{"KAFKA_BOOTSTRAP_SERVERS":["abc3.xyz.com:9092", "abc2.xyz.com:9092"]}}'
```

### 使用 VPC
<a name="kafka-esm-cli-create-vpc"></a>

如果仅 VPC 中的 Kafka 用户访问 Kafka 代理，则必须指定 VPC、子网和 VPC 安全组。以下示例使用 [create-event-source-mapping](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/create-event-source-mapping.html) AWS CLI 命令将名为 `my-kafka-function` 的 Lambda 函数映射至名为 `AWSKafkaTopic` 的 Kafka 主题。

```
aws lambda create-event-source-mapping \ 
  --topics AWSKafkaTopic \
  --source-access-configuration '[{"Type": "VPC_SUBNET", "URI": "subnet:subnet-0011001100"}, {"Type": "VPC_SUBNET", "URI": "subnet:subnet-0022002200"}, {"Type": "VPC_SECURITY_GROUP", "URI": "security_group:sg-0123456789"}]' \
  --function-name arn:aws:lambda:us-east-1:111122223333:function:my-kafka-function \
  --self-managed-event-source '{"Endpoints":{"KAFKA_BOOTSTRAP_SERVERS":["abc3.xyz.com:9092", "abc2.xyz.com:9092"]}}'
```

### 使用 AWS CLI 查看状态
<a name="kafka-esm-cli-view"></a>

以下示例使用 [get-event-source-mapping](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/get-event-source-mapping.html) AWS CLI 命令来描述您创建的事件源映射的状态。

```
aws lambda get-event-source-mapping
              --uuid dh38738e-992b-343a-1077-3478934hjkfd7
```

# Lambda 中的所有自托管式 Apache Kafka 事件源配置参数
<a name="kafka-esm-parameters"></a>

所有 Lambda 事件源类型共享相同的 [CreateEventSourceMapping](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html) 和 [UpdateEventSourceMapping](https://docs.aws.amazon.com/lambda/latest/api/API_UpdateEventSourceMapping.html) API 操作。但是，只有部分参数适用于自托管式 Apache Kafka，如下表中所示。


| 参数 | 必需 | 默认值 | 备注 | 
| --- | --- | --- | --- | 
|  BatchSize  |  否  |  100  |  最大值：10000  | 
|  DestinationConfig  |  N  |  不适用  |  [捕获 Amazon MSK 和自托管式 Apache Kafka 事件源的丢弃批次](kafka-on-failure.md)  | 
|  启用  |  N  |  True  |  | 
|  FilterCriteria  |  N  |  不适用  |  [控制 Lambda 向您的函数发送的事件](invocation-eventfiltering.md)  | 
|  FunctionName  |  是  |  不适用  |    | 
|  KMSKeyArn  |  N  |  不适用  |  [筛选条件的加密](invocation-eventfiltering.md#filter-criteria-encryption)  | 
|  MaximumBatchingWindowInSeconds  |  N  |  500 毫秒  |  [批处理行为](invocation-eventsourcemapping.md#invocation-eventsourcemapping-batching)  | 
|  ProvisionedPollersConfig  |  N  |  `MinimumPollers`：如果未指定，则默认值为 1 `MaximumPollers`：如果未指定，则默认值为 200 `PollerGroupName`：不适用  |  [预置模式](kafka-scaling-modes.md#kafka-provisioned-mode)  | 
|  SelfManagedEventSource  |  Y  | 不适用 |  Kafka 代理列表。只能在 Create（创建）设置  | 
|  SelfManagedKafkaEventSourceConfig  |  N  |  包含 ConsumerGroupId 字段，该字段默认为唯一值。  |  只能在 Create（创建）设置  | 
|  SourceAccessConfigurations  |  否  |  无凭证  |  集群的 VPC 信息或身份验证凭据   对于 SASL\$1PLAIN，设置为 BASIC\$1AUTH  | 
|  StartingPosition  |  Y  |  不适用  |  AT\$1TIMESTAMP、TRIM\$1HORIZON 或 LATEST 只能在 Create（创建）设置  | 
|  StartingPositionTimestamp  |  N  |  不适用  |  当 StartingPosition 设置为 AT\$1TIMESTAMP 时，为必需项  | 
|  标签  |  N  |  不适用  |  [在事件源映射上使用标签](tags-esm.md)  | 
|  主题  |  Y  |  不适用  |  主题名称 只能在 Create（创建）设置  | 

**注意**  
当您指定 `PollerGroupName` 时，同一 Amazon VPC 中的多个 ESM 可以共享事件轮询器单元（EPU）容量。您可以使用此选项来优化 ESM 的预置模式成本。ESM 分组的要求：  
ESM 必须位于同一 Amazon VPC 中
每个轮询器组最多 100 个 ESM
一个组中所有 ESM 的总轮询器数量上限不能超过 2000
您可以更新 `PollerGroupName` 以将 ESM 移动到其他组，也可以通过将 `PollerGroupName` 设置为空字符串（""）从某个组中移除 ESM。

# Lambda 中的 Apache Kafka 事件轮询器扩展模式
<a name="kafka-scaling-modes"></a>

您可以为 Amazon MSK 和自托管式 Apache Kafka 事件源映射选择两种事件轮询器扩展模式之一：
+ [按需模式（默认）](#kafka-default-mode)
+ [预置模式](#kafka-provisioned-mode)

## 按需模式（默认）
<a name="kafka-default-mode"></a>

当您最初创建 Kafka 事件源时，Lambda 会分配默认数量的事件轮询器来处理 Kafka 主题中的所有分区。Lambda 根据消息负载自动扩展或缩减[事件轮询器](invocation-eventsourcemapping.md#invocation-eventsourcemapping-provisioned-mode)的数量。

Lambda 会按一分钟的间隔时间来评估主题中所有分区的偏移滞后。如果偏移延迟太高，则分区接收消息的速度比 Lambda 处理消息的速度更快。如有必要，Lambda 会在主题中添加或删除事件轮询器。添加或删除事件轮询器的自动扩缩过程在评估后的三分钟内发生。

如果目标 Lambda 函数受到限制，Lambda 会减少事件轮询器的数量。此操作通过减少事件轮询器可以检索和发送到函数的消息数来减少函数的工作负载。

## 预置模式
<a name="kafka-provisioned-mode"></a>

对于需要微调事件源映射吞吐量的工作负载，您可以使用预调配模式。在预调配模式下，您可以为预调配事件轮询器数量定义最小和最大限制。这些预调配事件轮询器专用于事件源映射，并且可以通过响应式自动扩缩处理意外的消息激增。对于具有严格性能要求的 Kafka 工作负载，我们建议您使用预调配模式。

在 Lambda 中，事件轮询器是一种计算单元，其吞吐能力因事件源类型的不同而有所差异。对于 Amazon MSK 和自行管理的 Apache Kafka，每个事件轮询器最多可以处理 5 MB/秒的吞吐量或最多 5 次并发调用。例如，如果您的事件源平均生成 1MB 的有效载荷，而您的函数平均运行时间为 1 秒，则单一的 Kafka 事件轮询器能够支持每秒 5MB 的吞吐量以及 5 次并发的 Lambda 调用（假设没有有效载荷的转换操作）。对于 Amazon SQS，每个事件轮询器最多可以处理 1 MB/秒的吞吐量或最多 10 次并发调用。使用预置模式会产生额外成本，具体取决于您的事件轮询器使用情况。有关定价的详细信息，请参阅 [AWS Lambda 定价](https://aws.amazon.com/lambda/pricing/)。

**注意**  
使用预调配模式时，您无需创建 AWS PrivateLink VPC 端点或在网络配置过程中授予关联权限。

在预调配模式下，最小事件轮询器数量 (`MinimumPollers`) 的可接受值范围介于 1 到 200 之间（含首尾）。事件轮询器的最大数量 (`MaximumPollers`) 的可接受值范围介于 1 到 2000 之间（含首尾）。`MaximumPollers` 必须大于或等于 `MinimumPollers`。此外，为了保持分区内的有序处理，Lambda 會将 `MaximumPollers` 限制为主题中的分区数量。

有关选择适当的最小和最大事件轮询器值的更多详细信息，请参阅[最佳实践](#kafka-provisioned-mode-bp)。

您可以使用控制台或 Lambda API 为 Kafka 事件源映射配置预调配模式。

**为现有的事件源映射配置预调配模式（控制台）**

1. 打开 Lamba 控制台的 [Functions](https://console.aws.amazon.com/lambda/home#/functions)（函数）页面。

1. 选择具有要为其配置预调配模式的事件源映射的函数。

1. 选择**配置**，然后选择**触发器**。

1. 选择要为其配置预调配模式的事件源映射，然后选择**编辑**。

1. 在**预置模式下**，选择**配置**。
   + 对于**最少事件轮询器**，输入介于 1 到 200 之间的值。如果未指定值，则 Lambda 将选择默认值 1。
   + 对于**最大事件轮询器**，输入介于 1 到 2000 之间的值。此值必须大于或等于**最少事件轮询器**的值。如果未指定值，则 Lambda 将选择默认值 200。

1. 选择**保存**。

您可以使用 [EventSourceMappingConfiguration](https://docs.aws.amazon.com/lambda/latest/api/API_EventSourceMappingConfiguration.html) 中的 [ProvisionedPollerConfig](https://docs.aws.amazon.com/lambda/latest/api/API_ProvisionedPollerConfig.html) 对象，以编程方式配置预调配模式。例如，以下 [UpdateEventSourceMapping](https://docs.aws.amazon.com/lambda/latest/api/API_UpdateEventSourceMapping.html) CLI 命令将 `MinimumPollers` 值配置为 5，将 `MaximumPollers` 值配置为 100。

```
aws lambda update-event-source-mapping \
    --uuid a1b2c3d4-5678-90ab-cdef-EXAMPLE11111 \
    --provisioned-poller-config '{"MinimumPollers": 5, "MaximumPollers": 100}'
```

配置预调配模式后，您可以通过监控 `ProvisionedPollers` 指标来观测事件轮询器对您的工作负载的使用情况。有关更多信息，请参阅 [事件源映射指标](monitoring-metrics-types.md#event-source-mapping-metrics)。

要禁用预调配模式并返回默认（按需）模式，您可以使用以下 [UpdateEventSourceMapping](https://docs.aws.amazon.com/lambda/latest/api/API_UpdateEventSourceMapping.html) CLI 命令：

```
aws lambda update-event-source-mapping \
    --uuid a1b2c3d4-5678-90ab-cdef-EXAMPLE11111 \
    --provisioned-poller-config '{}'
```

## 高级错误处理和性能功能
<a name="services-kafka-advanced-features"></a>

对于启用了预置模式的 Kafka 事件源映射，您可以配置其他功能来改进错误处理和性能：
+ [重试配置](kafka-retry-configurations.md)：通过最大重试尝试次数、记录期限限制、批次拆分和部分批次响应来控制 Lambda 处理失败记录的方式。
+ [Kafka 失败时的目标](kafka-on-failure-destination.md)：将失败的记录发送到 Kafka 主题以供日后处理或分析。

## 使用预调配模式时的最佳实践和注意事项
<a name="kafka-provisioned-mode-bp"></a>

事件源映射的最小和最大事件轮询器的最佳配置取决于应用程序的性能需求。建议您从默认最小事件轮询器开始，以设定性能配置文件的基准。根据观测到的消息处理模式和所需的性能配置文件调整配置。

对于流量激增且性能需求严格的工作负载，请增加最少的事件轮询器数以处理消息突然激增。要确定所需的最少事件轮询器数，请考虑工作负载的每秒消息数和平均有效载荷大小，并使用单个事件轮询器的吞吐能力（最高 5 Mbps）作为参考。

为了保持分区内的有序处理，Lambda 会将最大事件轮询器数限制为主题中的分区数量。此外，您的事件源映射可以扩展到的最大事件轮询器数取决于函数的并发设置。

激活预调配模式时，更新您的网络设置以删除 AWS PrivateLink VPC 端点和关联的权限。

## 预置模式的成本优化
<a name="kafka-cost-optimization"></a>

### 预置模式定价
<a name="kafka-provisioned-pricing"></a>

预置模式的计费依据的是预置的最低事件轮询器数量，以及自动扩缩过程中所消耗的事件轮询器数量。费用通过名为事件轮询单元（EPU）的计费单位计算。您需根据所使用的 EPU 数量及使用时长进行付费，其计算单位为“事件轮询单元小时”。您可以将预置模式与单个 ESM 一起用于性能敏感型应用程序，也可以将多个 ESM 分组到同一个 VPC 中以共享 EPU 容量和成本。我们将深入探讨两项可帮助您优化预置模式成本的功能。有关定价的详细信息，请参阅 [AWS Lambda 定价](https://aws.amazon.com/lambda/pricing/)。

### 增强型 EPU 利用率
<a name="kafka-enhanced-epu-utilization"></a>

每个 EPU 支持最高 20 MB/s 的事件轮询吞吐能力，并且默认支持 10 个事件轮询器。当您通过设置最小和最大轮询器数量来为 Kafka ESM 创建预置模式时，它会依据每个 EPU 默认配备 10 个事件轮询器这一标准，使用最小轮询器数量来预置 EPU。但是，每个事件轮询器可以独立扩展以最高支持 5 MB/s 的吞吐能力，这可能意味着在特定的 EPU 上需要较少数量的事件轮询器，并且可能会触发 EPU 的扩展。在 EPU 上分配的事件轮询器数量取决于每个事件轮询器所消耗的计算容量。这种提高 EPU 利用率的方法使得具有不同吞吐量需求的事件轮询器能够有效地利用 EPU 容量，从而降低了所有 ESM 的成本。

### ESM 分组
<a name="kafka-esm-grouping-cost"></a>

为了进一步优化您的预置模式成本，您可以将多个 Kafka ESM 进行分组以共享 EPU 容量。借助 ESM 分组和增强的 EPU 利用率，与仅使用单个 ESM 模式运行相比，对于低吞吐量的工作负载，您能够将预置模式成本最高降低 90%。所有需要少于 1 个 EPU 容量的 ESM 都将受益于 ESM 分组。此类 ESM 通常仅需极少的最小事件轮询器数量即可满足其吞吐量需求。此功能将使您能够将预置模式应用于您所有的 Kafka 工作负载，并受益于诸如模式验证、对 Avro/Protobuf 事件的筛选、低延迟调用以及仅在预置模式下才具备的增强型错误处理等功能。

当您为同一 Amazon VPC 中的多个 ESM 配置具有相同值的 `PollerGroupName` 参数时，这些 ESM 将共享 EPU 资源，而不是每个 ESM 都需要专用 EPU 容量。对于每个轮询器组，最多可以对 100 个 ESM 进行分组，并且一个组中所有 ESM 的聚合最大轮询器数不能超过 2000。

#### 要配置 ESM 分组（控制台）
<a name="kafka-esm-grouping-console-cost"></a>

1. 打开 Lamba 控制台的[函数](https://console.aws.amazon.com/lambda/home#/functions)页面。

1. 选择您的函数。

1. 选择**配置**，然后选择**触发器**。

1. 创建新的 Kafka 事件源映射或编辑现有的 Kafka 事件源映射时，请在**预置模式**下选择**配置**。

1. 对于**最少事件轮询器**，输入介于 1 到 200 之间的值。

1. 对于**最大事件轮询器**，输入介于 1 到 2000 之间的值。

1. 对于**轮询器组名称**，输入组的标识符。对于想要组合在一起的其他 ESM，请使用相同的名称。

1. 选择**保存**。

#### 要配置 ESM 分组（AWS CLI）
<a name="kafka-esm-grouping-cli-cost"></a>

以下示例创建具有名为 `production-app-group` 的轮询器组的 ESM。

```
aws lambda create-event-source-mapping \
  --function-name myFunction1 \
  --event-source-arn arn:aws:kafka:us-east-1:123456789012:cluster/MyCluster/abcd1234 \
  --topics topic1 \
  --starting-position LATEST \
  --provisioned-poller-config '{
    "MinimumPollers": 1, 
    "MaximumPollers": 10, 
    "PollerGroupName": "production-app-group"
  }'
```

要将另一个 ESM 添加到同一个组（共享 EPU 容量），请使用相同的 PollerGroupName：

```
aws lambda create-event-source-mapping \
  --function-name myFunction2 \
  --event-source-arn arn:aws:kafka:us-east-1:123456789012:cluster/MyCluster/abcd1234 \
  --topics topic2 \
  --starting-position LATEST \
  --provisioned-poller-config '{
    "MinimumPollers": 1, 
    "MaximumPollers": 10, 
    "PollerGroupName": "production-app-group"
  }'
```

**注意**  
您可以更新 `PollerGroupName` 以将 ESM 移动到其他组，也可以通过传递 `PollerGroupName` 的空字符串（""）从某个组中移除 ESM：

```
# Move ESM to a different group
aws lambda update-event-source-mapping \
  --uuid a1b2c3d4-5678-90ab-cdef-EXAMPLE11111 \
  --provisioned-poller-config '{
    "MinimumPollers": 1, 
    "MaximumPollers": 10, 
    "PollerGroupName": "new-group-name"
  }'

# Remove ESM from group (use dedicated resources)
aws lambda update-event-source-mapping \
  --uuid a1b2c3d4-5678-90ab-cdef-EXAMPLE11111 \
  --provisioned-poller-config '{
    "MinimumPollers": 1, 
    "MaximumPollers": 10, 
    "PollerGroupName": ""
  }'
```

#### 分组策略注意事项
<a name="kafka-grouping-strategy-considerations"></a>
+ **应用程序边界**：对属于相同应用程序或服务的 ESM 进行分组，以实现更好的成本分配和管理。考虑使用 `app-name-environment` 之类的命名约定（例如 `order-processor-prod`）。
+ **流量模式**：避免将具有高吞吐量和峰值流量模式的 ESM 分组，因为这可能会导致资源争用。
+ **影响范围**：考虑一下如果共享基础设施出现故障会带来怎样的影响。同一组中的所有 ESM 都受到共享资源限制的影响。对于任务关键型工作负载，您可能要使用单独的组或专用 ESM。

#### 成本优化示例
<a name="kafka-cost-optimization-example"></a>

设想这样一个场景：您拥有 10 个 ESM，每个都配置了 1 个事件轮询器，且其吞吐量均低于 2 MB/s：

**不分组：**
+ 每个 ESM 都需要自己的 EPU
+ 所需的 EPU 总数：10
+ 每个 EPU 成本：美国东部（弗吉尼亚州北部）每小时 0.185 美元
+ 每月 EPU 成本（720 小时）：10 × 720 × 0.185 美元 = 1332 美元

**进行分组：**
+ 所有 10 个 ESM 均共享 EPU 容量
+ 1 个 EPU 中可容纳 10 个事件轮询器（每个 EPU 支持新增 10 个轮询器）
+ 所需的 EPU 总数：1
+ 每月 EPU 成本（720 小时）：1 × 720 × 0.185 美元 = 133.20 美元
+ **节省成本：90%**（每月可节省 1198.80 美元）

# Lambda 中 Apache Kafka 轮询和流的起始位置
<a name="kafka-starting-positions"></a>

[StartingPosition 参数](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html#lambda-CreateEventSourceMapping-request-StartingPosition)告诉 Lambda 何时开始从 Amazon MSK 或自托管式 Apache Kafka 流中读取消息。有三个选项可供选择：
+ **最新**：Lambda 从 Kafka 主题中的最新记录之后开始读取。
+ **修剪水平线**：Lambda 从 Kafka 主题中最后一条未修剪的记录开始读取。这也是该主题中最早的记录。
+ **位于时间戳**：Lambda 从时间戳定义的位置开始读取，以 Unix 时间秒为单位。使用 [StartingPositionTimestamp 参数](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html#lambda-CreateEventSourceMapping-request-StartingPositionTimestamp)指定时间戳。

事件源映射创建或更新期间的流轮询最终是一致的：
+ 在事件源映射创建期间，可能需要几分钟才能开始轮询来自流的事件。
+ 在事件源映射更新期间，可能需要长达 90 秒才能停止和重新开始轮询来自流的事件。

此行为意味着，如果您指定 `LATEST` 作为流的起始位置，事件源映射可能会在创建或更新期间错过事件。为确保不会错过任何事件，请指定 `TRIM_HORIZON` 或 `AT_TIMESTAMP`。

# Lambda 中可自定义的使用者组 ID
<a name="kafka-consumer-group-id"></a>

将 Amazon MSK 或自托管式 Apache Kafka 设置为事件源时，可指定[使用者组](https://developer.confluent.io/learn-more/kafka-on-the-go/consumer-groups/) ID。此使用者组 ID 是您希望 Lambda 函数加入的 Kafka 使用者组的现有标识符。您可以使用此功能将任何正在进行的 Kafka 记录处理设置从其他使用者无缝迁移到 Lambda。

Kafka 向使用者组中的所有使用者分发消息。如果您指定的使用者组 ID 具有其他活动使用者，则 Lambda 将仅接收来自 Kafka 主题的部分消息。如果希望 Lambda 处理主题中的所有消息，请关闭该使用者组中的任何其他使用者。

此外，如果指定了使用者组 ID，而 Kafka 找到了具有相同 ID 的有效现有使用者组，则 Lambda 会忽略事件源映射的 [StartingPosition](kafka-starting-positions.md) 参数。相反，Lambda 开始根据使用者组的已提交偏移量处理记录。如果指定了使用者组 ID，而 Kafka 找不到现有使用者组，则 Lambda 会使用指定的 `StartingPosition` 配置事件源。

在所有 Kafka 事件源中，您指定的使用者组 ID 必须是唯一的。在使用指定的使用者组 ID 创建 Kafka 事件源映射后，无法更新此值。

# 从 Amazon MSK 和自托管式 Apache Kafka 事件源中筛选事件
<a name="kafka-filtering"></a>

您可以使用事件筛选，控制 Lambda 将流或队列中的哪些记录发送给函数。有关事件筛选工作原理的一般信息，请参阅 [控制 Lambda 向您的函数发送的事件](invocation-eventfiltering.md)。

**注意**  
Amazon MSK 和自托管式 Apache Kafka 事件源映射仅支持对 `value` 键进行筛选。

**Topics**
+ [

## Kafka 事件筛选基础知识
](#filtering-kafka)

## Kafka 事件筛选基础知识
<a name="filtering-kafka"></a>

假设创建者以有效的 JSON 格式或纯字符串的形式将消息写入 Kafka 集群中的主题。示例记录将如下所示，`value` 字段中的消息会转换为 Base64 编码字符串。

```
{
    "mytopic-0":[
        {
            "topic":"mytopic",
            "partition":0,
            "offset":15,
            "timestamp":1545084650987,
            "timestampType":"CREATE_TIME",
            "value":"SGVsbG8sIHRoaXMgaXMgYSB0ZXN0Lg==",
            "headers":[]
        }
    ]
}
```

假设 Apache Kafka 创建器以如下 JSON 格式将消息写入主题。

```
{
    "device_ID": "AB1234",
    "session":{
        "start_time": "yyyy-mm-ddThh:mm:ss",
        "duration": 162
    }
}
```

您可以使用 `value` 键筛选记录。假设您只想筛选 `device_ID` 以字母 AB 开头的记录。`FilterCriteria` 对象将如下所示。

```
{
    "Filters": [
        {
            "Pattern": "{ \"value\" : { \"device_ID\" : [ { \"prefix\": \"AB\" } ] } }"
        }
    ]
}
```

为了更清楚起见，以下是在纯 JSON 中展开的筛选条件 `Pattern` 的值。

```
{
    "value": {
        "device_ID": [ { "prefix": "AB" } ]
      }
}
```

您可以使用控制台、AWS CLI 或 AWS SAM 模板添加筛选条件。

------
#### [ Console ]

要使用控制台添加此筛选条件，请按照 [将筛选条件附加到事件源映射（控制台）](invocation-eventfiltering.md#filtering-console) 中的说明，为**筛选条件**输入以下字符串。

```
{ "value" : { "device_ID" : [ { "prefix":  "AB" } ] } }
```

------
#### [ AWS CLI ]

要使用 AWS Command Line Interface（AWS CLI）创建包含这些筛选条件的新事件源映射，请运行以下命令。

```
aws lambda create-event-source-mapping \
    --function-name my-function \
    --event-source-arn arn:aws:kafka:us-east-2:123456789012:cluster/my-cluster/b-8ac7cc01-5898-482d-be2f-a6b596050ea8 \
    --filter-criteria '{"Filters": [{"Pattern": "{ \"value\" : { \"device_ID\" : [ { \"prefix\":  \"AB\" } ] } }"}]}'
```

要将这些筛选条件添加到现有事件源映射中，请运行以下命令。

```
aws lambda update-event-source-mapping \
    --uuid "a1b2c3d4-5678-90ab-cdef-11111EXAMPLE" \
    --filter-criteria '{"Filters": [{"Pattern": "{ \"value\" : { \"device_ID\" : [ { \"prefix\":  \"AB\" } ] } }"}]}'
```

------
#### [ AWS SAM ]

要使用 AWS SAM 添加此筛选条件，请将以下代码段添加到事件源的 YAML 模板中。

```
FilterCriteria:
  Filters:
    - Pattern: '{ "value" : { "device_ID" : [ { "prefix":  "AB" } ] } }'
```

------

通过 Kafka，您还可以筛选消息为纯字符串的记录。假设您想忽略字符串为“错误”的消息。`FilterCriteria` 对象将如下所示。

```
{
    "Filters": [
        {
            "Pattern": "{ \"value\" : [ { \"anything-but\": [ \"error\" ] } ] }"
        }
    ]
}
```

为了更清楚起见，以下是在纯 JSON 中展开的筛选条件 `Pattern` 的值。

```
{
    "value": [
        {
        "anything-but": [ "error" ]
        }
    ]
}
```

您可以使用控制台、AWS CLI 或 AWS SAM 模板添加筛选条件。

------
#### [ Console ]

要使用控制台添加此筛选条件，请按照 [将筛选条件附加到事件源映射（控制台）](invocation-eventfiltering.md#filtering-console) 中的说明，为**筛选条件**输入以下字符串。

```
{ "value" : [ { "anything-but": [ "error" ] } ] }
```

------
#### [ AWS CLI ]

要使用 AWS Command Line Interface（AWS CLI）创建包含这些筛选条件的新事件源映射，请运行以下命令。

```
aws lambda create-event-source-mapping \
    --function-name my-function \
    --event-source-arn arn:aws:kafka:us-east-2:123456789012:cluster/my-cluster/b-8ac7cc01-5898-482d-be2f-a6b596050ea8 \
    --filter-criteria '{"Filters": [{"Pattern": "{ \"value\" : [ { \"anything-but\": [ \"error\" ] } ] }"}]}'
```

要将这些筛选条件添加到现有事件源映射中，请运行以下命令。

```
aws lambda update-event-source-mapping \
    --uuid "a1b2c3d4-5678-90ab-cdef-11111EXAMPLE" \
    --filter-criteria '{"Filters": [{"Pattern": "{ \"value\" : [ { \"anything-but\": [ \"error\" ] } ] }"}]}'
```

------
#### [ AWS SAM ]

要使用 AWS SAM 添加此筛选条件，请将以下代码段添加到事件源的 YAML 模板中。

```
FilterCriteria:
  Filters:
    - Pattern: '{ "value" : [ { "anything-but": [ "error" ] } ] }'
```

------

Kafka 消息必须是 UTF-8 编码的字符串，可以是纯字符串或 JSON 格式。这是因为 Lambda 在应用筛选条件之前将 Kafka 字节数组解码为 UTF-8。如果您的消息使用另一种编码，例如 UTF-16 或 ASCII，或者消息格式与 `FilterCriteria` 格式不匹配，则 Lambda 仅处理元数据筛选条件。下表汇总了具体行为：


| 传入消息格式 | 消息属性的筛选条件模式格式 | 导致的操作 | 
| --- | --- | --- | 
|  纯字符串  |  纯字符串  |  Lambda 根据您的筛选条件进行筛选。  | 
|  纯字符串  |  数据属性中没有筛选条件模式  |  Lambda 根据您的筛选条件进行筛选（仅限其他元数据属性）。  | 
|  纯字符串  |  有效 JSON  |  Lambda 根据您的筛选条件进行筛选（仅限其他元数据属性）。  | 
|  有效 JSON  |  纯字符串  |  Lambda 根据您的筛选条件进行筛选（仅限其他元数据属性）。  | 
|  有效 JSON  |  数据属性中没有筛选条件模式  |  Lambda 根据您的筛选条件进行筛选（仅限其他元数据属性）。  | 
|  有效 JSON  |  有效 JSON  |  Lambda 根据您的筛选条件进行筛选。  | 
|  非 UTF-8 编码字符串  |  JSON、纯字符串或无模式  |  Lambda 根据您的筛选条件进行筛选（仅限其他元数据属性）。  | 

# 在 Lambda 中使用带有 Kafka 事件源的架构注册表
<a name="services-consume-kafka-events"></a>

 架构注册表可帮助您定义和管理数据流架构。架构定义了数据记录的结构和格式。在 Kafka 事件源映射的背景下，您可以配置架构注册表，以便在 Kafka 消息到达您的 Lambda 函数之前，根据预定义的架构对其结构和格式进行验证。这为您的应用程序增加了一层数据治理，使您能够通过事件筛选高效管理数据格式、确保架构合规性并优化成本。

 此功能适用于所有编程语言，但应考虑以下要点：
+ Powertools for Lambda 提供对 Java、Python 和 TypeScript 的特定支持，保持了与现有 Kafka 开发模式的一致性，并且无需自定义反序列化代码即可让您直接访问业务对象
+ 此功能仅适用于使用预置模式的事件源映射。架构注册表不支持按需模式下的事件源映射。如果您使用的是预配模式并且配置了架构注册表，则除非先移除架构注册表配置，否则无法更改为按需模式。有关更多信息，请参阅 [预调配模式](invocation-eventsourcemapping.md#invocation-eventsourcemapping-provisioned-mode)。
+ 每个事件源映射 (ESM) 只能配置一个架构注册表。将架构注册表与 Kafka 事件源一起使用可能会增加 Lambda Event Poller Unit (EPU) 的使用量，这是预置模式的定价维度。

**Topics**
+ [

## 架构注册表选项
](#services-consume-kafka-events-options)
+ [

## Lambda 如何对 Kafka 消息执行架构验证
](#services-consume-kafka-events-how)
+ [

## 配置 Kafka 架构注册表
](#services-consume-kafka-events-config)
+ [

## Avro 和 Protobuf 的筛选
](#services-consume-kafka-events-filtering)
+ [

## 有效载荷格式和反序列化行为
](#services-consume-kafka-events-payload)
+ [

## 在 Lambda 函数中使用反序列化数据
](#services-consume-kafka-events-payload-examples)
+ [

## 架构注册表的身份验证方法
](#services-consume-kafka-events-auth)
+ [

## 架构注册表问题的错误处理和故障排除
](#services-consume-kafka-events-troubleshooting)

## 架构注册表选项
<a name="services-consume-kafka-events-options"></a>

 Lambda 支持以下架构注册表选项：
+ [AWS Glue 架构注册表](https://docs.aws.amazon.com/glue/latest/dg/schema-registry.html)
+ [Confluent Cloud 架构注册表](https://docs.confluent.io/platform/current/schema-registry/index.html)
+ [自托管式 Confluent 架构注册表](https://docs.confluent.io/platform/current/schema-registry/index.html)

 您的架构注册表支持验证以下数据格式的消息：
+ Apache Avro
+ 协议缓冲区 (Protobuf)
+ JSON 架构 (JSON-SE)

 要使用架构注册表，首先应确保您的事件源映射处于预置模式。当您使用架构注册表时，Lambda 会将有关架构的元数据添加到有效载荷中。有关更多信息，请参阅[有效载荷格式和反序列化行为](#services-consume-kafka-events-payload)。

## Lambda 如何对 Kafka 消息执行架构验证
<a name="services-consume-kafka-events-how"></a>

 配置架构注册表时，Lambda 会对每条 Kafka 消息执行以下步骤：

1. Lambda 会轮询您集群中的 Kafka 记录。

1. Lambda 会根据您架构注册表中的特定架构来验证记录中的选定消息属性。
   + 如果在注册表中找不到与消息关联的架构，Lambda 会将消息发送到带有原因代码 `SCHEMA_NOT_FOUND` 的 DLQ。

1. Lambda 会根据架构注册表配置对消息进行反序列化以验证消息。如果配置了事件筛选，则 Lambda 会根据配置的筛选条件执行筛选。
   + 如果反序列化失败，Lambda 会将带有原因代码 `DESERIALIZATION_ERROR` 的消息发送到 DLQ。如果未配置 DLQ，则 Lambda 会丢弃此消息。

1. 如果消息已通过架构注册表验证，且未按您的筛选条件进行筛选，则 Lambda 会通过该消息来调用您的函数。

 此功能旨在验证已使用与架构注册表集成的 Kafka 客户端生成的消息。我们建议您将 Kafka 生产者配置为使用架构注册表来创建格式正确的消息。

## 配置 Kafka 架构注册表
<a name="services-consume-kafka-events-config"></a>

 通过以下控制台步骤，您可以将 Kafka 架构注册表配置添加到您的事件源映射中。

**要将 Kafka 架构注册表配置添加到您的事件源映射中**

1. 打开 Lambda 控制台的[函数页面](https://console.aws.amazon.com/lambda/home#/functions)。

1. 选择**配置**。

1. 选择**触发器**。

1. 选择要为其配置架构注册表的 Kafka 事件源映射，然后选择**编辑**。

1. 在**事件轮询器配置**下，选择**配置架构注册表**。您的事件源映射必须处于预置模式才能看到此选项。

1. 对于**架构注册表 URI**，请输入您 AWS Glue 架构注册表的 ARN，或者您 Confluent Cloud 架构注册表或自托管式 Confluent 架构注册表的 HTTPS URL。

1. 以下配置步骤将告诉 Lambda 如何访问您的架构注册表。有关更多信息，请参阅 [架构注册表的身份验证方法](#services-consume-kafka-events-auth)。
   + 对于**访问配置类型**，请选择 Lambda 用于访问您架构注册表的身份验证类型。
   + 对于**访问配置 URI**，请输入 Secrets Manager 密钥的 ARN，以便使用您的架构注册表进行身份验证（如果适用）。确保您函数的[执行角色](with-msk-permissions.md)包含正确的权限。

1. 只有当您的架构注册表由私有证书颁发机构 (CA) 或不在 Lambda 信任存储中的证书颁发机构 (CA) 签名时，**加密**字段才适用。如果适用，请提供包含您的注册表用于 TLS 加密的私有 CA 证书的私有密钥。

1. 对于**事件记录格式**，请选择您希望 Lambda 在架构验证后向函数传送记录的方式。有关更多信息，请参阅[有效载荷格式示例](#services-consume-kafka-events-payload)。
   + 如果您选择 **JSON**，Lambda 将以标准 JSON 格式提供您在下面的架构验证属性中选择的属性。对于您未选择的属性，Lambda 会按原样提供这些属性。
   + 如果您选择 **SOURCE**，Lambda 将以原始 SOURCE 格式提供您在下面的架构验证属性中选择的属性。

1. 对于**架构验证属性**，请选择您希望 Lambda 使用您的架构注册表进行验证和反序列化的消息属性。您必须至少选择一个**键**或**值**。如果您选择了 JSON 作为事件记录格式，Lambda 还会在将所选消息属性发送到您的函数之前对其进行反序列化。有关更多信息，请参阅[有效载荷格式和反序列化行为](#services-consume-kafka-events-payload)。

1. 选择**保存**。

 您还可以使用 Lambda API 通过架构注册表配置创建或更新您的事件源映射。以下示例演示了如何使用 AWS CLI 来配置 AWS Glue 或 Confluent 架构注册表，这与 *AWS Lambda API 参考*中的 [UpdateEventSourceMapping](https://docs.aws.amazon.com/lambda/latest/api/API_UpdateEventSourceMapping.html) 和 [CreateEventSourceMapping](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html) API 操作相对应：

**重要**  
如果您要使用 AWS CLI 或 `update-event-source-mapping` API 更新任何架构注册表配置字段，则必须更新架构注册表配置的所有字段。

------
#### [ Create Event Source Mapping ]

```
aws lambda create-event-source-mapping \
  --function-name my-schema-validator-function \
  --event-source-arn arn:aws:kafka:us-east-1:123456789012:cluster/my-cluster/a1b2c3d4-5678-90ab-cdef-11111EXAMPLE \
  --topics my-kafka-topic \
  --provisioned-poller-config MinimumPollers=1,MaximumPollers=1 \
  --amazon-managed-kafka-event-source-mapping '{
      "SchemaRegistryConfig" : {
          "SchemaRegistryURI": "https://abcd-ef123.us-west-2.aws.confluent.cloud",
          "AccessConfigs": [{
              "Type": "BASIC_AUTH", 
              "URI": "arn:aws:secretsmanager:us-east-1:123456789012:secret:secretName"
          }],
          "EventRecordFormat": "JSON",
          "SchemaValidationConfigs": [
          { 
              "Attribute": "KEY" 
          },
          { 
              "Attribute": "VALUE" 
          }]
      }
  }'
```

------
#### [ Update AWS Glue Schema Registry ]

```
aws lambda update-event-source-mapping \
    --uuid a1b2c3d4-5678-90ab-cdef-EXAMPLE11111 \
    --amazon-managed-kafka-event-source-mapping '{
        "SchemaRegistryConfig" : {
            "SchemaRegistryURI": "arn:aws:glue:us-east-1:123456789012:registry/registryName",
            "EventRecordFormat": "JSON",
            "SchemaValidationConfigs": [
            { 
                "Attribute": "KEY" 
            },
            { 
                "Attribute": "VALUE" 
            }]
        }
    }'
```

------
#### [ Update Confluent Schema Registry with Authentication ]

```
aws lambda update-event-source-mapping \
    --uuid a1b2c3d4-5678-90ab-cdef-EXAMPLE11111 \
    --amazon-managed-kafka-event-source-mapping '{
        "SchemaRegistryConfig" : {
            "SchemaRegistryURI": "https://abcd-ef123.us-west-2.aws.confluent.cloud",
            "AccessConfigs": [{
                "Type": "BASIC_AUTH", 
                "URI": "arn:aws:secretsmanager:us-east-1:123456789012:secret:secretName"
            }],
            "EventRecordFormat": "JSON",
            "SchemaValidationConfigs": [
            { 
                "Attribute": "KEY" 
            },
            { 
                "Attribute": "VALUE" 
            }]
        }
    }'
```

------
#### [ Update Confluent Schema Registry without Authentication ]

```
aws lambda update-event-source-mapping \
    --uuid a1b2c3d4-5678-90ab-cdef-EXAMPLE11111 \
    --amazon-managed-kafka-event-source-mapping '{
        "SchemaRegistryConfig" : {
            "SchemaRegistryURI": "https://abcd-ef123.us-west-2.aws.confluent.cloud",
            "EventRecordFormat": "JSON",
            "SchemaValidationConfigs": [
            { 
                "Attribute": "KEY" 
            },
            { 
                "Attribute": "VALUE" 
            }]
        }
    }'
```

------
#### [ Remove Schema Registry Configuration ]

要从事件源映射中删除架构注册表配置，您可以使用 *AWS Lambda API 参考*中的 CLI 命令 [UpdateEventSourceMapping](https://docs.aws.amazon.com/lambda/latest/api/API_UpdateEventSourceMapping.html)。

```
aws lambda update-event-source-mapping \
    --uuid a1b2c3d4-5678-90ab-cdef-EXAMPLE11111 \
    --amazon-managed-kafka-event-source-mapping '{
        "SchemaRegistryConfig" : {}
    }'
```

------

## Avro 和 Protobuf 的筛选
<a name="services-consume-kafka-events-filtering"></a>

 在架构注册表中使用 Avro 或 Protobuf 格式时，您可以对您的 Lambda 函数应用“事件筛选”。架构验证后，筛选模式将应用于您数据的反序列化经典 JSON 表示形式。例如，通过定义包括价格在内的产品详细信息的 Avro 架构，您可以根据价格值筛选消息：

**注意**  
 反序列化时，Avro 会转换为标准 JSON，这意味着它无法直接转换回 Avro 对象。如果您需要转换为 Avro 对象，请改用 SOURCE 格式。  
 对于 Protobuf 的反序列化，生成的 JSON 中的字段名称与模式中定义的字段名称一致，而不是像 Protobuf 通常那样被转换为驼峰式拼写法。创建筛选模式时，请记住这一点。

```
aws lambda create-event-source-mapping \
    --function-name myAvroFunction \
    --topics myAvroTopic \
    --starting-position TRIM_HORIZON \
    --kafka-bootstrap-servers '["broker1:9092", "broker2:9092"]' \
    --schema-registry-config '{
        "SchemaRegistryURI": "arn:aws:glue:us-east-1:123456789012:registry/myAvroRegistry",
        "EventRecordFormat": "JSON",
        "SchemaValidationConfigs": [
            { 
                "Attribute": "VALUE" 
            }
        ]
    }' \
    --filter-criteria '{
        "Filters": [
            {
                "Pattern": "{ \"value\" : { \"field_1\" : [\"value1\"], \"field_2\" : [\"value2\"] } }"
            }
        ]
    }'
```

 在此示例中，筛选模式将分析 `value` 对象，匹配 `field_1` 中带 `"value1"` 的消息和 `field_2` 中带 `"value2"` 的消息。在 Lambda 将消息从 Avro 格式转换为 JSON 之后，会根据反序列化的数据对筛选条件进行评估。

 有关事件筛选的更多信息，请参阅 [Lambda 事件筛选](invocation-eventfiltering.md)。

## 有效载荷格式和反序列化行为
<a name="services-consume-kafka-events-payload"></a>

 使用架构注册表时，Lambda 会将最终有效载荷以类似于[常规事件有效载荷](with-msk.md#msk-sample-event)的格式传递给您的函数，并附加一些字段。这些附加字段取决于 `SchemaValidationConfigs` 参数。对于您选择进行验证的每个属性（键或值），Lambda 都会向有效载荷添加相应的架构元数据。

**注意**  
您必须将您的 [aws-lambda-java-events](https://github.com/aws/aws-lambda-java-libs/tree/main/aws-lambda-java-events) 更新到 3.16.0 或更高版本，才能使用架构元数据字段。

 例如，如果您验证了 `value` 字段，Lambda 就会在您的有效载荷中添加一个名为 `valueSchemaMetadata` 的字段。同样，对于 `key` 字段，Lambda 会添加一个名为 `keySchemaMetadata` 的字段。此元数据包含有关用于验证的数据格式和架构 ID 的信息：

```
"valueSchemaMetadata": {
    "dataFormat": "AVRO",
    "schemaId": "a1b2c3d4-5678-90ab-cdef-EXAMPLE11111"
}
```

 该 `EventRecordFormat` 参数可以设置为 `JSON` 或 `SOURCE`，这决定了 Lambda 在将经架构验证的数据传送到您的函数之前如何处理这些数据。每个选项有着不同的处理能力：
+ `JSON` - Lambda 将经过验证的属性反序列化为标准 JSON 格式，使数据可以直接用于支持原生 JSON 的语言。当您不需要保留原始的二进制格式或使用生成的类时，这种格式是比较理想的选择。
+ `SOURCE` - Lambda 将数据的原始二进制格式保留为 Base64 编码的字符串，让您可以直接转换为 Avro 或 Protobuf 对象。当使用强类型语言或需要维护 Avro 或 Protobuf 架构的全部功能时，这种格式是必不可少的。

基于这些格式特征和特定语言的考虑因素，我们建议使用以下格式：


**基于编程语言的推荐格式**  

| 语言 | Avro | Protobuf | JSON | 
| --- | --- | --- | --- | 
| Java | SOURCE | SOURCE | SOURCE | 
| Python | JSON | JSON | JSON | 
| NodeJS | JSON | JSON | JSON | 
| .NET | SOURCE | SOURCE | SOURCE | 
| 其他 | JSON | JSON | JSON | 

以下各节详细描述了这些格式，并提供了每种格式的有效载荷示例。

### JSON 格式
<a name="services-consume-kafka-events-payload-json"></a>

 如果您选择将 `JSON` 作为 `EventRecordFormat`，Lambda 会验证并反序列化您在 `SchemaValidationConfigs` 字段中选择的消息属性（`key` 和/或 `value` 属性）。Lambda 在您的函数中将这些选定的属性作为其标准 JSON 表示形式的 base64 编码字符串提供。

**注意**  
 反序列化时，Avro 会转换为标准 JSON，这意味着它无法直接转换回 Avro 对象。如果您需要转换为 Avro 对象，请改用 SOURCE 格式。  
 对于 Protobuf 的反序列化，生成的 JSON 中的字段名称与模式中定义的字段名称一致，而不是像 Protobuf 通常那样被转换为驼峰式拼写法。创建筛选模式时，请记住这一点。

 下面是一个有效载荷示例，假设您选择 `JSON` 作为 `EventRecordFormat`，`key` 和 `value` 属性作为 `SchemaValidationConfigs`：

```
{
   "eventSource":"aws:kafka",
   "eventSourceArn":"arn:aws:kafka:us-east-1:123456789012:cluster/vpc-2priv-2pub/a1b2c3d4-5678-90ab-cdef-EXAMPLE11111-1",
   "bootstrapServers":"b-2.demo-cluster-1.a1bcde.c1.kafka.us-east-1.amazonaws.com:9092,b-1.demo-cluster-1.a1bcde.c1.kafka.us-east-1.amazonaws.com:9092",
   "records":{
      "mytopic-0":[
         {
            "topic":"mytopic",
            "partition":0,
            "offset":15,
            "timestamp":1545084650987,
            "timestampType":"CREATE_TIME",
            "key":"abcDEFghiJKLmnoPQRstuVWXyz1234==", //Base64 encoded string of JSON
            "keySchemaMetadata": {
                "dataFormat": "AVRO",
                "schemaId": "a1b2c3d4-5678-90ab-cdef-EXAMPLE11111"
            },
            "value":"abcDEFghiJKLmnoPQRstuVWXyz1234", //Base64 encoded string of JSON
            "valueSchemaMetadata": {
                "dataFormat": "AVRO",
                "schemaId": "a1b2c3d4-5678-90ab-cdef-EXAMPLE11111"
            },
            "headers":[
               {
                  "headerKey":[
                     104,
                     101,
                     97,
                     100,
                     101,
                     114,
                     86,
                     97,
                     108,
                     117,
                     101
                  ]
               }
            ]
         }
      ]
   }
}
```

 在本示例中：
+ `key` 和 `value` 都是反序列化后的 JSON 表示形式的 base64 编码字符串。
+ Lambda 包含 `keySchemaMetadata` 和 `valueSchemaMetadata` 中两个属性的架构元数据。
+ 您的函数可以解码 `key` 和 `value` 字符串，以便访问反序列化的 JSON 数据。

 对于 Python 或 Node.js 等非强类型语言，建议使用 JSON 格式。这些语言原生支持将 JSON 转换为对象。

### SOURCE 格式
<a name="services-consume-kafka-events-payload-source"></a>

 如果您选择 `SOURCE` 作为 `EventRecordFormat`，Lambda 仍会根据架构注册表验证记录，但无需反序列化即可将原始二进制数据传送到您的函数。此二进制数据以原始字节数据的 Base64 编码字符串形式提供，并删除了生产者附加的元数据。因此，您可以直接将原始二进制数据转换为您函数代码中的 Avro 和 Protobuf 对象。我们建议使用 Powertools for AWS Lambda，它会反序列化原始二进制数据，并直接为您提供 Avro 和 Protobuf 对象。

 例如，如果您将 Lambda 配置为同时验证 `key` 和 `value` 属性，但使用了 `SOURCE` 格式，则您的函数将接收到如下所示的有效载荷：

```
{
    "eventSource": "aws:kafka",
    "eventSourceArn": "arn:aws:kafka:us-east-1:123456789012:cluster/vpc-2priv-2pub/a1b2c3d4-5678-90ab-cdef-EXAMPLE11111-1",
    "bootstrapServers": "b-2.demo-cluster-1.a1bcde.c1.kafka.us-east-1.amazonaws.com:9092,b-1.demo-cluster-1.a1bcde.c1.kafka.us-east-1.amazonaws.com:9092",
    "records": {
        "mytopic-0": [
            {
                "topic": "mytopic",
                "partition": 0,
                "offset": 15,
                "timestamp": 1545084650987,
                "timestampType": "CREATE_TIME",
                "key": "abcDEFghiJKLmnoPQRstuVWXyz1234==", // Base64 encoded string of Original byte data, producer-appended metadata removed
                "keySchemaMetadata": {
                    "dataFormat": "AVRO",
                    "schemaId": "a1b2c3d4-5678-90ab-cdef-EXAMPLE11111"
                },
                "value": "abcDEFghiJKLmnoPQRstuVWXyz1234==", // Base64 encoded string of Original byte data, producer-appended metadata removed
                "valueSchemaMetadata": {
                    "dataFormat": "AVRO",
                    "schemaId": "a1b2c3d4-5678-90ab-cdef-EXAMPLE11111"
                },
                "headers": [
                    {
                        "headerKey": [
                            104,
                            101,
                            97,
                            100,
                            101,
                            114,
                            86,
                            97,
                            108,
                            117,
                            101
                        ]
                    }
                ]
            }
        ]
    }
}
```

 在本示例中：
+ `key` 和 `value` 均包含 Base64 编码字符串形式的原始二进制数据。
+ 您的函数需要使用相应的库来处理反序列化。

 如果您使用的是 AVRO 生成的对象或 Protobuf 生成的对象，尤其是使用 Java 函数时，建议选择 `SOURCE` for `EventRecordFormat`。这是因为 Java 是强类型的，需要针对 Avro 和 Protobuf 格式使用特定的反序列化器。在您的函数代码中，您可以使用自己喜欢的 Avro 或 Protobuf 库来反序列化数据。

## 在 Lambda 函数中使用反序列化数据
<a name="services-consume-kafka-events-payload-examples"></a>

Powertools for AWS Lambda 可根据您使用的格式，在函数代码中帮助您反序列化 Kafka 记录。该实用程序通过处理数据转换和提供即用型对象，简化了 Kafka 记录的使用。

 要在您的函数中使用 Powertools for AWS Lambda，您需要在构建 Lambda 函数时将 Powertools for AWS Lambda 添加为一个层或将其作为依赖项。有关设置说明和更多信息，请参阅您首选语言的 Powertools for AWS Lambda 文档：
+ [Powertools for AWS Lambda (Java)](https://docs.powertools.aws.dev/lambda/java/latest/utilities/kafka/)
+ [Powertools for AWS Lambda (Python)](https://docs.powertools.aws.dev/lambda/python/latest/utilities/kafka/)
+ [Powertools for AWS Lambda (TypeScript)](https://docs.powertools.aws.dev/lambda/typescript/latest/features/kafka/)
+ [Powertools for AWS Lambda (.NET)](https://docs.powertools.aws.dev/lambda/dotnet/utilities/kafka/)

**注意**  
在使用架构注册表集成时，您可以选择 `SOURCE` 或 `JSON` 格式。如下所示，每个选项支持不同的序列化格式：  


| Format | 支持 | 
| --- | --- | 
|  SOURCE  |  Avro 和 Protobuf（使用 Lambda 架构注册表集成）  | 
|  JSON  |  JSON 数据  | 

 在使用 `SOURCE` 或 `JSON` 格式时，您可以使用 Powertools for AWS 来帮助您反序列化您函数代码中的数据。以下是如何处理不同数据格式的示例：

------
#### [ AVRO ]

Java 示例：

```
package org.demo.kafka;

import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.demo.kafka.avro.AvroProduct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;

import software.amazon.lambda.powertools.kafka.Deserialization;
import software.amazon.lambda.powertools.kafka.DeserializationType;
import software.amazon.lambda.powertools.logging.Logging;

public class AvroDeserializationFunction implements RequestHandler<ConsumerRecords<String, AvroProduct>, String> {

    private static final Logger LOGGER = LoggerFactory.getLogger(AvroDeserializationFunction.class);

    @Override
    @Logging
    @Deserialization(type = DeserializationType.KAFKA_AVRO)
    public String handleRequest(ConsumerRecords<String, AvroProduct> records, Context context) {
        for (ConsumerRecord<String, AvroProduct> consumerRecord : records) {
            LOGGER.info("ConsumerRecord: {}", consumerRecord);

            AvroProduct product = consumerRecord.value();
            LOGGER.info("AvroProduct: {}", product);

            String key = consumerRecord.key();
            LOGGER.info("Key: {}", key);
        }

        return "OK";
    }

}
```

Python 示例：

```
from aws_lambda_powertools.utilities.kafka_consumer.kafka_consumer import kafka_consumer
from aws_lambda_powertools.utilities.kafka_consumer.schema_config import SchemaConfig
from aws_lambda_powertools.utilities.kafka_consumer.consumer_records import ConsumerRecords

from aws_lambda_powertools.utilities.typing import LambdaContext
from aws_lambda_powertools import Logger

logger = Logger(service="kafkaConsumerPowertools")

value_schema_str = open("customer_profile.avsc", "r").read()

schema_config = SchemaConfig(
value_schema_type="AVRO",
value_schema=value_schema_str)

@kafka_consumer(schema_config=schema_config)
def lambda_handler(event: ConsumerRecords, context:LambdaContext):

  for record in event.records:
      value = record.value
      logger.info(f"Received value: {value}")
```

TypeScript 示例：

```
import { kafkaConsumer } from '@aws-lambda-powertools/kafka';

import type { ConsumerRecords } from '@aws-lambda-powertools/kafka/types';
import { Logger } from '@aws-lambda-powertools/logger';
import type { Context } from 'aws-lambda';

const logger = new Logger();

type Value = {
    id: number;
    name: string;
    price: number;
};

const schema = '{   
    "type": "record",   
    "name": "Product",   
    "fields": [     
        { "name": "id", "type": "int" },     
        { "name": "name", "type": "string" },     
        { "name": "price", "type": "double" }   
    ] 
}';

export const handler = kafkaConsumer<string, Value>(
    (event: ConsumerRecords<string, Value>, _context: Context) => {
        for (const record of event.records) {
            logger.info(Processing record with key: ${record.key});
            logger.info(Record value: ${JSON.stringify(record.value)});
            // You can add more processing logic here
        }
    },
    {
        value: {
            type: 'avro',
            schema: schema,
        },
    }
);
```

.NET 示例：

```
using Amazon.Lambda.Core;
using AWS.Lambda.Powertools.Kafka;
using AWS.Lambda.Powertools.Kafka.Avro;
using AWS.Lambda.Powertools.Logging;
using Com.Example;

// Assembly attribute to enable the Lambda function's Kafka event to be converted into a .NET class.
[assembly: LambdaSerializer(typeof(PowertoolsKafkaAvroSerializer))]

namespace ProtoBufClassLibrary;

public class Function
{
    public string FunctionHandler(ConsumerRecords<string, CustomerProfile> records, ILambdaContext context)
    {
        foreach (var record in records)
        {
            Logger.LogInformation("Processing messagem from topic: {topic}", record.Topic);
            Logger.LogInformation("Partition: {partition}, Offset: {offset}", record.Partition, record.Offset);
            Logger.LogInformation("Produced at: {timestamp}", record.Timestamp);
            
            foreach (var header in record.Headers.DecodedValues())
            {
                Logger.LogInformation($"{header.Key}: {header.Value}");
            }
            
            Logger.LogInformation("Processing order for: {fullName}", record.Value.FullName);
        }
    
        return "Processed " + records.Count() + " records";
    }
}
```

------
#### [ PROTOBUF ]

Java 示例：

```
package org.demo.kafka;

import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.demo.kafka.protobuf.ProtobufProduct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;

import software.amazon.lambda.powertools.kafka.Deserialization;
import software.amazon.lambda.powertools.kafka.DeserializationType;
import software.amazon.lambda.powertools.logging.Logging;

public class ProtobufDeserializationFunction
        implements RequestHandler<ConsumerRecords<String, ProtobufProduct>, String> {

    private static final Logger LOGGER = LoggerFactory.getLogger(ProtobufDeserializationFunction.class);

    @Override
    @Logging
    @Deserialization(type = DeserializationType.KAFKA_PROTOBUF)
    public String handleRequest(ConsumerRecords<String, ProtobufProduct> records, Context context) {
        for (ConsumerRecord<String, ProtobufProduct> consumerRecord : records) {
            LOGGER.info("ConsumerRecord: {}", consumerRecord);

            ProtobufProduct product = consumerRecord.value();
            LOGGER.info("ProtobufProduct: {}", product);

            String key = consumerRecord.key();
            LOGGER.info("Key: {}", key);
        }

        return "OK";
    }

}
```

Python 示例：

```
from aws_lambda_powertools.utilities.kafka_consumer.kafka_consumer import kafka_consumer

from aws_lambda_powertools.utilities.kafka_consumer.schema_config import SchemaConfig
from aws_lambda_powertools.utilities.kafka_consumer.consumer_records import ConsumerRecords

from aws_lambda_powertools.utilities.typing import LambdaContext
from aws_lambda_powertools import Logger

from user_pb2 import User # protobuf generated class

logger = Logger(service="kafkaConsumerPowertools")

schema_config = SchemaConfig(
value_schema_type="PROTOBUF",
value_schema=User)

@kafka_consumer(schema_config=schema_config)
def lambda_handler(event: ConsumerRecords, context:LambdaContext):

  for record in event.records:
      value = record.value
      logger.info(f"Received value: {value}")
```

TypeScript 示例：

```
import { kafkaConsumer } from '@aws-lambda-powertools/kafka';
import type { ConsumerRecords } from '@aws-lambda-powertools/kafka/types';
import { Logger } from '@aws-lambda-powertools/logger';
import type { Context } from 'aws-lambda';
import { Product } from './product.generated.js';

const logger = new Logger();

type Value = {
    id: number;
    name: string;
    price: number;
};

export const handler = kafkaConsumer<string, Value>(
    (event: ConsumerRecords<string, Value>, _context: Context) => {
        for (const record of event.records) {
            logger.info(Processing record with key: ${record.key});
            logger.info(Record value: ${JSON.stringify(record.value)});
        }
    },
    {
        value: {
            type: 'protobuf',
            schema: Product,
        },
    }
);
```

.NET 示例：

```
using Amazon.Lambda.Core;
using AWS.Lambda.Powertools.Kafka;
using AWS.Lambda.Powertools.Kafka.Protobuf;
using AWS.Lambda.Powertools.Logging;
using Com.Example;

// Assembly attribute to enable the Lambda function's Kafka event to be converted into a .NET class.
[assembly: LambdaSerializer(typeof(PowertoolsKafkaProtobufSerializer))]

namespace ProtoBufClassLibrary;

public class Function
{
    public string FunctionHandler(ConsumerRecords<string, CustomerProfile> records, ILambdaContext context)
    {
        foreach (var record in records)
        {
            Logger.LogInformation("Processing messagem from topic: {topic}", record.Topic);
            Logger.LogInformation("Partition: {partition}, Offset: {offset}", record.Partition, record.Offset);
            Logger.LogInformation("Produced at: {timestamp}", record.Timestamp);
            
            foreach (var header in record.Headers.DecodedValues())
            {
                Logger.LogInformation($"{header.Key}: {header.Value}");
            }
            
            Logger.LogInformation("Processing order for: {fullName}", record.Value.FullName);
        }
    
        return "Processed " + records.Count() + " records";
    }
}
```

------
#### [ JSON ]

Java 示例：

```
package org.demo.kafka;

import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;

import software.amazon.lambda.powertools.kafka.Deserialization;
import software.amazon.lambda.powertools.kafka.DeserializationType;
import software.amazon.lambda.powertools.logging.Logging;

public class JsonDeserializationFunction implements RequestHandler<ConsumerRecords<String, Product>, String> {

    private static final Logger LOGGER = LoggerFactory.getLogger(JsonDeserializationFunction.class);

    @Override
    @Logging
    @Deserialization(type = DeserializationType.KAFKA_JSON)
    public String handleRequest(ConsumerRecords<String, Product> consumerRecords, Context context) {
        for (ConsumerRecord<String, Product> consumerRecord : consumerRecords) {
            LOGGER.info("ConsumerRecord: {}", consumerRecord);

            Product product = consumerRecord.value();
            LOGGER.info("Product: {}", product);

            String key = consumerRecord.key();
            LOGGER.info("Key: {}", key);
        }

        return "OK";
    }
}
```

Python 示例：

```
from aws_lambda_powertools.utilities.kafka_consumer.kafka_consumer import kafka_consumer

from aws_lambda_powertools.utilities.kafka_consumer.schema_config import SchemaConfig
from aws_lambda_powertools.utilities.kafka_consumer.consumer_records import ConsumerRecords

from aws_lambda_powertools.utilities.typing import LambdaContext
from aws_lambda_powertools import Logger

logger = Logger(service="kafkaConsumerPowertools")

schema_config = SchemaConfig(value_schema_type="JSON")

@kafka_consumer(schema_config=schema_config)
def lambda_handler(event: ConsumerRecords, context:LambdaContext):

  for record in event.records:
      value = record.value
      logger.info(f"Received value: {value}")
```

TypeScript 示例：

```
import { kafkaConsumer } from '@aws-lambda-powertools/kafka';
import type { ConsumerRecords } from '@aws-lambda-powertools/kafka/types';
import { Logger } from '@aws-lambda-powertools/logger';
import type { Context } from 'aws-lambda';

const logger = new Logger();

type Value = {
    id: number;
    name: string;
    price: number;
};

export const handler = kafkaConsumer<string, Value>(
    (event: ConsumerRecords<string, Value>, _context: Context) => {
        for (const record of event.records) {
            logger.info(Processing record with key: ${record.key});
            logger.info(Record value: ${JSON.stringify(record.value)});
            // You can add more processing logic here
        }
    },
    {
        value: {
            type: 'json',
        },
    }
);
```

.NET 示例：

```
using Amazon.Lambda.Core;
using AWS.Lambda.Powertools.Kafka;
using AWS.Lambda.Powertools.Kafka.Json;
using AWS.Lambda.Powertools.Logging;
using Com.Example;

// Assembly attribute to enable the Lambda function's Kafka event to be converted into a .NET class.
[assembly: LambdaSerializer(typeof(PowertoolsKafkaJsonSerializer))]

namespace JsonClassLibrary;

public class Function
{
    public string FunctionHandler(ConsumerRecords<string, CustomerProfile> records, ILambdaContext context)
    {
        foreach (var record in records)
        {
            Logger.LogInformation("Processing messagem from topic: {topic}", record.Topic);
            Logger.LogInformation("Partition: {partition}, Offset: {offset}", record.Partition, record.Offset);
            Logger.LogInformation("Produced at: {timestamp}", record.Timestamp);
            
            foreach (var header in record.Headers.DecodedValues())
            {
                Logger.LogInformation($"{header.Key}: {header.Value}");
            }
            
            Logger.LogInformation("Processing order for: {fullName}", record.Value.FullName);
        }
    
        return "Processed " + records.Count() + " records";
    }
}
```

------

## 架构注册表的身份验证方法
<a name="services-consume-kafka-events-auth"></a>

 要使用架构注册表，Lambda 需要能够安全地访问它。如果您使用的是 AWS Glue 架构注册表，则 Lambda 将依赖于 IAM 身份验证。这意味着，您函数的[执行角色](lambda-intro-execution-role.md)必须具有以下权限才能访问 AWS Glue 注册表：
+ *AWS Glue Web API* 参考中的 [GetRegistry](https://docs.aws.amazon.com/glue/latest/webapi/API_GetRegistry.html) 
+ *AWS Glue Web API 参考*中的 [GetSchemaVersion](https://docs.aws.amazon.com/glue/latest/webapi/API_GetSchemaVersion.html) 

所需 IAM 策略的示例：

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "glue:GetRegistry",
                "glue:GetSchemaVersion"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}
```

------

**注意**  
 对于 AWS Glue 架构注册表，如果您为 AWS Glue 注册表提供 `AccessConfigs`，Lambda 将返回一个验证异常。

如果您使用的是 Confluent 架构注册表，您可以为 [KafkaSchemaRegistryAccessConfig](https://docs.aws.amazon.com/lambda/latest/api/API_KafkaSchemaRegistryAccessConfig) 对象的 `Type` 参数选择三种支持的身份验证方法之一：
+ **BASIC\$1AUTH** — Lambda 通过用户名和密码或 API 密钥和 API 密钥身份验证来访问您的注册表。如果您选择此选项，请在 URI 字段中提供包含您凭证的 Secrets Manager ARN。
+ **CLIENT\$1CERTIFICATE\$1TLS\$1AUTH** — Lambda 使用基于客户端证书的双向 TLS 身份验证。要使用此选项，Lambda 需要同时访问证书和私钥。请在 URI 字段中提供包含这些凭证的 Secrets Manager ARN。
+ **NO\$1AUTH** — 公有 CA 证书必须由 Lambda 信任存储中的证书颁发机构 (CA) 签名。对于私有 CA /自签名证书，您可以配置服务器根 CA 证书。要使用此选项，请省略 `AccessConfigs` 参数。

 此外，如果 Lambda 需要访问私有 CA 证书来验证您架构注册表的 TLS 证书，请选择 `SERVER_ROOT_CA_CERT` 作为 `Type`，并在 URI 字段中为证书提供 Secrets Manager ARN。

**注意**  
 要在控制台中配置 `SERVER_ROOT_CA_CERT` 选项，请在**加密**字段中提供包含证书的密钥 ARN。

 架构注册表的身份验证配置与您为 Kafka 集群配置的任何身份验证是分开的。即使它们使用相似的身份验证方法，也必须分别进行配置。

## 架构注册表问题的错误处理和故障排除
<a name="services-consume-kafka-events-troubleshooting"></a>

在 Amazon MSK 事件源中使用架构注册表时，您可能会遇到各种错误。本节提供有关常见问题以及如何解决这些问题的指导。

### 配置错误
<a name="consume-kafka-events-troubleshooting-configuration-errors"></a>

当您进行架构注册表配置时，会发生这些错误。

需要启用预置模式  
**错误消息**：`SchemaRegistryConfig is only available for Provisioned Mode. To configure Schema Registry, please enable Provisioned Mode by specifying MinimumPollers in ProvisionedPollerConfig.`  
**解决方案：**通过在 `ProvisionedPollerConfig` 中配置 `MinimumPollers` 参数，为事件源映射启用预置模式。

架构注册表 URL 无效  
**错误消息**：`Malformed SchemaRegistryURI provided. Please provide a valid URI or ARN. For example, https://schema-registry.example.com:8081 or arn:aws:glue:us-east-1:123456789012:registry/ExampleRegistry.`  
**解决方案：**为 Confluent 架构注册表提供有效的 HTTPS URL，或者为 AWS Glue 架构注册表提供有效的 ARN。

事件记录格式无效或缺失  
**错误消息**：`EventRecordFormat is a required field for SchemaRegistryConfig. Please provide one of supported format types: SOURCE, JSON.`  
**解决方案：**在架构注册表配置中将 SOURCE 或 JSON 指定为 eventRecordFormat。

重复验证属性  
**错误消息**：`Duplicate KEY/VALUE Attribute in SchemaValidationConfigs. SchemaValidationConfigs must contain at most one KEY/VALUE Attribute.`  
**解决方案：**从您的 SchemaValidationConfigs 中删除重复的 KEY 或 VALUE 属性。每种属性类型只能出现一次。

缺少验证配置  
**错误消息**：`SchemaValidationConfigs is a required field for SchemaRegistryConfig.`  
**解决方案：**将 SchemaValidationConfigs 添加到您的配置中，并且至少指定一个验证属性（KEY 或 VALUE）。

### 访问权和权限错误
<a name="consume-kafka-events-troubleshooting-access-errors"></a>

当 Lambda 由于权限或身份验证问题而无法访问架构注册表时，就会发生这些错误。

AWS Glue 架构注册表访问被拒绝  
**错误消息**：`Cannot access Glue Schema with provided role. Please ensure the provided role can perform the GetRegistry and GetSchemaVersion Actions on your schema.`  
**解决方案：**将所需的权限（`glue:GetRegistry` 和 `glue:GetSchemaVersion`）添加到您函数的执行角色中。

Confluent 架构注册表访问被拒绝  
**错误消息**：`Cannot access Confluent Schema with the provided access configuration.`  
**解决方案：**验证您的身份验证凭证（存储在 Secrets Manager 中）是否正确并具有访问架构注册表所需的权限。

跨账户 AWS Glue 架构注册表  
**错误消息**：`Cross-account Glue Schema Registry ARN not supported.`  
**解决方案：**使用与您的 Lambda 函数位于同一 AWS 账户中的 AWS Glue 架构注册表。

跨区域 AWS Glue 架构注册表  
**错误消息**：`Cross-region Glue Schema Registry ARN not supported.`  
**解决方案：**使用与您的 Lambda 函数位于同一区域中的 AWS Glue 架构注册表。

密钥访问问题  
**错误消息**：`Lambda received InvalidRequestException from Secrets Manager.`  
**解决方案：**验证您的函数执行角色是否具有访问该密钥的权限，并且如果从其他账户访问，该密钥未使用默认的 AWS KMS 密钥进行加密。

### 连接错误
<a name="consume-kafka-events-troubleshooting-connection-errors"></a>

当 Lambda 无法与架构注册表建立连接时，就会发生这些错误。

VPC 连接问题  
**错误消息**：`Cannot connect to your Schema Registry. Your Kafka cluster's VPC must be able to connect to the schema registry. You can provide access by configuring AWS PrivateLink or a NAT Gateway or VPC Peering between Kafka Cluster VPC and the schema registry VPC.`  
**解决方案：**配置您的 VPC 网络，以允许通过 AWS PrivateLink、NAT 网关或 VPC 对等来连接架构注册表。

TLS 握手失败  
**错误消息**：`Unable to establish TLS handshake with the schema registry. Please provide correct CA-certificate or client certificate using Secrets Manager to access your schema registry.`  
**解决方案：**在 Secrets Manager 中验证您的 CA 证书和客户端证书（适用于 mTLS）是否正确且配置正确。

节流  
**错误消息**：`Receiving throttling errors when accessing the schema registry. Please increase API TPS limits for your schema registry.`  
**解决方案：**提高架构注册表的 API 速率限制或降低来自应用程序的请求速率。

自托管式架构注册表错误  
**错误消息**：`Lambda received an internal server an unexpected error from the provided self-managed schema registry.`  
**解决方案：**检查自托管式架构注册表服务器的运行状况和配置。

# 低延迟处理 Kafka 事件源
<a name="with-kafka-low-latency"></a>

AWS Lambda 原生支持低延迟事件处理，适用于需要端到端延迟始终小于 100 毫秒的应用程序。本页提供了启用低延迟工作流程的配置详细信息和建议。

## 启用低延迟处理
<a name="enable-low-latency"></a>

要对 Kafka 事件源映射启用低延迟处理，需要进行以下基本配置：
+ 启用预置模式。有关更多信息，请参阅 [预置模式](kafka-scaling-modes.md#kafka-provisioned-mode)。
+ 将事件源映射的 `MaximumBatchingWindowInSeconds` 参数设置为 0。有关更多信息，请参阅 [批处理行为](invocation-eventsourcemapping.md#invocation-eventsourcemapping-batching)。

## 微调低延迟 Kafka ESM
<a name="recommendations-low-latency"></a>

请考虑以下优化 Kafka 事件源映射实现低延迟的建议：

### 预置模式配置
<a name="recommendations-pollers"></a>

在 Kafka 事件源映射的预置模式下，Lambda 支持通过配置名为**事件轮询器**的最小和最大资源数来微调配事件源映射的吞吐量。事件轮询器（或**轮询器**）代表一种计算资源，支持预置模式下的事件源映射，最多可分配 5MB/s 的吞吐量。每个事件轮询器最多支持 5 次 Lambda 并发调用。

要为应用程序确定最佳的轮询器配置，需要考虑您的峰值摄取率和处理要求。让我们看一个简化的例子：

一个批次大小为 20 条记录，目标函数平均持续时间为 50 毫秒，则吞吐量限制为 5 Mb/s 的情况下，每个轮询器每秒可以处理 2,000 条记录。计算公式为：（20 条记录 x 1000ms/50ms）x 5 次 Lambda 并发调用。因此，如果所需的峰值摄取率为每秒 20,000 条记录，则至少需要 10 个事件轮询器。

**注意**  
我们建议配置更多的事件轮询器作为缓冲，以避免持续以满负荷运行。

预置模式会根据配置的最小和最大事件轮询器内的流量模式自动扩展**事件轮询器**，这可能会触发再平衡，从而带来额外的延迟。您可以通过为最小和最大**事件轮询器**配置相同的值来禁用自动扩缩。

### 其他注意事项
<a name="additional-considerations-low-latency"></a>

其他一些考虑因素包括：
+ 调用 Lambda 目标函数时的冷启动可能会增加端到端的延迟。要降低这种风险，需要考虑在事件源映射的目标函数中启用[预置并发](provisioned-concurrency.md)或 [SnapStart](snapstart.md)。此外，还要优化函数的内存分配，以确保执行的一致性和最佳效果。
+ `MaximumBatchingWindowInSeconds` 设置为 0 时，Lambda 会立即处理所有可用记录，而无需等待填充整个批次大小。例如，如果批次大小设置为 1,000 条记录，但只有 100 条记录可用，那么 Lambda 会立即处理这 100 条记录，而不必等待累积到全部 1,000 条记录。

**重要**  
低延迟处理的最佳配置因特定工作负载而不同。我们强烈建议根据实际工作负载测试不同的配置，以确定适合使用案例的最佳设置。

# 为 Kafka 事件源配置错误处理控件
<a name="kafka-retry-configurations"></a>

您可以配置 Lambda 为 Kafka 事件源映射处理错误及重试情况的方式。这些配置可帮助您控制 Lambda 如何处理失败的记录和管理重试行为。

## 可用重试配置
<a name="kafka-retry-options"></a>

以下重试配置适用于 Amazon MSK 和自行管理的 Kafka 事件源：
+ **最大重试次数**：您函数返回错误时 Lambda 重试的最大次数。这不包括初始的调用尝试。默认值为 -1（无限）。当您同时配置无限重试和[失败目标](kafka-on-failure-destination.md)时，Lambda 会自动应用最多 10 次重试尝试。
+ **最长记录期限**：Lambda 发送到您的函数的记录的最长期限。默认值为 -1（无限）。
+ **出现错误时拆分批**：当您的函数返回错误时，将批次分成两个较小的批次，然后分别对每个批次进行重试。这有助于隔离有问题的记录。
+ **部分批次响应**：使您的函数能够返回有关批次中哪些记录在处理过程中出现错误的信息，这样 Lambda 就可以仅对那些失败的记录进行重试。

## 配置错误处理控件（控制台）
<a name="kafka-retry-console"></a>

在 Lambda 控制台中创建或更新 Kafka 事件源映射时，您可以配置重试行为。

**要为 Kafka 事件源配置重试行为（控制台）**

1. 打开 Lamba 控制台的[函数](https://console.aws.amazon.com/lambda/home#/functions)页面。

1. 选择您的函数名称。

1. 请执行以下操作之一：
   + 要添加新的 Kafka 触发器，请在**函数概述**下选择**添加触发器**。
   + 要修改现有的 Kafka 触发器，请选择该触发器，然后选择**编辑**。

1. 在**事件轮询器配置**下，选择预置模式以配置错误处理控件：

   1. 对于**重试尝试**，请输入最大重试次数（0-10000，或者 -1 表示无限次）。

   1. 对于**最长记录期限**，输入以秒为单位的最长期限（60-604800，或 -1 表示无限期）。

   1. 要在出现错误时启用批次拆分，请选择**出现错误时拆分批**。

   1. 要启用部分批次响应，请选择 **ReportBatchItemFailures**。

1. 选择**添加**或**保存**。

## 配置重试行为（AWS CLI）
<a name="kafka-retry-cli"></a>

使用以下 AWS CLI 命令为 Kafka 事件源映射配置重试行为。

### 使用重试配置创建事件源映射
<a name="kafka-retry-cli-create"></a>

以下示例创建了一个带有错误处理控件的自行管理 Kafka 事件源映射：

```
aws lambda create-event-source-mapping \
  --function-name my-kafka-function \
  --topics my-kafka-topic \
  --source-access-configuration Type=SASL_SCRAM_512_AUTH,URI=arn:aws:secretsmanager:us-east-1:111122223333:secret:MyBrokerSecretName \
  --self-managed-event-source '{"Endpoints":{"KAFKA_BOOTSTRAP_SERVERS":["abc.xyz.com:9092"]}}' \
  --starting-position LATEST \
  --provisioned-poller-config MinimumPollers=1,MaximumPollers=1 \
  --maximum-retry-attempts 3 \
  --maximum-record-age-in-seconds 3600 \
  --bisect-batch-on-function-error \
  --function-response-types "ReportBatchItemFailures"
```

对于 Amazon MSK 事件源：

```
aws lambda create-event-source-mapping \
  --event-source-arn arn:aws:kafka:us-east-1:111122223333:cluster/my-cluster/fc2f5bdf-fd1b-45ad-85dd-15b4a5a6247e-2 \
  --topics AWSMSKKafkaTopic \
  --starting-position LATEST \
  --function-name my-kafka-function \
  --source-access-configurations '[{"Type": "SASL_SCRAM_512_AUTH","URI": "arn:aws:secretsmanager:us-east-1:111122223333:secret:my-secret"}]' \
  --provisioned-poller-config MinimumPollers=1,MaximumPollers=1 \
  --maximum-retry-attempts 3 \
  --maximum-record-age-in-seconds 3600 \
  --bisect-batch-on-function-error \
  --function-response-types "ReportBatchItemFailures"
```

### 更新重试配置
<a name="kafka-retry-cli-update"></a>

使用 `update-event-source-mapping` 命令修改现有事件源映射的重试配置：

```
aws lambda update-event-source-mapping \
  --uuid 12345678-1234-1234-1234-123456789012 \
  --maximum-retry-attempts 5 \
  --maximum-record-age-in-seconds 7200 \
  --bisect-batch-on-function-error \
  --function-response-types "ReportBatchItemFailures"
```

## PartialBatchResponse
<a name="kafka-partial-batch-response"></a>

部分批次响应，也称为 ReportBatchItemFailures，是 Lambda 与 Kafka 源集成时错误处理的关键功能。如果没有此功能，当批次中的某一项目出现错误时，就会导致需要重新处理该批次中的所有消息。启用并实施部分批次响应功能后，处理程序仅返回失败消息的标识符，从而使得 Lambda 只能重试这些特定的项目。这样可以更好地控制对包含失败消息的批次的处理方式。

要报告批次错误，您将使用以下 JSON 架构：

```
{
  "batchItemFailures": [
    {
      "itemIdentifier": {
        "partition": "topic-partition_number",
        "offset": 100
      }
    },
    ...
  ]
}
```

**重要**  
如果您返回空的有效 JSON 或 null，则事件源映射会将批次视为已成功处理。如果返回的任何无效的 topic-partition\$1number 或偏移量并未包含在调用事件中，则将被视为失败，并且整个批次将重新尝试执行。

以下代码示例显示如何为接收来自 Kafka 源的事件的 Lambda 函数实现部分批次响应。该函数在响应中报告批处理项目失败，并指示 Lambda 稍后重试这些消息。

以下是展示该方法的 Python Lambda 处理程序实现示例：

```
import base64
from typing import Any, Dict, List

def lambda_handler(event: Dict[str, Any], context: Any) -> Dict[str, List[Dict[str, Dict[str, Any]]]]:
    failures: List[Dict[str, Dict[str, Any]]] = []
    records_dict = event.get("records", {})
    
    for topic_partition, records_list in records_dict.items():
        for record in records_list:
            topic = record.get("topic")
            partition = record.get("partition")
            offset = record.get("offset")
            value_b64 = record.get("value")
            
            try:
                data = base64.b64decode(value_b64).decode("utf-8")
                process_message(data)
            except Exception as exc:
                print(f"Failed to process record topic={topic} partition={partition} offset={offset}: {exc}")
                item_identifier: Dict[str, Any] = {
                    "partition": f"{topic}-{partition}",
                    "offset": int(offset) if offset is not None else None,
                }
                failures.append({"itemIdentifier": item_identifier})
    
    return {"batchItemFailures": failures}

def process_message(data: str) -> None:
    # Your business logic for a single message
    pass
```

以下是 Node.js 版本：

```
const { Buffer } = require("buffer");

const handler = async (event) => {
  const failures = [];
  
  for (let topicPartition in event.records) {
    const records = event.records[topicPartition];
    
    for (const record of records) {
      const topic = record.topic;
      const partition = record.partition;
      const offset = record.offset;
      const valueBase64 = record.value;
      const data = Buffer.from(valueBase64, "base64").toString("utf8");
      
      try {
        await processMessage(data);
      } catch (error) {
        console.error("Failed to process record", { topic, partition, offset, error });
        const itemIdentifier = {
          "partition": `${topic}-${partition}`,
          "offset": Number(offset),
        };
        failures.push({ itemIdentifier });
      }
    }
  }
  
  return { batchItemFailures: failures };
};

async function processMessage(payload) {
  // Your business logic for a single message
}

module.exports = { handler };
```

# 捕获 Amazon MSK 和自托管式 Apache Kafka 事件源的丢弃批次
<a name="kafka-on-failure"></a>

要保留失败的事件源映射调用的记录，请在函数的事件源映射中添加一个目标。发送到目标的每条记录都是一个 JSON 文档，其中包含有关失败调用的元数据。对于 Amazon S3 目标，Lambda 还会发送整个调用记录以及元数据。您可以将任何 Amazon SNS 主题、Amazon SQS 队列、Amazon S3 存储桶或 Kafka 配置为目标。

借助 Amazon S3 目标，您可以使用 [Amazon S3 事件通知](https://docs.aws.amazon.com/)功能在对象上传到目标 S3 存储桶时接收通知。您还可以将 S3 事件通知配置为调用另一个 Lambda 函数来对失败的批次执行自动处理。

您的执行角色必须具有目标的权限：
+ **对于 SQS 目标：**[sqs:SendMessage](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_SendMessage.html)
+ **对于 SNS 目标：**[sns:Publish](https://docs.aws.amazon.com/sns/latest/api/API_Publish.html)
+ **对于 S3 目标：**[ s3:PutObject](https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html) 和 [s3:ListBucket](https://docs.aws.amazon.com/AmazonS3/latest/API/ListObjectsV2.html)
+ **对于 Kafka 目标：**[kafka-cluster:WriteData](https://docs.aws.amazon.com/msk/latest/developerguide/kafka-actions.html)

您可以将 Kafka 主题配置为 Kafka 事件源映射失败时的目标。当 Lambda 在重试次数用尽后仍无法处理记录，或者当记录的保存时间超过最大期限时，Lambda 会将这些失败的记录发送至指定的 Kafka 主题，以便后续进行处理。请参考[使用 Kafka 主题作为失败时的目标](kafka-on-failure-destination.md)。

您必须在 Kafka 集群 VPC 中为故障目标服务部署 VPC 端点。

此外，如果您在目标上配置了 KMS 密钥，则根据具体目标类型，Lambda 需要以下权限：
+ 如果您已使用自己的 KMS 密钥为 S3 目标启用加密，则需要 [kms:GenerateDataKey](https://docs.aws.amazon.com/kms/latest/APIReference/API_GenerateDataKey.html)。如果 KMS 密钥和 S3 存储桶目标与您的 Lambda 函数和执行角色位于不同的账户中，请将 KMS 密钥配置为信任执行角色以允许 kms:GenerateDataKey。
+ 如果您已使用自己的 KMS 密钥为 SQS 目标启用加密，则需要 [kms:Decrypt](https://docs.aws.amazon.com/kms/latest/APIReference/API_Decrypt.html) 和 [kms:GenerateDataKey](https://docs.aws.amazon.com/kms/latest/APIReference/API_GenerateDataKey.html)。如果 KMS 密钥和 SQS 队列目标与您的 Lambda 函数和执行角色位于不同的账户中，请将 KMS 密钥配置为信任执行角色以允许 kms:Decrypt、kms:GenerateDataKey、[kms:DescribeKey](https://docs.aws.amazon.com/kms/latest/APIReference/API_DescribeKey.html) 和 [kms:ReEncrypt](https://docs.aws.amazon.com/kms/latest/APIReference/API_ReEncrypt.html)。
+ 如果您已使用自己的 KMS 密钥为 SNS 目标启用加密，则需要 [kms:Decrypt](https://docs.aws.amazon.com/kms/latest/APIReference/API_Decrypt.html) 和 [kms:GenerateDataKey](https://docs.aws.amazon.com/kms/latest/APIReference/API_GenerateDataKey.html)。如果 KMS 密钥和 SNS 主题目标与您的 Lambda 函数和执行角色位于不同的账户中，请将 KMS 密钥配置为信任执行角色以允许 kms:Decrypt、kms:GenerateDataKey、[kms:DescribeKey](https://docs.aws.amazon.com/kms/latest/APIReference/API_DescribeKey.html) 和 [kms:ReEncrypt](https://docs.aws.amazon.com/kms/latest/APIReference/API_ReEncrypt.html)。

## 为 Kafka 事件源映射配置失败时的目标
<a name="kafka-onfailure-destination"></a>

要使用控制台配置失败时的目标，请执行以下步骤：

1. 打开 Lamba 控制台的[函数](https://console.aws.amazon.com/lambda/home#/functions)页面。

1. 选择函数。

1. 在 **Function overview (函数概览)** 下，选择 **Add destination (添加目标)**。

1. 对于**源**，请选择**事件源映射调用**。

1. 对于**事件源映射**，请选择为此函数配置的事件源。

1. 在**条件**中，选择**失败时**。对于事件源映射调用，这是唯一可接受的条件。

1. 对于**目标类型**，请选择 Lambda 要发送调用记录的目标类型。

1. 对于 **Destination (目标)**，请选择一个资源。

1. 选择**保存**。

您还可以使用 AWS CLI 配置失败时的目标。例如，以 [create-event-source-mapping](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/create-event-source-mapping.html) 命令将带有 SQS 失败时目标的事件源映射添加到 `MyFunction`：

```
aws lambda create-event-source-mapping \
--function-name "MyFunction" \
--event-source-arn arn:aws:kafka:us-east-1:123456789012:cluster/vpc-2priv-2pub/751d2973-a626-431c-9d4e-d7975eb44dd7-2 \
--destination-config '{"OnFailure": {"Destination": "arn:aws:sqs:us-east-1:123456789012:dest-queue"}}'
```

以下 [update-event-source-mapping](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/update-event-source-mapping.html) 命令将 S3 失败时目标添加到与输入 `uuid` 关联的事件源：

```
aws lambda update-event-source-mapping \
--uuid f89f8514-cdd9-4602-9e1f-01a5b77d449b \
--destination-config '{"OnFailure": {"Destination": "arn:aws:s3:::dest-bucket"}}'
```

要移除目标，请提供一个空字符串作为 `destination-config` 参数的实际参数：

```
aws lambda update-event-source-mapping \
--uuid f89f8514-cdd9-4602-9e1f-01a5b77d449b \
--destination-config '{"OnFailure": {"Destination": ""}}'
```

### Amazon S3 目标的安全最佳实践
<a name="kafka-s3-destination-security"></a>

如果删除配置为目标的 S3 存储桶而不将目标从函数配置中删除，则可能会造成安全风险。如果其他用户知道您的目标存储桶的名称，则他们可以在其 AWS 账户中重新创建存储桶。调用失败的记录将发送到存储桶，这可能会暴露您函数中的数据。

**警告**  
为确保您的函数中的调用记录不会发送到另一个 AWS 账户中的 S3 存储桶，请向函数的执行角色添加条件，以将 `s3:PutObject` 权限限制为您账户中的存储桶。

以下示例显示了一个 IAM 策略，该策略将您函数的 `s3:PutObject` 权限限制为您账户中的存储桶。该策略还为 Lambda 提供了使用 S3 存储桶作为目标所需的 `s3:ListBucket` 权限。

```
{
    "Version": "2012-10-17",		 	 	 
    "Statement": [
        {
            "Sid": "S3BucketResourceAccountWrite",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:ListBucket"
            ],
            "Resource": [
                "arn:aws:s3:::*/*",
                "arn:aws:s3:::*"
            ],
            "Condition": {
                "StringEquals": {
                    "s3:ResourceAccount": "111122223333"
                }
            }
        }
    ]
}
```

要使用 AWS 管理控制台 或 AWS CLI 向函数的执行角色添加权限策略，请参阅以下程序中的说明：

------
#### [ Console ]

**向函数的执行角色添加权限策略（控制台）**

1. 打开 Lamba 控制台的[函数](https://console.aws.amazon.com/lambda/home#/functions)页面。

1. 选择想要修改其执行角色的 Lambda 函数。

1. 在**配置**选项卡中，选择**权限**。

1. 在**执行角色**选项卡中，选择您的函数的**角色名称**,以打开该角色的 IAM 控制台页面。

1. 通过执行以下操作，向角色添加权限策略：

   1. 在**权限策略**窗格中，选择**添加权限**，然后选择**创建内联策略**。

   1. 在**策略编辑器**中，选择 **JSON**。

   1. 将要添加的策略粘贴到编辑器中（替换现有 JSON），然后选择**下一步**。

   1. 在**策略详细信息**下，输入**策略名称**。

   1. 选择**创建策略**。

------
#### [ AWS CLI ]

**向函数的执行角色添加权限策略（CLI）**

1. 创建具有所需权限的 JSON 策略文档并将其保存在本地目录中。

1. 使用 IAM `put-role-policy` CLI 命令向您函数的执行角色添加权限。在您保存 JSON 策略文档的目录中运行以下命令，并用您自己的值替换角色名称、策略名称和策略文档。

   ```
   aws iam put-role-policy \
   --role-name my_lambda_role \
   --policy-name LambdaS3DestinationPolicy \
   --policy-document file://my_policy.json
   ```

------

### SNS 和 SQS 示例调用记录
<a name="kafka-sns-sqs-destinations"></a>

以下示例显示了 Lambda 在 Kafka 事件源调用失败时向 SNS 主题或 SQS 队列目标发送的内容。`recordsInfo` 下面的每个密钥都包含 Kafka 主题和分区，用连字符分隔。例如，对于密钥 `"Topic-0"`，`Topic` 是 Kafka 主题，`0` 是分区。对于每个主题和分区，可以使用偏移量和时间戳数据来查找原始调用记录。

```
{
    "requestContext": {
        "requestId": "316aa6d0-8154-xmpl-9af7-85d5f4a6bc81",
        "functionArn": "arn:aws:lambda:us-east-1:123456789012:function:myfunction",
        "condition": "RetryAttemptsExhausted" | "MaximumPayloadSizeExceeded",
        "approximateInvokeCount": 1
    },
    "responseContext": { // null if record is MaximumPayloadSizeExceeded
        "statusCode": 200,
        "executedVersion": "$LATEST",
        "functionError": "Unhandled"
    },
    "version": "1.0",
    "timestamp": "2019-11-14T00:38:06.021Z",
    "KafkaBatchInfo": {
        "batchSize": 500,
        "eventSourceArn": "arn:aws:kafka:us-east-1:123456789012:cluster/vpc-2priv-2pub/751d2973-a626-431c-9d4e-d7975eb44dd7-2",
        "bootstrapServers": "...",
        "payloadSize": 2039086, // In bytes
        "recordsInfo": {
            "Topic-0": {
                "firstRecordOffset": "49601189658422359378836298521827638475320189012309704722",
                "lastRecordOffset": "49601189658422359378836298522902373528957594348623495186",
                "firstRecordTimestamp": "2019-11-14T00:38:04.835Z",
                "lastRecordTimestamp": "2019-11-14T00:38:05.580Z",
            },
            "Topic-1": {
                "firstRecordOffset": "49601189658422359378836298521827638475320189012309704722",
                "lastRecordOffset": "49601189658422359378836298522902373528957594348623495186",
                "firstRecordTimestamp": "2019-11-14T00:38:04.835Z",
                "lastRecordTimestamp": "2019-11-14T00:38:05.580Z",
            }
        }
    }
}
```

### S3 目标示例调用记录
<a name="kafka-s3-destinations"></a>

对于 S3 目标，Lambda 会将整个调用记录以及元数据发送到目标。以下示例显示了 Lambda 因调用 Kafka 事件源失败而向 S3 存储桶目标发送消息。除了针对 SQS 和 SNS 目标的上一示例中的所有字段外，`payload` 字段还包含作为转义 JSON 字符串的原始调用记录。

```
{
    "requestContext": {
        "requestId": "316aa6d0-8154-xmpl-9af7-85d5f4a6bc81",
        "functionArn": "arn:aws:lambda:us-east-1:123456789012:function:myfunction",
        "condition": "RetryAttemptsExhausted" | "MaximumPayloadSizeExceeded",
        "approximateInvokeCount": 1
    },
    "responseContext": { // null if record is MaximumPayloadSizeExceeded
        "statusCode": 200,
        "executedVersion": "$LATEST",
        "functionError": "Unhandled"
    },
    "version": "1.0",
    "timestamp": "2019-11-14T00:38:06.021Z",
    "KafkaBatchInfo": {
        "batchSize": 500,
        "eventSourceArn": "arn:aws:kafka:us-east-1:123456789012:cluster/vpc-2priv-2pub/751d2973-a626-431c-9d4e-d7975eb44dd7-2",
        "bootstrapServers": "...",
        "payloadSize": 2039086, // In bytes
        "recordsInfo": {
            "Topic-0": {
                "firstRecordOffset": "49601189658422359378836298521827638475320189012309704722",
                "lastRecordOffset": "49601189658422359378836298522902373528957594348623495186",
                "firstRecordTimestamp": "2019-11-14T00:38:04.835Z",
                "lastRecordTimestamp": "2019-11-14T00:38:05.580Z",
            },
            "Topic-1": {
                "firstRecordOffset": "49601189658422359378836298521827638475320189012309704722",
                "lastRecordOffset": "49601189658422359378836298522902373528957594348623495186",
                "firstRecordTimestamp": "2019-11-14T00:38:04.835Z",
                "lastRecordTimestamp": "2019-11-14T00:38:05.580Z",
            }
        }
    },
    "payload": "<Whole Event>" // Only available in S3
}
```

**提示**  
我们建议在目标存储桶上启用 S3 版本控制。

# 使用 Kafka 主题作为失败时的目标
<a name="kafka-on-failure-destination"></a>

您可以将 Kafka 主题配置为 Kafka 事件源映射失败时的目标。当 Lambda 在重试次数用尽后仍无法处理记录，或者当记录的保存时间超过最大期限时，Lambda 会将这些失败的记录发送至指定的 Kafka 主题，以便后续进行处理。当您同时配置[无限重试](kafka-retry-configurations.md)和失败目标时，Lambda 会自动应用最多 10 次重试尝试。

## Kafka 失败时的目标的工作原理
<a name="kafka-ofd-overview"></a>

当您将 Kafka 主题配置为失败时的目标时，Lambda 将充当 Kafka 生产者，并将失败的记录写入目标主题。这会在您的 Kafka 基础设施中创建死信主题（DLT）模式。
+ **相同的集群要求**：目标主题必须与源主题位于同一个 Kafka 集群中。
+ **实际记录内容**：Kafka 目标会收到实际的失败记录以及故障元数据。
+ **递归防护**：Lambda 通过阻止源主题和目标主题相同的配置来避免无限循环。

## 配置 Kafka 失败时的目标
<a name="kafka-ofd-configure"></a>

在创建或更新 Kafka 事件源映射时，您可以将 Kafka 主题配置为失败时的目标。

### 配置 Kafka 目标（控制台）
<a name="kafka-ofd-console"></a>

**要将 Kafka 主题配置为失败时的目标（控制台）**

1. 打开 Lamba 控制台的[函数](https://console.aws.amazon.com/lambda/home#/functions)页面。

1. 选择您的函数名称。

1. 请执行以下操作之一：
   + 要添加新的 Kafka 触发器，请在**函数概述**下选择**添加触发器**。
   + 要修改现有的 Kafka 触发器，请选择该触发器，然后选择**编辑**。

1. 在**其他设置**下，对于**失败时的目标**，选择 **Kafka 主题**。

1. 对于**主题名称**，输入要将失败记录发送至的 Kafka 主题的名称。

1. 选择**添加**或**保存**。

### 配置 Kafka 目标（AWS CLI）
<a name="kafka-ofd-cli"></a>

使用 `kafka://` 前缀将 Kafka 主题指定为失败时的目标。

#### 使用 Kafka 目标创建事件源映射
<a name="kafka-ofd-cli-create"></a>

以下示例将 Kafka 主题作为失败时的目标创建了一个 Amazon MSK 事件源映射：

```
aws lambda create-event-source-mapping \
  --function-name my-kafka-function \
  --topics AWSKafkaTopic \
  --event-source-arn arn:aws:kafka:us-east-1:123456789012:cluster/my-cluster/abc123 \
  --starting-position LATEST \
  --provisioned-poller-config MinimumPollers=1,MaximumPollers=3 \
  --destination-config '{"OnFailure":{"Destination":"kafka://failed-records-topic"}}'
```

对于自行管理的 Kafka，请使用相同的语法：

```
aws lambda create-event-source-mapping \
  --function-name my-kafka-function \
  --topics AWSKafkaTopic \
  --self-managed-event-source '{"Endpoints":{"KAFKA_BOOTSTRAP_SERVERS":["abc.xyz.com:9092"]}}' \
  --starting-position LATEST \
  --provisioned-poller-config MinimumPollers=1,MaximumPollers=3 \
  --destination-config '{"OnFailure":{"Destination":"kafka://failed-records-topic"}}'
```

#### 更新 Kafka 目标
<a name="kafka-ofd-cli-update"></a>

使用 `update-event-source-mapping` 命令添加或修改 Kafka 目标：

```
aws lambda update-event-source-mapping \
  --uuid 12345678-1234-1234-1234-123456789012 \
  --destination-config '{"OnFailure":{"Destination":"kafka://failed-records-topic"}}'
```

## Kafka 目标的记录格式
<a name="kafka-ofd-record-format"></a>

当 Lambda 向 Kafka 主题发送失败的记录时，每条消息都包含有关失败的元数据和实际的记录内容。

### 失败元数据
<a name="kafka-ofd-metadata"></a>

元数据包含记录失败的原因以及原始批次的详细信息：

```
{
  "requestContext": {
    "requestId": "e4b46cbf-b738-xmpl-8880-a18cdf61200e",
    "functionArn": "arn:aws:lambda:us-east-1:123456789012:function:my-function:$LATEST",
    "condition": "RetriesExhausted",
    "approximateInvokeCount": 3
  },
  "responseContext": {
    "statusCode": 200,
    "executedVersion": "$LATEST",
    "functionError": "Unhandled"
  },
  "version": "1.0",
  "timestamp": "2019-11-14T18:16:05.568Z",
  "KafkaBatchInfo": {
    "batchSize": 1,
    "eventSourceArn": "arn:aws:kafka:us-east-1:123456789012:cluster/my-cluster/abc123",
    "bootstrapServers": "b-1.mycluster.abc123.kafka.us-east-1.amazonaws.com:9098",
    "payloadSize": 1162,
    "recordInfo": {
      "offset": "49601189658422359378836298521827638475320189012309704722",
      "timestamp": "2019-11-14T18:16:04.835Z"
    }
  },
  "payload": {
    "bootstrapServers": "b-1.mycluster.abc123.kafka.us-east-1.amazonaws.com:9098",
    "eventSource": "aws:kafka",
    "eventSourceArn": "arn:aws:kafka:us-east-1:123456789012:cluster/my-cluster/abc123",
    "records": {
      "my-topic-0": [
        {
          "headers": [],
          "key": "dGVzdC1rZXk=",
          "offset": 100,
          "partition": 0,
          "timestamp": 1749116692330,
          "timestampType": "CREATE_TIME",
          "topic": "my-topic",
          "value": "dGVzdC12YWx1ZQ=="
        }
      ]
    }
  }
}
```

### 分区键行为
<a name="kafka-ofd-partitioning"></a>

Lambda 在将数据发送至目标主题时，会使用原始记录中的相同分区键。如果原始记录中没有键，则 Lambda 会使用 Kafka 的默认轮询分区机制，在目标主题的所有可用分区中进行分区。

## 要求和限制
<a name="kafka-ofd-requirements"></a>
+ **需要预置模式**：Kafka 失败时的目标仅适用于启用了预置模式的事件源映射。
+ **仅限同一集群**：目标主题必须与源主题位于同一个 Kafka 集群中。
+ **主题权限**：您的事件源映射必须对目标主题具有写入权限。示例：

  ```
  {
      "Version": "2012-10-17",		 	 	 
      "Statement": [
          {
              "Sid": "ClusterPermissions",
              "Effect": "Allow",
              "Action": [
                  "kafka-cluster:Connect",
                  "kafka-cluster:DescribeCluster",
                  "kafka-cluster:DescribeTopic",
                  "kafka-cluster:WriteData",
                  "kafka-cluster:ReadData"
              ],
              "Resource": [
                  "arn:aws:kafka:*:*:cluster/*"
              ]
          },
          {
              "Sid": "TopicPermissions",
              "Effect": "Allow",
              "Action": [
                  "kafka-cluster:DescribeTopic",
                  "kafka-cluster:WriteData",
                  "kafka-cluster:ReadData"
              ],
              "Resource": [
                  "arn:aws:kafka:*:*:topic/*/*"
              ]
          },
          {
              "Effect": "Allow",
              "Action": [
                  "kafka:DescribeCluster",
                  "kafka:GetBootstrapBrokers",
                  "kafka:Produce"
              ],
              "Resource": "arn:aws:kafka:*:*:cluster/*"
          },
          {
              "Effect": "Allow",
              "Action": [
                  "ec2:CreateNetworkInterface",
                  "ec2:DescribeNetworkInterfaces",
                  "ec2:DeleteNetworkInterface",
                  "ec2:DescribeSubnets",
                  "ec2:DescribeSecurityGroups"
              ],
              "Resource": "*"
          }
      ]
  }
  ```
+ **无递归**：目标主题名称不能与您的任何源主题名称相同。

# Kafka 事件源映射日志记录
<a name="esm-logging"></a>

您可以为 Kafka 事件源映射配置系统级日志记录，以启用和筛选 Lambda 事件轮询器发送至 CloudWatch 的系统日志。

此功能仅适用于使用[预置模式](https://docs.aws.amazon.com/lambda/latest/dg/kafka-scaling-modes.html#kafka-provisioned-mode)的 Kafka 事件源映射。

对于带有日志记录配置的事件源映射，您现在还可以从控制台 **Lambda** > **其他资源** > **事件源映射**页面的**监视器**选项卡中通过预构建的日志查询查看系统日志。

## 日志记录的工作原理
<a name="esm-logging-overview"></a>

当您在事件源映射中使用日志级别设置日志记录配置时，Lambda 事件轮询器会发送相应的日志（事件源映射系统日志）。

事件源映射会对 Lambda 函数重复使用相同的[日志目标](https://docs.aws.amazon.com/lambda/latest/dg/monitoring-logs.html#configuring-log-destinations)。请确保 Lambda 函数的执行角色拥有必要的日志记录权限。

事件源映射将有自己的日志流，日志流名称为日期和事件源映射 UUID，例如 `2020/01/01/12345678-1234-1234-1234-12345678901`。

对于事件源映射系统日志，您可以在以下日志级别之间进行选择。


| 日志级别 | 用法 | 
| --- | --- | 
| DEBUG（最详细） | 事件源处理进度详细信息 | 
| INFO | 关于事件源映射正常运行情况的消息 | 
| WARN（最简略） | 关于可能会导致意外行为的潜在警告和错误的消息 | 

当您选择日志级别后，Lambda 事件轮询器会发送该级别及更低级别的日志。例如，如果将事件源映射系统日志级别设置为 INFO，则事件轮询器不会发送 DEBUG 级别的日志输出。

## 配置日志记录
<a name="esm-logging-configure"></a>

您可以在创建或更新 Kafka 事件源映射时设置日志记录配置。

### 配置日志记录（控制台）
<a name="esm-logging-console"></a>

**配置日志记录（控制台）**

1. 打开 Lamba 控制台的[函数](https://console.aws.amazon.com/lambda/home#/functions)页面。

1. 选择您的函数名称。

1. 请执行以下操作之一：
   + 要添加新的 Kafka 触发器，请在**函数概述**下选择**添加触发器**。
   + 要修改现有的 Kafka 触发器，请选择该触发器，然后选择**编辑**。

1. 在**事件轮询器配置**下，对于**预置模式**启用**配置**复选框。此时将显示**日志级别**设置。

1.  单击**日志级别**下拉列表，然后选择事件源映射的级别。

1. 选择底部的**添加**或**保存**，以创建或更新事件源映射。

### 配置日志记录（AWS CLI）
<a name="esm-logging-cli"></a>

#### 创建带有日志记录的事件源映射
<a name="esm-logging-cli-create"></a>

下面的示例会创建带有日志记录配置的 Amazon MSK 事件源映射：

```
aws lambda create-event-source-mapping \
  --function-name my-kafka-function \
  --topics AWSKafkaTopic \
  --event-source-arn arn:aws:kafka:us-east-1:123456789012:cluster/my-cluster/abc123 \
  --starting-position LATEST \
  --provisioned-poller-config MinimumPollers=1,MaximumPollers=3 \
  --logging-config '{"SystemLogLevel":"DEBUG"}'
```

对于自行管理的 Kafka，请使用相同的语法：

```
aws lambda create-event-source-mapping \
  --function-name my-kafka-function \
  --topics AWSKafkaTopic \
  --self-managed-event-source '{"Endpoints":{"KAFKA_BOOTSTRAP_SERVERS":["abc.xyz.com:9092"]}}' \
  --starting-position LATEST \
  --provisioned-poller-config MinimumPollers=1,MaximumPollers=3 \
  --logging-config '{"SystemLogLevel":"DEBUG"}'
```

#### 更新日志记录配置
<a name="esm-logging-cli-update"></a>

使用 `update-event-source-mapping` 命令添加或修改日志记录配置：

```
aws lambda update-event-source-mapping \
  --uuid 12345678-1234-1234-1234-123456789012 \
  --logging-config '{"SystemLogLevel":"WARN"}'
```

## Kafka 事件源映射系统日志的记录格式
<a name="esm-logging-record-format"></a>

当 Lambda 事件轮询器发送日志时，每个日志条目都包含常规事件源映射元数据以及事件特定内容。

### WARN 日志记录
<a name="esm-logging-warn-record"></a>

WARN 记录包含来自事件轮询器的错误或警告，会在事件发生时发出。例如：

```
{
    "eventType": "ESM_PROCESSING_EVENT",
    "timestamp": 1546347650000,
    "resourceArn": "arn:aws:lambda:us-east-1:123456789012:event-source-mapping:12345678-1234-1234-1234-123456789012",
    "eventSourceArn": "arn:aws:kafka:us-east-1:123456789012:cluster/tests-cluster/87654321-4321-4321-4321-876543221-s1",
    "eventProcessorId": "12345678-1234-1234-1234-123456789012/0",
    "logLevel": "WARN",
    "error": {
        "errorMessage": "Timeout expired while fetching topic metadata",
        "errorCode": "org.apache.kafka.common.errors.TimeoutException"
    }
}
```

### INFO 日志记录
<a name="esm-logging-info-record"></a>

INFO 记录包含每个事件轮询器中的 Kafka 使用者客户端配置，会在生成或更改使用者时发出。例如：

```
{
    "eventType": "POLLER_STATUS_EVENT",
    "timestamp": 1546347660000,
    "resourceArn": "arn:aws:lambda:us-east-1:123456789012:event-source-mapping:12345678-1234-1234-1234-123456789012",
    "eventSourceArn": "arn:aws:kafka:us-east-1:123456789012:cluster/tests-cluster/87654321-4321-4321-4321-876543221-s1",
    "eventProcessorId": "12345678-1234-1234-1234-123456789012/0",
    "logLevel": "INFO",
    "kafkaEventSourceConnection": {
        "brokerEndpoints": "boot-abcd1234.c2.kafka-serverless.us-east-1.amazonaws.com:9098",
        "consumerId": "12345678-1234-1234-1234-123456789012-0",
        "topics": [
            "test"
        ],
        "consumerGroupId": "12345678-1234-1234-1234-123456789012",
        "securityProtocol": "SASL_SSL",
        "saslMechanism": "AWS_MSK_IAM",
        "totalPartitionCount": 2,
        "assignedPartitionCount": 2,
        "partitionsAssignmentGeneration": 5,
        "assignedPartitions": [
            "test-0",
            "test-1"
        ],
        "networkConfig": {
            "ipAddresses": [
                "10.100.141.1"
            ],
            "subnetCidrBlock": "10.100.128.0/20",
            "securityGroups": [
                "sg-abcdefabcdefabcdef"
            ]
        }
    }
}
```

### DEBUG 日志记录
<a name="esm-logging-debug-record"></a>

DEBUG 日志包含事件源映射处理中与 Kafka 偏移量相关的信息，偏移量信息每分钟发出一次。例如：

```
{
    "eventType": "KAFKA_STATUS_EVENT",
    "timestamp": 1546347670000,
    "resourceArn": "arn:aws:lambda:us-east-1:123456789012:event-source-mapping:12345678-1234-1234-1234-123456789012",
    "eventSourceArn": "arn:aws:kafka:us-east-1:123456789012:cluster/tests-cluster/87654321-4321-4321-4321-876543221-s1",
    "eventProcessorId": "12345678-1234-1234-1234-123456789012/0",
    "logLevel": "DEBUG",
    "kafkaPartitionOffsets": {
        "partition": "test-1",
        "endOffset": 5004,
        "consumedOffset": 5003,
        "processedOffset": 5003,
        "committedOffset": 5004
    }
}
```

# Kafka 事件源映射错误的故障排除
<a name="with-kafka-troubleshoot"></a>

以下主题针对在使用 Amazon MSK 或自托管式 Apache Kafka 和 Lambda 时可能遇到的错误和问题提供了故障排除建议。

有关故障排除的更多帮助，请访问 [AWS 知识中心](https://repost.aws/knowledge-center#AWS_Lambda)。

## 身份验证和授权错误
<a name="kafka-permissions-errors"></a>

如果缺少使用来自 Kafka 集群的数据所需的任何权限，Lambda 会在 **LastProcessingResult** 下的事件源映射中显示以下错误消息。

**Topics**
+ [

### 集群未能授权 Lambda
](#kafka-authorize-errors)
+ [

### SASL 身份验证失败
](#kafka-sasl-errors)
+ [

### 服务器未能通过 Lambda 的身份验证
](#kafka-mtls-errors-server)
+ [

### Lambda 未能对服务器进行身份验证
](#kafka-mtls-errors-lambda)
+ [

### 提供的证书或私有密钥无效
](#kafka-key-errors)

### 集群未能授权 Lambda
<a name="kafka-authorize-errors"></a>

对于 SASL/SCRAM 或 mTLS，此错误表明提供的用户不具有以下所有必需的 Kafka 访问控制列表（ACL）权限：
+ DescribeConfigs 集群
+ 描述组
+ 读取组
+ 描述主题
+ 读取主题

当您使用所需的 `kafka-cluster` 权限创建 Kafka ACL 时，请将主题和组指定为资源。主题名称必须与事件源映射中的主题一致。组名称必须与事件源映射的 UUID 一致。

向执行角色添加所需的权限后，更改可能需要几分钟才会生效。

下面是针对此问题启用[日志记录配置](esm-logging.md)后的 ESM 系统级日志示例：

```
{
    "eventType": "ESM_PROCESSING_EVENT",
    "timestamp": 1734567890123,
    "resourceArn": "arn:aws:lambda:us-east-1:123456789012:event-source-mapping:a1b2c3d4-5678-90ab-cdef-EXAMPLE11111",
    "eventSourceArn": "arn:aws:kafka:us-east-1:123456789012:cluster/my-kafka-cluster/12345678-abcd-1234-efgh-EXAMPLE11111-1",
    "eventProcessorId": "a1b2c3d4-5678-90ab-cdef-EXAMPLE11111/0",
    "logLevel": "WARN",
    "error": {
        "errorMessage": "Not authorized to access topics: [my-topic]",
        "errorCode": "org.apache.kafka.common.errors.TopicAuthorizationException"
    }
}
```

### SASL 身份验证失败
<a name="kafka-sasl-errors"></a>

对于 SASL/SCRAM 或 SASL/PLAIN，此错误表明提供的登录凭证无效。

对于 IAM 访问控制，此错误表明执行角色缺少集群的 `kafka-cluster:Connect` 权限。将此权限添加到该角色并将集群的 Amazon 资源名称（ARN）指定为资源。

您可能会看到此错误间歇性发生。在 TCP 连接数超过服务配额后，集群将拒绝连接。Lambda 会退回并重试，直到连接成功为止。在 Lambda 连接到集群并轮询记录后，最后的处理结果将更改为 `OK`。

下面是在使用 IAM 身份验证时针对此问题启用[日志记录配置](esm-logging.md)后的 ESM 系统级日志示例：

```
{
    "eventType": "ESM_PROCESSING_EVENT",
    "timestamp": 1734567890456,
    "resourceArn": "arn:aws:lambda:us-east-1:123456789012:event-source-mapping:a1b2c3d4-5678-90ab-cdef-EXAMPLE22222",
    "eventSourceArn": "arn:aws:kafka:us-east-1:123456789012:cluster/my-kafka-cluster/12345678-abcd-1234-efgh-EXAMPLE22222-1",
    "eventProcessorId": "a1b2c3d4-5678-90ab-cdef-EXAMPLE22222/0",
    "logLevel": "WARN",
    "error": {
        "errorMessage": "[a1b2c3d4-5678-90ab-cdef-EXAMPLE22222]: Access denied",
        "errorCode": "org.apache.kafka.common.errors.SaslAuthenticationException"
    }
}
```

### 服务器未能通过 Lambda 的身份验证
<a name="kafka-mtls-errors-server"></a>

此错误表明 Kafka 代理未能对 Lambda 进行身份验证。出现此错误的可能原因如下：
+ 您没有为 mTLS 身份验证提供客户端证书。
+ 您提供了客户端证书，但未将 Kafka 代理配置为使用 mTLS 身份验证。
+ Kafka 代理不信任客户端证书。

### Lambda 未能对服务器进行身份验证
<a name="kafka-mtls-errors-lambda"></a>

此错误表明 Lambda 未能对 Kafka 代理进行身份验证。出现此错误的可能原因如下：
+ 对于自托管式 Apache Kafka：Kafka 代理使用自签名证书或私有 CA，但未提供服务器根 CA 证书。
+ 对于自托管式 Apache Kafka：服务器根 CA 证书与签署代理证书的根 CA 不匹配。
+ 主机名验证失败，因为代理的证书未将该代理的 DNS 名称或 IP 地址用作主题替代名称。

### 提供的证书或私有密钥无效
<a name="kafka-key-errors"></a>

此错误表明 Kafka 使用者无法使用提供的证书或私有密钥。确保证书和密钥使用 PEM 格式，并且私有密钥加密使用 PBES1 算法。

下面是针对此问题启用[日志记录配置](esm-logging.md)后的 ESM 系统级日志示例：

```
{
    "eventType": "ESM_PROCESSING_EVENT",
    "timestamp": 1734567891234,
    "resourceArn": "arn:aws:lambda:us-east-1:123456789012:event-source-mapping:a1b2c3d4-5678-90ab-cdef-EXAMPLE44444",
    "eventSourceArn": "arn:aws:kafka:us-east-1:123456789012:cluster/my-kafka-cluster/12345678-abcd-1234-efgh-EXAMPLE44444-1",
    "eventProcessorId": "a1b2c3d4-5678-90ab-cdef-EXAMPLE44444/0",
    "logLevel": "WARN",
    "error": {
        "errorMessage": "Invalid PEM keystore configs",
        "errorCode": "org.apache.kafka.common.errors.InvalidConfigurationException"
    }
}
```

## 网络和连接错误
<a name="kafka-network-errors"></a>

网络配置问题可能会导致 Lambda 无法连接到您的 Kafka 集群。下面的主题介绍了常见的网络相关错误。

**Topics**
+ [

### 由于安全组配置而导致连接超时
](#kafka-security-group-errors)
+ [

### 无法解析 Kafka 代理端点
](#kafka-cluster-deleted-errors)

### 由于安全组配置而导致连接超时
<a name="kafka-security-group-errors"></a>

如果与 Kafka 集群关联的安全组不允许来自自身的入站流量，则 Lambda 无法连接到该集群。确保安全组的入站规则允许来自安全组本身的流量通过 Kafka 代理端口。

下面是针对此问题启用[日志记录配置](esm-logging.md)后的 ESM 系统级日志示例：

```
{
    "eventType": "ESM_PROCESSING_EVENT",
    "timestamp": 1734567892345,
    "resourceArn": "arn:aws:lambda:us-east-1:123456789012:event-source-mapping:a1b2c3d4-5678-90ab-cdef-EXAMPLE55555",
    "eventSourceArn": "arn:aws:kafka:us-east-1:123456789012:cluster/my-kafka-cluster/12345678-abcd-1234-efgh-EXAMPLE55555-1",
    "eventProcessorId": "a1b2c3d4-5678-90ab-cdef-EXAMPLE55555/0",
    "logLevel": "WARN",
    "error": {
        "errorMessage": "Timeout expired while fetching topic metadata",
        "errorCode": "org.apache.kafka.common.errors.TimeoutException"
    }
}
```

您还可以查看 Kafka 使用者 INFO 日志，以验证连接和网络配置。`brokerEndpoints` 字段显示 Kafka 代理地址，`securityProtocol` 和 `saslMechanism`（如果适用）显示身份验证方法，`networkConfig` 字段显示事件源映射使用的 IP 地址、子网 CIDR 数据块和安全组。验证列出的安全组是否允许所需的入站流量：

```
{
    "eventType": "POLLER_STATUS_EVENT",
    "timestamp": 1734567892456,
    "resourceArn": "arn:aws:lambda:us-east-1:123456789012:event-source-mapping:a1b2c3d4-5678-90ab-cdef-11111EXAMPLE",
    "eventSourceArn": "arn:aws:kafka:us-east-1:123456789012:cluster/my-kafka-cluster/a1b2c3d4-5678-90ab-cdef-11111EXAMPLE-1",
    "eventProcessorId": "a1b2c3d4-5678-90ab-cdef-11111EXAMPLE/0",
    "logLevel": "INFO",
    "kafkaEventSourceConnection": {
        "brokerEndpoints": "boot-abcd1234.c2.kafka-serverless.us-east-1.amazonaws.com:9098",
        "consumerId": "a1b2c3d4-5678-90ab-cdef-11111EXAMPLE-0",
        "topics": [
            "my-topic"
        ],
        "consumerGroupId": "a1b2c3d4-5678-90ab-cdef-11111EXAMPLE",
        "securityProtocol": "SASL_SSL",
        "saslMechanism": "AWS_MSK_IAM",
        "totalPartitionCount": 2,
        "assignedPartitionCount": 2,
        "partitionsAssignmentGeneration": 1,
        "assignedPartitions": [
            "my-topic-0",
            "my-topic-1"
        ],
        "networkConfig": {
            "ipAddresses": [
                "10.0.0.37"
            ],
            "subnetCidrBlock": "10.0.0.32/28",
            "securityGroups": [
                "sg-0123456789abcdef0"
            ]
        }
    }
}
```

### 无法解析 Kafka 代理端点
<a name="kafka-cluster-deleted-errors"></a>

此错误表明 Kafka 集群不存在或已遭删除。验证事件源映射中指定的集群是否存在并且处于活动状态。

下面是针对此问题启用[日志记录配置](esm-logging.md)后的 ESM 系统级日志示例：

```
{
    "eventType": "ESM_PROCESSING_EVENT",
    "timestamp": 1734567893456,
    "resourceArn": "arn:aws:lambda:us-east-1:123456789012:event-source-mapping:a1b2c3d4-5678-90ab-cdef-EXAMPLE66666",
    "eventSourceArn": "arn:aws:kafka:us-east-1:123456789012:cluster/my-kafka-cluster/12345678-abcd-1234-efgh-EXAMPLE66666-1",
    "eventProcessorId": "a1b2c3d4-5678-90ab-cdef-EXAMPLE66666/0",
    "logLevel": "WARN",
    "error": {
        "errorMessage": "No resolvable bootstrap urls given in bootstrap.servers",
        "errorCode": "org.apache.kafka.common.config.ConfigException"
    }
}
```

## 事件源映射错误
<a name="services-event-errors"></a>

将 Apache Kafka 集群作为 Lambda 函数的[事件源](invocation-eventsourcemapping.md)添加时，如果您的函数遇到错误，Kafka 使用者将停止处理记录。主题分区的使用者是那些订阅、阅读和处理记录的使用者。您的其他 Kafka 使用者可以继续处理记录，只要他们没有遇到同样的错误即可。

要确定使用者停止的原因，请检查 `StateTransitionReason` 响应中的 `EventSourceMapping` 字段。下表列出了您可能收到的事件源错误：

**`ESM_CONFIG_NOT_VALID`**  
事件源映射配置无效。

**`EVENT_SOURCE_AUTHN_ERROR`**  
Lambda 无法对事件源进行身份验证。

**`EVENT_SOURCE_AUTHZ_ERROR`**  
Lambda 没有访问事件源所需的权限。

**`FUNCTION_CONFIG_NOT_VALID`**  
函数配置无效。

**注意**  
如果您的 Lambda 事件记录超过允许的 6 MB 大小限制，那么它们可能处于未处理状态。

# 使用 Amazon API Gateway 端点调用 Lambda 函数
<a name="services-apigateway"></a>

您可以使用 Amazon API Gateway 为 Lambda 函数创建带有 HTTP 端点的 Web API。API Gateway 提供工具，用于创建和记录向 Lambda 函数路由 HTTP 请求的 Web API。您可以使用身份验证和授权控制来保护对 API 的访问。您的 API 可以通过互联网传输流量，也可以仅允许在您的 VPC 内访问。

**提示**  
Lambda 提供了两种方法来通过 HTTP 端点调用函数：API Gateway 和 Lambda 函数 URL。如果您不确定哪种方法最适合您的应用场景，请参阅[选择使用 HTTP 请求调用 Lambda 函数的方法](apig-http-invoke-decision.md)。

API 中的资源会定义一个或多个方法，如 GET 或 POST。方法具有将请求传送给 Lambda 函数或其他集成类型的集成。您可以单独定义每个资源和方法，也可以使用特定的资源和方法类型来匹配属于特定模式的所有请求。[代理资源](https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html)会捕获某个资源下的所有路径。`ANY` 方法会捕获所有 HTTP 方法。

**Topics**
+ [

## 选择 API 类型
](#services-apigateway-apitypes)
+ [

## 向 Lambda 函数添加终端节点
](#apigateway-add)
+ [

## 代理集成
](#apigateway-proxy)
+ [

## 事件格式
](#apigateway-example-event)
+ [

## 响应格式
](#apigateway-types-transforms)
+ [

## 权限
](#apigateway-permissions)
+ [

## 示例应用程序
](#services-apigateway-samples)
+ [

## Powertools for AWS Lambda 中的事件处理程序
](#services-apigateway-powertools)
+ [

# 教程：利用 API Gateway 使用 Lambda
](services-apigateway-tutorial.md)
+ [

# 利用 API Gateway API 处理 Lambda 错误
](services-apigateway-errors.md)
+ [

# 选择使用 HTTP 请求调用 Lambda 函数的方法
](apig-http-invoke-decision.md)

## 选择 API 类型
<a name="services-apigateway-apitypes"></a>

API Gateway 支持三种可调用 Lambda 函数的 API 类型：
+ [HTTP API](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api.html)：一种轻型的低延迟 RESTful API。
+ [REST API](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-rest-api.html)：一种功能丰富的可定制 RESTful API。
+ [WebSocket API](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-websocket-api.html)：一种 Web API，可与客户端保持持久连接并进行全双工通信。

HTTP API 和 REST API 都是用于处理 HTTP 请求并返回响应的 RESTful API。HTTP API 后推出，是使用 API Gateway 版本 2 API 构建的。以下是 HTTP API 的新功能：

**HTTP API 功能**
+ **自动部署** – 当您修改路线或集成时，更改会自动部署到启用了自动部署的阶段。
+ **默认阶段** – 您可以创建默认阶段 (`$default`)，以便在 API URL 的根路径处提供请求。对于具名阶段，必须在路径的开头包含阶段名称。
+ **CORS 配置** – 您可以配置 API，使其将 CORS 标头添加到传出响应中，而不是在函数代码中手动添加。

REST API 是 API Gateway 自发布起就支持的典型 RESTful API。REST API 现在具有更多的自定义、集成和管理功能。

**REST API 功能**
+ **集成类型** – REST API 支持自定义 Lambda 集成。使用自定义集成，您可以直接将请求正文发送到函数，也可以对其应用转换模板，然后再发送到函数。
+ **访问控制** – REST API 支持更多身份验证和授权选项。
+ **监控和跟踪** – REST API 支持 AWS X-Ray 跟踪和其他日志记录选项。

有关详细比较，请参阅《API Gateway 开发人员指南》**中的[在 HTTP API 和 REST API 之间选择](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-vs-rest.html)。

WebSocket API 也使用 API Gateway 版本 2 API 并支持类似的功能集。对于受益于客户端和 API 之间的持久连接的应用程序，请使用 WebSocket API。WebSocket API 提供全双工通信，这意味着客户端和 API 都可以持续发送消息，而无需等待响应。

HTTP API 支持简化的事件格式（2.0 版）。有关来自 HTTP API 的事件的示例，请参阅[针对 API Gateway 中的 HTTP API 创建 AWS Lambda 代理集成](https://docs.aws.amazon.com//apigateway/latest/developerguide/http-api-develop-integrations-lambda.html)。

有关更多信息，请参阅[针对 API Gateway 中的 HTTP API 创建 AWS Lambda 代理集成](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html)。

## 向 Lambda 函数添加终端节点
<a name="apigateway-add"></a>

**向 Lambda 函数添加公有端点**

1. 打开 Lamba 控制台的[函数](https://console.aws.amazon.com/lambda/home#/functions)页面。

1. 选择函数。

1. 在 **Function overview**（函数概览）下，选择 **Add trigger**（添加触发器）。

1. 选择 **API Gateway (API 网关)**。

1. 选择 **Create an API**（创建 API）或 **Use an existing API**（使用现有 API）。

   1. **New API（新 API）：**对于 **API type**（API 类型），请选择 **HTTP API**。有关更多信息，请参阅 [选择 API 类型](#services-apigateway-apitypes)。

   1. **现有 API：**从下拉列表中选择 API 或输入 API ID（例如，r3pmxmplak）。

1. 对于 **Security (安全性)**，请选择 **Open (打开)**。

1. 选择**添加**。

## 代理集成
<a name="apigateway-proxy"></a>

API Gateway API 由阶段、资源、方法和集成组成。阶段和资源决定终端节点的路径：

**API 路径格式**
+ `/prod/` – `prod` 阶段和根资源。
+ `/prod/user` – `prod` 阶段和 `user` 资源。
+ `/dev/{proxy+}` – `dev` 阶段中的路线。
+ `/` – (HTTP API) 默认阶段和根资源。

Lambda 集成将路径和 HTTP 方法的组合映射到 Lambda 函数。您可以将 API Gateway 配置为按原样（自定义集成）传递 HTTP 请求体，或者将请求正文封装在包含所有请求信息（包括标头、资源、路径和方法）的文档中。

有关更多信息，请参阅 [API Gateway 中的 Lambda 代理集成](https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html)。

## 事件格式
<a name="apigateway-example-event"></a>

Amazon API Gateway 会使用包含 HTTP 请求的 JSON 表示形式的事件[同步](invocation-sync.md)调用函数。对于自定义集成，该事件为请求的正文。对于代理集成，该事件具有已确定的结构。有关来自 API Gateway REST API 的代理事件的示例，请参阅《API Gateway 开发人员指南》**中的[用于代理集成的 Lambda 函数的输入格式](https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format)。

## 响应格式
<a name="apigateway-types-transforms"></a>

API Gateway 会等待函数响应并将结果转发给调用方。对于自定义集成，您可以定义集成响应和方法响应，以将函数的输出转换为 HTTP 响应。对于代理集成，函数必须以特定格式的响应形式来做出响应。

以下示例显示了来自 Node.js 函数的响应对象。该响应对象表示包含 JSON 文档的成功 HTTP 响应。

**Example index.mjs：代理集成响应对象（Node.js）**  

```
var response = {
      "statusCode": 200,
      "headers": {
        "Content-Type": "application/json"
      },
      "isBase64Encoded": false,
      "multiValueHeaders": { 
        "X-Custom-Header": ["My value", "My other value"],
      },
      "body": "{\n  \"TotalCodeSize\": 104330022,\n  \"FunctionCount\": 26\n}"
    }
```

Lambda 运行时会将响应对象序列化为 JSON 并将其发送给 API。此 API 会解析该响应并用它来创建 HTTP 响应，然后再将其发送到发出原始请求的客户端。

**Example HTTP 响应**  

```
< HTTP/1.1 200 OK
  < Content-Type: application/json
  < Content-Length: 55
  < Connection: keep-alive
  < x-amzn-RequestId: 32998fea-xmpl-4268-8c72-16138d629356
  < X-Custom-Header: My value
  < X-Custom-Header: My other value
  < X-Amzn-Trace-Id: Root=1-5e6aa925-ccecxmplbae116148e52f036
  <
  {
    "TotalCodeSize": 104330022,
    "FunctionCount": 26
  }
```

## 权限
<a name="apigateway-permissions"></a>

Amazon API Gateway 将从函数的[基于资源的策略](access-control-resource-based.md)获取调用函数的权限。您可以授予对整个 API 的调用权限，也可以仅授予对某个阶段、资源或方法的有限访问权限。

当您使用 Lambda 控制台、API Gateway 控制台或 AWS SAM 模板向函数添加 API 时，会自动更新函数的基于资源的策略。以下是一个示例函数策略。

**Example 函数策略**    
****  

```
{
  "Version":"2012-10-17",		 	 	 
  "Id": "default",
  "Statement": [
    {
      "Sid": "nodejs-apig-functiongetEndpointPermissionProd-BWDBXMPLXE2F",
      "Effect": "Allow",
      "Principal": {
        "Service": "apigateway.amazonaws.com"
      },
      "Action": "lambda:InvokeFunction",
      "Resource": "arn:aws:lambda:us-east-2:111122223333:function:nodejs-apig-function-1G3MXMPLXVXYI",
      "Condition": {
        "StringEquals": {
          "aws:SourceAccount": "111122223333"
        },
        "ArnLike": {
          "aws:SourceArn": "arn:aws:execute-api:us-east-2:111122223333:ktyvxmpls1/*/GET/"
        }
      }
    }
  ]
}
```

您可以通过以下 API 操作手动管理函数策略权限：
+ [AddPermission](https://docs.aws.amazon.com/lambda/latest/api/API_AddPermission.html)
+ [RemovePermission](https://docs.aws.amazon.com/lambda/latest/api/API_RemovePermission.html)
+ [GetPolicy](https://docs.aws.amazon.com/lambda/latest/api/API_GetPolicy.html)

使用 `add-permission` 命令，可授予对现有 API 的调用权限。示例：

```
aws lambda add-permission \
  --function-name my-function \
  --statement-id apigateway-get --action lambda:InvokeFunction \
  --principal apigateway.amazonaws.com \
  --source-arn "arn:aws:execute-api:us-east-2:123456789012:mnh1xmpli7/default/GET/"
```

您应看到以下输出：

```
{
    "Statement": "{\"Sid\":\"apigateway-test-2\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"apigateway.amazonaws.com\"},\"Action\":\"lambda:InvokeFunction\",\"Resource\":\"arn:aws:lambda:us-east-2:123456789012:function:my-function\",\"Condition\":{\"ArnLike\":{\"AWS:SourceArn\":\"arn:aws:execute-api:us-east-2:123456789012:mnh1xmpli7/default/GET\"}}}"
}
```

**注意**  
如果您的函数和 API 位于不同的 AWS 区域，则源 ARN 中的区域标识符必须与函数的区域（而不是 API 的区域）匹配。当 API Gateway 调用函数时，它会使用基于 API ARN 的资源 ARN，但该资源 ARN 已被修改为与函数的区域相匹配。

此示例中的源 ARN 授予对 API 默认阶段根资源 GET 方法的集成的权限，其 ID为 `mnh1xmpli7`。您可以在源 ARN 中使用星号授予对多个阶段、方法或资源的权限。

**资源模式**
+ `mnh1xmpli7/*/GET/*` – 对所有阶段中的所有资源调用 GET 方法。
+ `mnh1xmpli7/prod/ANY/user` – 对 `prod` 阶段中的 `user` 资源调用 ANY 方法。
+ `mnh1xmpli7/*/*/*` – 对所有阶段中的所有资源调用 ANY 方法。

有关查看策略和删除语句的详细信息，请参阅[在 Lambda 中查看基于资源的 IAM 策略](access-control-resource-based.md)。

## 示例应用程序
<a name="services-apigateway-samples"></a>

[带有 Node.js 的 API Gateway](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/nodejs-apig) 示例应用程序包含具有 AWS SAM 模板的函数，该模板可用于创建启用了 AWS X-Ray 跟踪的 REST API。它还包含用于部署和调用函数、测试 API 以及执行清理的脚本。

## Powertools for AWS Lambda 中的事件处理程序
<a name="services-apigateway-powertools"></a>

在编写由 API Gateway 端点（HTTP 或 REST）调用的 Lambda 函数时，Powertools for AWS Lambda 工具包中的事件处理程序提供路由、中间件、CORS 配置、OpenAPI 规范生成、请求验证、错误处理和其他有用的功能。事件处理程序实用程序适用于 Python 和 TypeScript/JavaScript。有关更多信息，请参阅 *Powertools for AWS Lambda (Python) 文档*中的[事件处理程序 REST API](https://docs.powertools.aws.dev/lambda/python/latest/core/event_handler/api_gateway/) 和 *Powertools for AWS Lambda (TypeScript) 文档*中的[事件处理程序 HTTP API](https://docs.aws.amazon.com/powertools/typescript/latest/features/event-handler/http/)。

### Python
<a name="services-apigateway-powertools-python"></a>

```
from aws_lambda_powertools import Logger
from aws_lambda_powertools.event_handler import APIGatewayRestResolver
from aws_lambda_powertools.logging import correlation_paths
from aws_lambda_powertools.utilities.typing.lambda_context import LambdaContext

app = APIGatewayRestResolver()
logger = Logger()

@app.get("/healthz")
def ping():
    return {"message": "health status ok"}

@logger.inject_lambda_context(correlation_id_path=correlation_paths.API_GATEWAY_REST)  
def lambda_handler(event: dict, context: LambdaContext) -> dict:
    return app.resolve(event, context)
```

### Typescript
<a name="services-apigateway-powertools-typescript"></a>

```
import { Router } from '@aws-lambda-powertools/event-handler/experimental-rest';
import { Logger } from '@aws-lambda-powertools/logger';
import {
  correlationPaths,
  search,
} from '@aws-lambda-powertools/logger/correlationId';
import type { Context } from 'aws-lambda/handler';

const logger = new Logger({
  correlationIdSearchFn: search,
});

const app = new Router({ logger });

app.get("/healthz", async () => {
  return { message: "health status ok" };
});

export const handler = async (event: unknown, context: Context) => {
  // You can continue using other utilities just as before
  logger.addContext(context);
  logger.setCorrelationId(event, correlationPaths.API_GATEWAY_REST);
  return app.resolve(event, context);
};
```

# 教程：利用 API Gateway 使用 Lambda
<a name="services-apigateway-tutorial"></a>

在本教程中，您将创建 REST API，您可以借助它使用 HTTP 请求调用 Lambda 函数。Lambda 函数将对 DynamoDB 表执行创建、读取、更新和删除（CRUD）操作。此处提供此函数用于演示，但您将学习配置可以调用任何 Lambda 函数的 API Gateway REST API。

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/APIG_tut_resources.png)


使用 API Gateway 可为用户提供安全的 HTTP 端点来调用 Lambda 函数，并可以通过节流流量以及自动验证和授权 API 调用来帮助管理大量函数调用。API Gateway 还使用 AWS Identity and Access Management（IAM）和 Amazon Cognito 提供灵活的安全控制。对于需要预先授权才能调用应用程序的使用案例而言，这非常有用。

**提示**  
Lambda 提供了两种方法来通过 HTTP 端点调用函数：API Gateway 和 Lambda 函数 URL。如果您不确定哪种方法最适合您的应用场景，请参阅[选择使用 HTTP 请求调用 Lambda 函数的方法](apig-http-invoke-decision.md)。

要完成本教程，您需要经历以下阶段：

1. 在 Python 或 Node.js 中创建和配置 Lambda 函数以对 DynamoDB 表执行操作。

1. 在 API Gateway 中创建 REST API 以连接 Lambda 函数。

1. 创建 DynamoDB 表，并在控制台中使用 Lambda 函数对其进行测试。

1. 部署 API 并在终端中使用 curl 测试完整设置。

完成这些阶段后，您将了解如何使用 API Gateway 创建 HTTP 端点，该端点可以安全地调用任何规模的 Lambda 函数。此外，您将了解如何部署 API、如何在控制台中对其进行测试以及如何使用终端发送 HTTP 请求。

## 创建权限策略
<a name="services-apigateway-tutorial-policy"></a>

为 Lambda 函数创建[执行角色](lambda-intro-execution-role.md)之前，首先需要创建权限策略以授予函数访问所需 AWS 资源的权限。在本教程中，该策略允许 Lambda 对 DynamoDB 表执行 CRUD 操作并写入 Amazon CloudWatch Logs。

**创建策略**

1. 打开 IAM 控制台的 [Policies（策略）页面](https://console.aws.amazon.com/iam/home#/policies)。

1. 选择**创建策略**。

1. 选择 **JSON** 选项卡，然后将以下自定义策略粘贴到 JSON 编辑器中。

------
#### [ JSON ]

****  

   ```
   {
     "Version":"2012-10-17",		 	 	 
     "Statement": [
       {
         "Sid": "Stmt1428341300017",
         "Action": [
           "dynamodb:DeleteItem",
           "dynamodb:GetItem",
           "dynamodb:PutItem",
           "dynamodb:Query",
           "dynamodb:Scan",
           "dynamodb:UpdateItem"
         ],
         "Effect": "Allow",
         "Resource": "*"
       },
       {
         "Sid": "",
         "Resource": "*",
         "Action": [
           "logs:CreateLogGroup",
           "logs:CreateLogStream",
           "logs:PutLogEvents"
         ],
         "Effect": "Allow"
       }
     ]
   }
   ```

------

1. 选择**下一步：标签**。

1. 选择**下一步：审核**。

1. 在 **Review policy (查看策略)** 下，为策略 **Name (名称)** 输入 **lambda-apigateway-policy**。

1. 选择**创建策略**。

## 创建执行角色
<a name="services-apigateway-tutorial-role"></a>

[执行角色](lambda-intro-execution-role.md)是一个 AWS Identity and Access Management（IAM）角色，用于向 Lambda 函数授予访问 AWS 服务 和资源的权限。要使函数对 DynamoDB 表执行操作，您需要附加上一步中创建的权限策略。

**创建执行角色并附加自定义权限策略**

1. 打开 IAM 控制台的[角色页面](https://console.aws.amazon.com/iam/home#/roles)。

1. 选择**创建角色**。

1. 对于可信实体，选择 **AWS 服务**，对于使用案例，选择 **Lambda**。

1. 选择**下一步**。

1. 在策略搜索框中，输入 **lambda-apigateway-policy**。

1. 在搜索结果中，选择您创建的策略（`lambda-apigateway-policy`），然后选择 **Next**（下一步）。

1. 在 **Role details**（角色详细信息）下，为 **Role name**（角色名称）输入 **lambda-apigateway-role**，然后选择 **Create role**（创建角色）。

## 创建 Lambda 函数
<a name="services-apigateway-tutorial-function"></a>

1. 打开 Lambda 控制台的[“函数”页面](https://console.aws.amazon.com/lambda/home#/functions)，然后选择**创建函数**。

1. 选择**从头开始创作**。

1. 对于**函数名称**，请输入 `LambdaFunctionOverHttps`。

1. 对于**运行时**，选择最新的 Node.js 或 Python 运行时。

1. 在**权限**下，展开**更改默认执行角色**。

1. 选择**使用现有角色**，然后选择您之前创建的 **lambda-apigateway-role** 角色。

1. 选择**创建函数**。

1. 在**代码源**窗格中，将默认代码替换为下面的 Node.js 或 Python 代码。

------
#### [ Node.js ]

   `region` 设置必须与您部署函数和[创建 DynamoDB 表](#services-apigateway-tutorial-table)的 AWS 区域相匹配。

**Example index.mjs**  

   ```
   import { DynamoDBDocumentClient, PutCommand, GetCommand, 
            UpdateCommand, DeleteCommand} from "@aws-sdk/lib-dynamodb";
   import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
   
   const ddbClient = new DynamoDBClient({ region: "us-east-2" });
   const ddbDocClient = DynamoDBDocumentClient.from(ddbClient);
   
   // Define the name of the DDB table to perform the CRUD operations on
   const tablename = "lambda-apigateway";
   
   /**
    * Provide an event that contains the following keys:
    *
    *   - operation: one of 'create,' 'read,' 'update,' 'delete,' or 'echo'
    *   - payload: a JSON object containing the parameters for the table item
    *     to perform the operation on
    */
   export const handler = async (event, context) => {
      
        const operation = event.operation;
      
        if (operation == 'echo'){
             return(event.payload);
        }
        
       else { 
           event.payload.TableName = tablename;
           let response;
           
           switch (operation) {
             case 'create':
                  response = await ddbDocClient.send(new PutCommand(event.payload));
                  break;
             case 'read':
                  response = await ddbDocClient.send(new GetCommand(event.payload));
                  break;
             case 'update':
                  response = ddbDocClient.send(new UpdateCommand(event.payload));
                  break;
             case 'delete':
                  response = ddbDocClient.send(new DeleteCommand(event.payload));
                  break;
             default:
               response = 'Unknown operation: ${operation}';
             }
           console.log(response);
           return response;
       }
   };
   ```

------
#### [ Python ]

**Example lambda\$1function.py**  

   ```
   import boto3
   
   # Define the DynamoDB table that Lambda will connect to
   table_name = "lambda-apigateway"
   
   # Create the DynamoDB resource
   dynamo = boto3.resource('dynamodb').Table(table_name)
   
   # Define some functions to perform the CRUD operations
   def create(payload):
       return dynamo.put_item(Item=payload['Item'])
   
   def read(payload):
       return dynamo.get_item(Key=payload['Key'])
   
   def update(payload):
       return dynamo.update_item(**{k: payload[k] for k in ['Key', 'UpdateExpression', 
       'ExpressionAttributeNames', 'ExpressionAttributeValues'] if k in payload})
   
   def delete(payload):
       return dynamo.delete_item(Key=payload['Key'])
   
   def echo(payload):
       return payload
   
   operations = {
       'create': create,
       'read': read,
       'update': update,
       'delete': delete,
       'echo': echo,
   }
   
   def lambda_handler(event, context):
       '''Provide an event that contains the following keys:
         - operation: one of the operations in the operations dict below
         - payload: a JSON object containing parameters to pass to the 
           operation being performed
       '''
       
       operation = event['operation']
       payload = event['payload']
       
       if operation in operations:
           return operations[operation](payload)
           
       else:
           raise ValueError(f'Unrecognized operation "{operation}"')
   ```

------
**注意**  
在此示例中，DynamoDB 表的名称定义为函数代码中的变量。在实际应用程序中，最佳做法是将此参数作为环境变量传递，并避免对表名称进行硬编码。有关更多信息，请参阅[使用 AWS Lambda 环境变量](https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html)。

1. 在**部署**部分，选择**部署**以更新函数的代码：  
![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/getting-started-tutorial/deploy-console.png)

## 测试此函数
<a name="services-apigateway-tutorial-test-function"></a>

将函数与 API Gateway 集成之前，请确认您已成功部署该函数。使用 Lambda 控制台向您的函数发送测试事件。

1. 在函数的 Lambda 控制台页面上，选择**测试**选项卡。  
![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/test-tab.png)

1. 向下滚动到**事件 JSON** 部分，并将默认事件替换为以下内容。此事件与 Lambda 函数预期的结构相匹配。

   ```
   {
       "operation": "echo",
       "payload": {
           "somekey1": "somevalue1",
           "somekey2": "somevalue2"
       }
   }
   ```

1. 选择**测试**。

1. 在**执行函数：成功**下，展开**详细信息**。您应看到以下响应：

   ```
   {
     "somekey1": "somevalue1",
     "somekey2": "somevalue2"
   }
   ```

## 使用 API Gateway 创建 REST API
<a name="services-apigateway-tutorial-api"></a>

在此步骤中，您将创建用于调用 Lambda 函数的 API Gateway REST API。

**创建 API**

1. 打开 [API Gateway 控制台](https://console.aws.amazon.com/apigateway)。

1. 选择 **Create API (创建 API)**。

1. 在 **REST API** 框中，选择 **Build (构建)**。

1. 在 **API 详细信息**下，将**新建 API** 保留为选中状态，然后在 **API 名称**中输入 **DynamoDBOperations**。

1. 选择**创建 API**。

## 在 REST API 上创建资源
<a name="services-apigateway-tutorial-resource"></a>

要将 HTTP 方法添加到 API，您首先需要创建资源供该方法运行。您可以在此处创建资源来管理 DynamoDB 表。

**创建资源**

1. 在 [API Gateway 控制台](https://console.aws.amazon.com/apigateway)中，在您的 API 的**资源**页面上，选择**创建资源**。

1. 在**资源详细信息**中，在**资源名称**中输入 **DynamoDBManager**。

1. 选择**创建资源**。

## 创建 HTTP POST 方法
<a name="services-apigateway-tutorial-method"></a>

在此步骤中，您将为 `DynamoDBManager` 资源创建方法（`POST`）。您可以将此 `POST` 方法链接到 Lambda 函数，以便当该方法接收 HTTP 请求时，API Gateway 可以调用 Lambda 函数。

**注意**  
 就本教程而言，一个 HTTP 方法（`POST`）可用于调用一个 Lambda 函数，该函数将对 DynamoDB 表执行全部操作。在实际应用程序中，最佳做法是针对每个操作使用不同的 Lambda 函数和 HTTP 方法。有关更多信息，请参阅 Serverless Land 中的 [The Lambda monolith](https://serverlessland.com/content/service/lambda/guides/aws-lambda-operator-guide/monolith)。

**创建 POST 方法**

1. 在您的 API 的**资源**页面上，确保突出显示 `/DynamoDBManager` 资源。然后，在**方法**窗格中，选择**创建方法**。

1. 对于**方法类型**，选择 **POST**。

1. 对于**集成类型**，将 **Lambda 函数**保留为选中状态。

1. 对于 **Lambda 函数**，选择函数（`LambdaFunctionOverHttps`）的 Amazon 资源名称（ARN）。

1. 选择**创建方法**。

## 创建 DynamoDB 表
<a name="services-apigateway-tutorial-table"></a>

创建一个空的 DynamoDB 表，Lambda 函数将对其执行 CRUD 操作。

**创建 DynamoDB 表**

1. 打开 DynamoDB 控制台中 [Tables page](https://console.aws.amazon.com/dynamodbv2#tables)（表页面）。

1. 选择**创建表**。

1. 在 **Table details (表详细信息)** 下，执行以下操作：

   1. 对于**表名称**，输入 **lambda-apigateway**。

   1. 对于 **Partition key (分区键)**，输入 **id**，并将数据类型设置为 **String (字符串)**。

1. 在 **Table settings**（表设置）下，使用 **Default settings**（默认设置）。

1. 选择**创建表**。

## 测试 API Gateway、Lambda 和 DynamoDB 的集成
<a name="services-apigateway-tutorial-test-setup"></a>

现在，您已准备好测试 API Gateway API 方法与 Lambda 函数和 DynamoDB 表的集成。借助 API Gateway 控制台，您可以使用控制台的测试功能将请求直接发送到 `POST` 方法。在此步骤中，您首先使用 `create` 操作将新项目添加到 DynamoDB 表，然后使用 `update` 操作修改该项目。

**测试 1：在 DynamoDB 表中创建新项目**

1. 在 [API Gateway console](https://console.aws.amazon.com/apigateway)（API Gateway 控制台）中，选择 API（`DynamoDBOperations`）。

1. 在 `DynamoDBManager` 资源下，选择 **POST** 方法。

1. 选择**测试**选项卡。您可能需要选择右箭头按钮，以显示该选项卡。

1. 在**测试方法**下，将**查询字符串**和**标头**留空。对于**请求正文**，粘贴以下 JSON：

   ```
   {
     "operation": "create",
     "payload": {
       "Item": {
         "id": "1234ABCD",
         "number": 5
       }
     }
   }
   ```

1. 选择**测试**。

   测试完成后，所显示的结果应显示状态 `200`。此状态代码表示 `create` 操作成功。

    要进行确认，请检查 DynamoDB 表现在是否包含新项目。

1. 打开 DynamoDB 控制台中的 [Tables page](https://console.aws.amazon.com/dynamodbv2#tables)（表页面），然后选择 `lambda-apigateway` 表。

1. 选择 **Explore table items**（浏览表项目）。在 **Items returned**（返回的项目）窗格中，您会看到一个带 **id** `1234ABCD` 和 **number**（编号）`5` 的项目。示例：  
![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/items-returned.png)

**测试 2：更新 DynamoDB 表中的项目**

1. 在 [API Gateway 控制台](https://console.aws.amazon.com/apigateway)中，返回到 POST 方法的**测试**选项卡。

1. 在**测试方法**下，将**查询字符串**和**标头**留空。对于**请求正文**，粘贴以下 JSON：

   ```
   {
       "operation": "update",
       "payload": {
           "Key": {
               "id": "1234ABCD"
           },
           "UpdateExpression": "SET #num = :newNum",
           "ExpressionAttributeNames": {
               "#num": "number"
           },
           "ExpressionAttributeValues": {
               ":newNum": 10
           }
       }
   }
   ```

1. 选择**测试**。

   测试完成后，所显示的结果应显示状态 `200`。此状态代码表示 `update` 操作成功。

    要进行确认，请检查 DynamoDB 表中的项目是否已修改。

1. 打开 DynamoDB 控制台中的 [Tables page](https://console.aws.amazon.com/dynamodbv2#tables)（表页面），然后选择 `lambda-apigateway` 表。

1. 选择 **Explore table items**（浏览表项目）。在 **Items returned**（返回的项目）窗格中，您会看到一个带 **id** `1234ABCD` 和 **number**（编号）`10` 的项目。  
![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/items-returned-2.png)

## 部署 API
<a name="services-apigateway-tutorial-deploy-api"></a>

要让客户端调用 API，您必须创建部署和关联的阶段。阶段表示 API 的快照，包括其方法和集成。

**部署 API**

1. 打开 [API Gateway console](https://console.aws.amazon.com/apigateway)（API Gateway 控制台）的 **API** 页面，然后选择 `DynamoDBOperations` API。

1. 在您的 API 的**资源**页面上，选择**部署 API**。

1. 对于**阶段**，请选择**\$1新建阶段\$1**，然后在**阶段名称**中输入 **test**。

1. 选择**部署**。

1. 在**阶段详细信息**窗格中，复制**调用 URL**。您将在下一步中使用它，通过 HTTP 请求调用函数。

## 使用 curl 通过 HTTP 请求调用函数
<a name="services-apigateway-tutorial-invoke-function"></a>

现在，您可以通过向 API 发出 HTTP 请求来调用 Lambda 函数。在此步骤中，您将在 DynamoDB 表中创建一个新项目，然后对该项目执行读取、更新和删除操作。

**使用 curl 在 DynamoDB 表中创建项目**

1. 在本地计算机上打开终端或命令提示符，使用您在上一步中复制的调用 URL 运行以下 `curl` 命令。此命令使用以下选项：
   + `-H`：向请求添加自定义标头。这里，它将内容类型指定为 JSON。
   + `-d`：在请求正文中发送数据。此选项默认使用 HTTP POST 方法。

------
#### [ Linux/macOS ]

   ```
   curl https://l8togsqxd8.execute-api.us-east-2.amazonaws.com/test/DynamoDBManager \
   -H "Content-Type: application/json" \
   -d '{"operation": "create", "payload": {"Item": {"id": "5678EFGH", "number": 15}}}'
   ```

------
#### [ PowerShell ]

   ```
   curl.exe 'https://l8togsqxd8.execute-api.us-east-2.amazonaws.com/test/DynamoDBManager' -H 'Content-Type: application/json' -d '{\"operation\": \"create\", \"payload\": {\"Item\": {\"id\": \"5678EFGH\", \"number\": 15}}}'
   ```

------

   如果操作成功，您应该会看到返回的响应，其中包含 HTTP 状态代码 200。

1. 您还可以使用 DynamoDB 控制台，通过执行以下操作来验证新项目是否在表中：

   1. 打开 DynamoDB 控制台中的 [Tables page](https://console.aws.amazon.com/dynamodbv2#tables)（表页面），然后选择 `lambda-apigateway` 表。

   1. 选择 **Explore table items**（浏览表项目）。在 **Items returned**（返回的项目）窗格中，您会看到一个带 **id** `5678EFGH` 和 **number**（编号）`15` 的项目。

**使用 curl 读取 DynamoDB 表中的项目**
+ 在终端或命令提示符中，运行以下 `curl` 命令读取您刚创建的项目的值。使用您自己的调用 URL。

------
#### [ Linux/macOS ]

  ```
  curl https://l8togsqxd8.execute-api.us-east-2.amazonaws.com/test/DynamoDBManager \
  -H "Content-Type: application/json" \
  -d '{"operation": "read", "payload": {"Key": {"id": "5678EFGH"}}}'
  ```

------
#### [ PowerShell ]

  ```
  curl.exe 'https://l8togsqxd8.execute-api.us-east-2.amazonaws.com/test/DynamoDBManager' -H 'Content-Type: application/json' -d '{\"operation\": \"read\", \"payload\": {\"Key\": {\"id\": \"5678EFGH\"}}}'
  ```

------

  根据您选择的是 Node.js 还是 Python 函数代码，您应该会看到如下输出：

------
#### [ Node.js ]

  ```
  {"$metadata":{"httpStatusCode":200,"requestId":"7BP3G5Q0C0O1E50FBQI9NS099JVV4KQNSO5AEMVJF66Q9ASUAAJG",
  "attempts":1,"totalRetryDelay":0},"Item":{"id":"5678EFGH","number":15}}
  ```

------
#### [ Python ]

  ```
  {"Item":{"id":"5678EFGH","number":15},"ResponseMetadata":{"RequestId":"QNDJICE52E86B82VETR6RKBE5BVV4KQNSO5AEMVJF66Q9ASUAAJG",
  "HTTPStatusCode":200,"HTTPHeaders":{"server":"Server","date":"Wed, 31 Jul 2024 00:37:01 GMT","content-type":"application/x-amz-json-1.0",
  "content-length":"52","connection":"keep-alive","x-amzn-requestid":"QNDJICE52E86B82VETR6RKBE5BVV4KQNSO5AEMVJF66Q9ASUAAJG","x-amz-crc32":"2589610852"},
  "RetryAttempts":0}}
  ```

------

**使用 curl 更新 DynamoDB 表中的项目**

1. 在终端或命令提示符中，运行以下 `curl` 命令，通过更改 `number` 值来更新您刚创建的项目。使用您自己的调用 URL。

------
#### [ Linux/macOS ]

   ```
   curl https://l8togsqxd8.execute-api.us-east-2.amazonaws.com/test/DynamoDBManager \
   -H "Content-Type: application/json" \
   -d '{"operation": "update", "payload": {"Key": {"id": "5678EFGH"}, "UpdateExpression": "SET #num = :new_value", "ExpressionAttributeNames": {"#num": "number"}, "ExpressionAttributeValues": {":new_value": 42}}}'
   ```

------
#### [ PowerShell ]

   ```
   curl.exe 'https://l8togsqxd8.execute-api.us-east-2.amazonaws.com/test/DynamoDBManager' -H 'Content-Type: application/json' -d '{\"operation\": \"update\", \"payload\": {\"Key\": {\"id\": \"5678EFGH\"}, \"UpdateExpression\": \"SET #num = :new_value\", \"ExpressionAttributeNames\": {\"#num\": \"number\"}, \"ExpressionAttributeValues\": {\":new_value\": 42}}}'
   ```

------

1. 要确认项目的 `number` 值已更新，再运行一条 read 命令：

------
#### [ Linux/macOS ]

   ```
   curl https://l8togsqxd8.execute-api.us-east-2.amazonaws.com/test/DynamoDBManager \
   -H "Content-Type: application/json" \
   -d '{"operation": "read", "payload": {"Key": {"id": "5678EFGH"}}}'
   ```

------
#### [ PowerShell ]

   ```
   curl.exe 'https://l8togsqxd8.execute-api.us-east-2.amazonaws.com/test/DynamoDBManager' -H 'Content-Type: application/json' -d '{\"operation\": \"read\", \"payload\": {\"Key\": {\"id\": \"5678EFGH\"}}}'
   ```

------

**使用 curl 删除 DynamoDB 表中的项目**

1. 在终端或命令提示符中，运行以下 `curl` 命令以删除您刚创建的项目。使用您自己的调用 URL。

------
#### [ Linux/macOS ]

   ```
   curl https://l8togsqxd8.execute-api.us-east-2.amazonaws.com/test/DynamoDBManager \
   -H "Content-Type: application/json" \
   -d '{"operation": "delete", "payload": {"Key": {"id": "5678EFGH"}}}'
   ```

------
#### [ PowerShell ]

   ```
   curl.exe 'https://l8togsqxd8.execute-api.us-east-2.amazonaws.com/test/DynamoDBManager' -H 'Content-Type: application/json' -d '{\"operation\": \"delete\", \"payload\": {\"Key\": {\"id\": \"5678EFGH\"}}}'
   ```

------

1. 确认删除操作是否成功。在 DynamoDB 控制台**浏览项目**页面的**返回的项目**窗格中，验证表中是否不再包含带 **id** `5678EFGH` 的项目。

## 清除资源（可选）
<a name="cleanup"></a>

除非您想要保留为本教程创建的资源，否则可立即将其删除。通过删除您不再使用的 AWS 资源，可防止您的 AWS 账户 产生不必要的费用。

**删除 Lambda 函数**

1. 打开 Lamba 控制台的 [Functions（函数）页面](https://console.aws.amazon.com/lambda/home#/functions)。

1. 选择您创建的函数。

1. 依次选择**操作**和**删除**。

1. 在文本输入字段中键入 **confirm**，然后选择**删除**。

**删除执行角色**

1. 打开 IAM 控制台的[角色页面](https://console.aws.amazon.com/iam/home#/roles)。

1. 选择您创建的执行角色。

1. 选择**删除**。

1. 在文本输入字段中输入角色名称，然后选择**删除**。

**删除 API**

1. 打开 API Gateway 控制台的 [API 页面](https://console.aws.amazon.com/apigateway/main/apis)。

1. 选择您创建的 API。

1. 依次选择**操作**和**删除**。

1. 选择**删除**。

**删除 DynamoDB 表**

1. 打开 DynamoDB 控制台中 [Tables page](https://console.aws.amazon.com//dynamodb/home#tables:)（表页面）。

1. 选择您创建的表。

1. 选择**删除**。

1. 在文本框中输入 **delete**。

1. 选择 **删除表**。

# 利用 API Gateway API 处理 Lambda 错误
<a name="services-apigateway-errors"></a>

API Gateway 将所有调用和函数错误视为内部错误。如果 Lambda API 拒绝调用请求，API Gateway 会返回 500 错误代码。如果函数运行但返回错误，或返回格式错误的响应，API Gateway 会返回 502。在这两种情况下，来自 API Gateway 的响应的正文都是 `{"message": "Internal server error"}`。

**注意**  
API Gateway 不会重试 Lambda 调用。如果 Lambda 返回错误，API Gateway 会向客户端返回错误响应。

以下示例显示导致 API Gateway 中出现函数错误和 502 的请求的 X-Ray 跟踪映射。客户端收到通用错误消息。

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/tracemap-apig-502.png)


要自定义错误响应，您必须捕获代码中的错误并以所需格式设置响应的格式。

**Example [index.mjs](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/nodejs-apig/function/index.mjs)：格式设置错误**  

```
var formatError = function(error){
  var response = {
    "statusCode": error.statusCode,
    "headers": {
      "Content-Type": "text/plain",
      "x-amzn-ErrorType": error.code
    },
    "isBase64Encoded": false,
    "body": error.code + ": " + error.message
  }
  return response
}
```

API Gateway 将此响应转换为带有自定义状态代码和正文的 HTTP 错误。在跟踪映射中，函数节点为绿色，因为它处理了错误。

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/tracemap-apig-404.png)


# 选择使用 HTTP 请求调用 Lambda 函数的方法
<a name="apig-http-invoke-decision"></a>

Lambda 的许多常见使用案例都涉及使用 HTTP 请求调用函数。例如，您可能希望 Web 应用程序通过浏览器请求调用函数。Lambda 函数还可用于创建完整的 REST API、处理来自移动应用程序的用户交互、通过 HTTP 调用处理来自外部服务的数据，或者创建自定义 Webhook。

以下几节介绍了通过 HTTP 调用 Lambda 的选择，并提供了有助于您针对特定使用案例做出正确决策的信息。

## 在选择 HTTP 调用方法时，您有什么选择？
<a name="w2aad101c29c46b9"></a>

Lambda 提供了两种使用 HTTP 请求调用函数的主要方法：[函数 URL](urls-configuration.md) 和 [API Gateway](services-apigateway.md)。这两个选项之间的主要差异如下所示：
+ **Lambda 函数 URL** 为 Lambda 函数提供了简单、直接的 HTTP 端点。它们针对简单性和成本效益进行了优化，且提供了通过 HTTP 公开 Lambda 函数的最快路径。
+ **API Gateway** 是用于构建功能齐全的 API 的更高级服务。API Gateway 针对大规模构建和管理生产 API 进行了优化，并提供了用于安全、监控和流量管理的全面工具。

## 在已经知道自己要求的情况下的建议
<a name="w2aad101c29c46c11"></a>

如果您已经明确自己的要求，则以下是我们的基本建议：

建议在简单的应用程序或原型设计中使用**[函数 URL](urls-configuration.md)**，其中您只需要基本身份验证方法和请求/响应处理，并且希望将成本和复杂性降至最低。

**[API Gateway](services-apigateway.md)** 是大规模生产应用程序或需要更高级功能的情况下的更好选择，例如 [OpenAPI 描述](https://www.openapis.org/)支持、身份验证选项选择、自定义域名或丰富的请求/响应处理（包括节流、缓存和请求/响应转换）。

## 选择用于调用 Lambda 函数的方法时要考虑的事项
<a name="w2aad101c29c46c13"></a>

在函数 URL 与 API Gateway 之间进行选择时，您需要考虑以下因素：
+ 您的身份验证需求，例如您需要 OAuth 还是 Amazon Cognito 来对用户进行身份验证
+ 您的扩展要求和要实施的 API 的复杂性
+ 您是否需要请求验证和请求/响应格式等高级功能
+ 您的监控要求
+ 您的成本目标

通过了解这些因素，您可以选择能平衡安全性、复杂性和成本要求的最佳选项。

以下信息汇总了两个选项之间的主要差异。

### 身份验证
<a name="w2aad101c29c46c13c11b1"></a>
+ **函数 URL** 通过 AWS Identity and Access Management（IAM）提供基本身份验证选项。您可以将端点配置为公有（无身份验证）或要求 IAM 身份验证。通过 IAM 身份验证，您可以使用标准 AWS 凭证或 IAM 角色来控制访问权限。虽然设置起来很简单，但与其他身份验证方法相比，此方法提供的选项有限。
+ **API Gateway** 提供对更全面的身份验证选项的访问权限。除了 IAM 身份验证外，您还可以使用 [Lambda 授权方](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html)（自定义身份验证逻辑）、[Amazon Cognito](https://docs.aws.amazon.com/cognito/latest/developerguide/what-is-amazon-cognito.html) 用户池和 OAuth 2.0 流。这种灵活性使您能够实施复杂的身份验证方案，包括第三方身份验证提供商、基于令牌的身份验证和多重身份验证。

### 请求/响应处理
<a name="w2aad101c29c46c13c11b3"></a>
+ **函数 URL** 提供了基本的 HTTP 请求和响应处理。它们支持标准 HTTP 方法，并包括内置的跨源资源共享（CORS）支持。虽然它们可以自然地处理 JSON 有效载荷和查询参数，但不提供请求转换或验证功能。响应处理同样简单 – 客户端从您的 Lambda 函数接收的响应与 Lambda 返回的响应完全相同。
+ **API Gateway** 提供了高级的请求和响应处理功能。您可以定义请求验证器、使用映射模板转换请求和响应、设置请求/响应标头，以及实施响应缓存。API Gateway 还支持二进制有效载荷和自定义域名，并且可以在响应到达客户端之前对其修改。您可以通过使用 JSON 架构来为请求/响应验证和转换设置模型。

### 扩展
<a name="w2aad101c29c46c13c11b5"></a>
+ **函数 URL** 可直接根据您的 Lambda 函数的并发限制进行扩展，并通过将函数扩展到其配置的最大并发限制来处理流量激增。达到该限制后，Lambda 将使用 HTTP 429 响应来响应其他请求。由于没有内置的队列机制，因此处理扩展完全取决于您的 Lambda 函数的配置。默认情况下，Lambda 函数每个 AWS 区域的并发执行限制为 1000 次。
+ **API Gateway** 在 Lambda 自己的扩展基础上提供了额外的扩展功能。其中包括内置的请求队列和节流控制，使您可以更妥善地管理流量激增。默认情况下，API Gateway 每个区域每秒最多可以处理 1 万个请求，容量爆增为每秒 5000 个请求。还提供了在不同级别（API、阶段或方法）限制请求的工具，以保护您的后端。

### 监控
<a name="w2aad101c29c46c13c11b7"></a>
+ **函数 URL** 通过 Amazon CloudWatch 指标来提供基本监控，包括请求计数、延迟和错误率。您可以访问标准 Lambda 指标和日志，它们显示了进入函数的原始请求。虽然这提供了基本的运营可见性，但这些指标主要侧重于函数执行。
+ **API Gateway** 提供全面的监控功能，其中包括详细的指标、日志记录和跟踪选项。您可以通过 CloudWatch 监控 API 调用、延迟、错误率和缓存命中/未命中率。API Gateway 还与 AWS X-Ray 集成，用于分布式跟踪，并提供可自定义的日志记录格式。

### 成本
<a name="w2aad101c29c46c13c11b9"></a>
+ **函数 URL** 遵循标准 Lambda 定价模型 – 您只需按函数调用和计算时间付费。URL 端点本身不收取额外费用。如果您不需要 API Gateway 的其他功能，则会使其成为简单 API 或低流量应用程序的经济实惠之选。
+ **API Gateway** 提供[免费套餐](https://aws.amazon.com/api-gateway/pricing/#Free_Tier)，其中包括为 REST API 接收的一百万次 API 调用和为 HTTP API 接收的一百万次 API 调用。此后，API Gateway 将对 API 调用、数据传输和缓存（如果已启用）收费。请参阅 API Gateway [定价页面](https://aws.amazon.com/api-gateway/pricing/)，了解您自己的使用案例的成本。

### 其他功能
<a name="w2aad101c29c46c13c11c11"></a>
+ **函数 URL** 旨在实现简单性以及 Lambda 直接集成。它们同时支持 HTTP 和 HTTPS 端点，提供内置的 CORS 支持，并提供双堆栈（IPv4 和 IPv6）端点。虽然它们缺乏高级功能，但在需要以快速、直接的方式通过 HTTP 公开 Lambda 函数的场景中，它们表现出色。
+ **API Gateway** 包括许多其他功能，例如 API 版本控制、阶段管理、使用计划的 API 密钥、通过 Swagger/OpenAPI 提供的 API 文档、WebSocket API、VPC 内的私有 API，以及用于提高安全性的 WAF 集成。它还支持金丝雀部署、用于测试的模拟集成以及与 Lambda 之外的其他 AWS 服务的集成。

## 选择调用 Lambda 函数的方法
<a name="w2aad101c29c46c15"></a>

现在，您已经了解了在 Lambda 函数 URL 与 API Gateway 之间进行选择的标准以及它们之间的主要区别，您可以选择最符合您需求的选项，并使用以下资源来帮助您开始使用它。

------
#### [ Function URLs ]

**通过以下资源开始使用函数 URL**
+ 按照[使用函数 URL 创建 Lambda 函数](urls-webhook-tutorial.md)教程进行操作
+ 在本指南的[创建和管理 Lambda 函数 URL](urls-configuration.md) 章节中了解有关函数 URL 的更多信息
+ 通过执行以下操作，尝试控制台内指导教程**创建简单的 Web 应用程序**：

1. 打开 Lamba 控制台的[函数页面](https://console.aws.amazon.com/lambda/home#/functions)。

1. 选择屏幕右上角的图标打开帮助面板。  
![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/console_help_screenshot.png)

1. 选择**教程**。

1. 在**创建简单的 Web 应用程序**中，选择**开始教程**。

------
#### [ API Gateway ]

**通过以下资源开始使用 Lambda 和 API Gateway**
+ 按照[利用 API Gateway 使用 Lambda](services-apigateway-tutorial.md) 教程，创建与后端 Lambda 函数集成的 REST API。
+ 要详细了解 API Gateway 提供的不同类型 API，请参阅《Amazon API Gateway 开发人员指南》**的以下部分：
  + [API Gateway REST API](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-rest-api.html)
  + [API Gateway HTTP API](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api.html)
  + [API Gateway WebSocket API](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-websocket-api.html)
+ 试用《Amazon API Gateway 开发人员指南》**的[教程和研讨会](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-tutorials.html)部分中的一个或多个示例。

------

# 配合使用 AWS Lambda和 AWS 基础架构编辑器
<a name="services-appcomposer"></a>

AWS 基础架构编辑器 是一款在 AWS 上设计现代应用程序的可视化生成器。您可以通过在可视画布中拖动、分组和连接 AWS 服务 来设计应用程序架构。基础设施编辑器根据您的设计创建基础设施即代码（IaC）模板，创建的模板可以使用 [AWS SAM](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html) 或 [CloudFormation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/Welcome.html) 进行部署。

## 将 Lambda 函数出到基础设施编辑器
<a name="services-appcomposer-export"></a>

开始使用基础设施编辑器时，您可以使用 Lambda 控制台根据现有 Lambda 函数的配置创建一个新项目。要将函数的配置和代码导出到基础设施编辑器来创建新项目，请执行以下操作：

1. 打开 Lamba 控制台的 [Functions](https://console.aws.amazon.com/lambda/home#/functions)（函数）页面。

1. 选择您希望作为基础设施编辑器项目基础的函数。

1. 在**函数概述**窗格中，选择**导出到基础设施编辑器**。

   为了将函数的配置和代码导出到基础设施编辑器，Lambda 在您的账户中创建了一个 Amazon S3 存储桶来临时存储此数据。

1. 在对话框中，选择**确认并创建项目**以接受此存储桶的默认名称，并将函数的配置和代码导出到基础设施编辑器。

1. （可选）要为 Lambda 创建的 Amazon S3 存储桶选择其他名称，请输入新名称并选择**确认并创建项目**。Amazon S3 存储桶的名称必须全局唯一，并遵守[存储桶命名规则](https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html)。

1. 要在基础设施编辑器中保存项目和函数文件，请激活[本地同步模式](https://docs.aws.amazon.com/application-composer/latest/dg/reference-features-local-sync.html)。

**注意**  
如果您之前使用过**导出到应用程序编辑器**功能并使用默认名称创建了 Amazon S3 存储桶，Lambda 可以重复使用该存储桶（如果它仍然存在）。接受对话框中的默认存储桶名称以重复使用现有存储桶。

### Amazon S3 传输存储桶配置
<a name="services-appcomposer-bucket-info"></a>

Lambda 为传输您的函数配置而创建的 Amazon S3 存储桶会使用 AES 256 加密标准自动加密对象。Lambda 还将存储桶配置为使用[存储桶拥有者条件](https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucket-owner-condition.html)，以确保只有您的 AWS 账户 能向存储桶添加对象。

Lambda 将存储桶配置为在上传对象 10 天后自动将其删除。但是，Lambda 不会自动删除存储桶本身。要从您的 AWS 账户 删除存储桶，请按照[删除存储桶](https://docs.aws.amazon.com/AmazonS3/latest/userguide/delete-bucket.html)中的说明进行操作。默认存储桶名称使用前缀 `lambdasam`、10 位字母数字字符串以及您创建函数的所在 AWS 区域：

```
lambdasam-06f22da95b-us-east-1
```

为避免给您的 AWS 账户 增加额外费用，建议在完成将函数导出到基础设施编辑器后立即删除 Amazon S3 存储桶。

标准 [Amazon S3 定价](https://aws.amazon.com/s3/pricing/)适用。

### 所需的权限
<a name="services-appcomposer-permissions"></a>

要使用 Lambda 与基础设施编辑器集成功能，您需要一定的权限才能下载 AWS SAM 模板并将函数配置写入 Amazon S3。

要下载 AWS SAM 模板，您必须拥有使用以下 API 操作的权限：
+ [GetPolicy](https://docs.aws.amazon.com/lambda/latest/api/API_GetPolicy.html)
+ [iam:GetPolicyVersion](https://docs.aws.amazon.com/IAM/latest/APIReference/API_GetPolicyVersion.html)
+ [iam:GetRole](https://docs.aws.amazon.com/IAM/latest/APIReference/API_GetRole.html)
+ [iam:GetRolePolicy](https://docs.aws.amazon.com/IAM/latest/APIReference/API_GetRolePolicy.html)
+ [iam:ListAttachedRolePolicies](https://docs.aws.amazon.com/IAM/latest/APIReference/API_ListAttachedRolePolicies.html)
+ [iam:ListRolePolicies](https://docs.aws.amazon.com/IAM/latest/APIReference/API_ListRolePolicies.html)
+ [iam:ListRoles](https://docs.aws.amazon.com/IAM/latest/APIReference/API_ListRoles.html)

通过将 [https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AWSLambda_ReadOnlyAccess.html](https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AWSLambda_ReadOnlyAccess.html) AWS 托管策略添加到 IAM 用户角色，可以授予使用所有这些操作的权限。

要让 Lambda 将您的函数配置写入 Amazon S3，您必须拥有使用以下 API 操作的权限：
+ [S3:PutObject](https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html)
+ [S3:CreateBucket](https://docs.aws.amazon.com/AmazonS3/latest/API/API_CreateBucket.html)
+ [S3:PutBucketEncryption](https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketEncryption.html)
+ [S3:PutBucketLifecycleConfiguration](https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketLifecycleConfiguration.html)

如果无法将函数的配置导出到基础设施编辑器，请检查账户是否具有这些操作所需的权限。如果您拥有所需的权限但仍然无法导出函数配置，请检查[基于资源的策略](access-control-resource-based.md)，因为这些策略可能会限制对 Amazon S3 的访问权限。

## 其他资源
<a name="w2aad101c33b7"></a>

有关如何在基础设施编辑器中基于现有 Lambda 函数设计无服务器应用程序的更详细教程，请参阅[将 Lambda 与基础设施即代码（IaC）结合使用](foundation-iac.md)。

要使用基础设施编辑器以及 AWS SAM 通过 Lambda 设计和部署完整的无服务器应用程序，您也可以按照 [AWS Serverless Patterns 讲习会](https://catalog.workshops.aws/serverless-patterns/en-US)中的 [AWS 基础架构编辑器 教程](https://catalog.workshops.aws/serverless-patterns/en-US/dive-deeper/module1a)进行操作。

# 配合使用 AWS Lambda 和 CloudFormation
<a name="services-cloudformation"></a>

在 AWS CloudFormation 模板中，您可以指定 Lambda 函数作为自定义资源的对象。使用自定义资源来处理参数、检索配置值，或者在堆栈生命周期事件期间调用其他 AWS 服务。

以下示例调用在模板中的其他位置定义的函数。

**Example – 自定义资源定义**  

```
Resources:
  primerinvoke:
    Type: [AWS::CloudFormation::CustomResource](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cfn-customresource.html)
    Version: "1.0"
    Properties:
      ServiceToken: !GetAtt primer.Arn
      FunctionName: !Ref randomerror
```

服务令牌是在您创建、更新或删除堆栈时，CloudFormation 所调用函数的 Amazon 资源名称 (ARN)。您还可以按原样包含 `FunctionName` 传递到您函数的其他属性，例如 CloudFormation。

CloudFormation 通过包含回调 URL 的事件[异步](invocation-async.md)调用您的 Lambda 函数。

**Example – CloudFormation 消息事件**  

```
{
    "RequestType": "Create",
    "ServiceToken": "arn:aws:lambda:us-east-1:123456789012:function:lambda-error-processor-primer-14ROR2T3JKU66",
    "ResponseURL": "https://cloudformation-custom-resource-response-useast1.s3-us-east-1.amazonaws.com/arn%3Aaws%3Acloudformation%3Aus-east-1%3A123456789012%3Astack/lambda-error-processor/1134083a-2608-1e91-9897-022501a2c456%7Cprimerinvoke%7C5d478078-13e9-baf0-464a-7ef285ecc786?AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&Expires=1555451971&Signature=28UijZePE5I4dvukKQqM%2F9Rf1o4%3D",
    "StackId": "arn:aws:cloudformation:us-east-1:123456789012:stack/lambda-error-processor/1134083a-2608-1e91-9897-022501a2c456",
    "RequestId": "5d478078-13e9-baf0-464a-7ef285ecc786",
    "LogicalResourceId": "primerinvoke",
    "ResourceType": "AWS::CloudFormation::CustomResource",
    "ResourceProperties": {
        "ServiceToken": "arn:aws:lambda:us-east-1:123456789012:function:lambda-error-processor-primer-14ROR2T3JKU66",
        "FunctionName": "lambda-error-processor-randomerror-ZWUC391MQAJK"
    }
}
```

函数负责将指示成功还是失败的响应返回到回调 URL。有关完整响应语法，请参阅[自定义资源响应对象](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/crpg-ref-responses.html)。

**Example – CloudFormation 自定义资源响应**  

```
{
    "Status": "SUCCESS",
    "PhysicalResourceId": "2019/04/18/[$LATEST]b3d1bfc65f19ec610654e4d9b9de47a0",
    "StackId": "arn:aws:cloudformation:us-east-1:123456789012:stack/lambda-error-processor/1134083a-2608-1e91-9897-022501a2c456",
    "RequestId": "5d478078-13e9-baf0-464a-7ef285ecc786",
    "LogicalResourceId": "primerinvoke"
}
```

CloudFormation 提供称为 `cfn-response` 的库来处理响应的发送。如果您在模板中定义函数，则可以按名称请求库。随后，CloudFormation 将库添加到为函数创建的部署程序包。

如果自定义资源使用的函数附加了[弹性网络接口](configuration-vpc.md#configuration-vpc-enis)，请将以下资源添加到 VPC 策略，其中 **region** 是函数所在的区域（不带破折号）。例如，`us-east-1` 为 `useast1`。这将允许自定义资源响应将信号发送回 CloudFormation 堆栈的回调 URL。

```
arn:aws:s3:::cloudformation-custom-resource-response-region",
"arn:aws:s3:::cloudformation-custom-resource-response-region/*",
```

以下示例函数调用第二个函数。如果调用成功，则函数发送成功响应到 CloudFormation，并且堆栈更新继续。该模板使用 AWS Serverless Application Model 提供的 [AWS::Serverless::Function](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html) 资源类型。

**Example – 自定义资源函数**  

```
Transform: 'AWS::Serverless-2016-10-31'
Resources:
  primer:
    Type: [AWS::Serverless::Function](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html)
    Properties:
      Handler: index.handler
      Runtime: nodejs16.x
      InlineCode: |
        var aws = require('aws-sdk');
        var response = require('cfn-response');
        exports.handler = function(event, context) {
            // For Delete requests, immediately send a SUCCESS response.
            if (event.RequestType == "Delete") {
                response.send(event, context, "SUCCESS");
                return;
            }
            var responseStatus = "FAILED";
            var responseData = {};
            var functionName = event.ResourceProperties.FunctionName
            var lambda = new aws.Lambda();
            lambda.invoke({ FunctionName: functionName }, function(err, invokeResult) {
                if (err) {
                    responseData = {Error: "Invoke call failed"};
                    console.log(responseData.Error + ":\n", err);
                }
                else responseStatus = "SUCCESS";
                response.send(event, context, responseStatus, responseData);
            });
        };
      Description: Invoke a function to create a log stream.
      MemorySize: 128
      Timeout: 8
      Role: !GetAtt role.Arn
      Tracing: Active
```

如果模板中未定义自定义资源调用的函数，您可以从 AWS CloudFormation 用户指南中的 [cfn-response 模块](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-lambda-function-code-cfnresponsemodule.html)获取 `cfn-response` 的源代码。

有关自定义资源的更多信息，请参阅 *AWS CloudFormation 用户指南*中的[自定义资源](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-custom-resources.html)。

# 使用 Lambda 处理 Amazon DocumentDB 事件
<a name="with-documentdb"></a>

您可以通过将 Amazon DocumentDB 集群配置为事件源，从而使用 Lambda 函数来处理 [Amazon DocumentDB（与 MongoDB 兼容）更改流](https://docs.aws.amazon.com/documentdb/latest/developerguide/change_streams.html)中的事件。然后，您可以在 Amazon DocumentDB 集群每次发生数据更改时调用 Lambda 函数，从而实现事件驱动型工作负载的自动化。

**注意**  
Lambda 仅支持 Amazon DocumentDB 4.0 和 5.0 版本。Lambda 不支持 3.6 版本。  
此外，对于事件源映射，Lambda 仅支持基于实例的集群和区域性集群。Lambda 不支持 [弹性集群](https://docs.aws.amazon.com/documentdb/latest/developerguide/docdb-using-elastic-clusters.html) 或 [全球集群](https://docs.aws.amazon.com/documentdb/latest/developerguide/global-clusters.html)。当使用 Lambda 作为客户端连接到 Amazon DocumentDB 时，此限制不适用。Lambda 可以连接到所有集群类型来执行 CRUD 操作。

Lambda 按照事件到达的顺序依次处理 Amazon DocumentDB 更改流中的事件。因此，您的函数一次只能处理一条来自 Amazon DocumentDB 的并发调用。要监控函数，您可以跟踪其[并发指标](https://docs.aws.amazon.com/lambda/latest/dg/monitoring-concurrency.html)。

**警告**  
Lambda 事件源映射至少处理每个事件一次，有可能出现重复处理记录的情况。为避免与重复事件相关的潜在问题，我们强烈建议您将函数代码设为幂等性。要了解更多信息，请参阅 AWS 知识中心的[如何让我的 Lambda 函数保持幂等性](https://repost.aws/knowledge-center/lambda-function-idempotent)。

**Topics**
+ [

## Amazon DocumentDB 事件示例
](#docdb-sample-event)
+ [

## 先决条件和权限
](#docdb-prereqs)
+ [

## 配置网络安全
](#docdb-network)
+ [

## 创建 Amazon DocumentDB 事件源映射（控制台）
](#docdb-configuration)
+ [

## 创建 Amazon DocumentDB 事件源映射（SDK 或 CLI）
](#docdb-api)
+ [

## 轮询和流的起始位置
](#docdb-stream-polling)
+ [

## 监控 Amazon DocumentDB 事件源
](#docdb-monitoring)
+ [

# 教程：将 AWS Lambda 与 Amazon DocumentDB 流结合使用
](with-documentdb-tutorial.md)

## Amazon DocumentDB 事件示例
<a name="docdb-sample-event"></a>

```
{
    "eventSourceArn": "arn:aws:rds:us-east-1:123456789012:cluster:canaryclusterb2a659a2-qo5tcmqkcl03",
    "events": [
        {
            "event": {
                "_id": {
                    "_data": "0163eeb6e7000000090100000009000041e1"
                },
                "clusterTime": {
                    "$timestamp": {
                        "t": 1676588775,
                        "i": 9
                    }
                },
                "documentKey": {
                    "_id": {
                        "$oid": "63eeb6e7d418cd98afb1c1d7"
                    }
                },
                "fullDocument": {
                    "_id": {
                        "$oid": "63eeb6e7d418cd98afb1c1d7"
                    },
                    "anyField": "sampleValue"
                },
                "ns": {
                    "db": "test_database",
                    "coll": "test_collection"
                },
                "operationType": "insert"
            }
        }
    ],
    "eventSource": "aws:docdb"
}
```

有关此示例中的事件及其形状的更多信息，请参阅 MongoDB 文档网站上的[更改事件](https://www.mongodb.com/docs/manual/reference/change-events/)。

## 先决条件和权限
<a name="docdb-prereqs"></a>

将 Amazon DocumentDB 用作 Lambda 函数的事件源之前，请注意以下先决条件。您必须：
+ **有一个现有的 Amazon DocumentDB 集群并且该集群必须与函数位于同一 AWS 账户和 AWS 区域中。**如果您没有现有的集群，可以按照《Amazon DocumentDB 开发者**指南》中的 [Amazon DocumentDB 入门](https://docs.aws.amazon.com/documentdb/latest/developerguide/get-started-guide.html)创建一个。此外，[教程：将 AWS Lambda 与 Amazon DocumentDB 流结合使用](with-documentdb-tutorial.md) 中的第一组步骤将指导您创建满足所有必要先决条件的 Amazon DocumentDB 集群。
+ **允许 Lambda 访问与 Amazon DocumentDB 集群关联的 Amazon Virtual Private Cloud（Amazon VPC）资源的权限。**有关更多信息，请参阅 [配置网络安全](#docdb-network)。
+ **在 Amazon DocumentDB 集群上启用 TLS。**这是默认设置。如果您禁用了 TLS，Lambda 将无法与您的集群通信。
+ **在 Amazon DocumentDB 集群上激活更改流。**有关更多信息，请参阅《Amazon DocumentDB 开发人员指南**》中的[将更改流与 Amazon DocumentDB 搭配使用](https://docs.aws.amazon.com/documentdb/latest/developerguide/change_streams.html)。
+ **为 Lambda 提供访问 Amazon DocumentDB 集群的凭证。**在设置事件源时，请提供包含访问集群所需的身份验证详细信息（用户名和密码）的 [AWS Secrets Manager](https://docs.aws.amazon.com/secretsmanager/latest/userguide/intro.html) 密钥。要在设置过程中提供此密钥，请执行以下任一操作：
  + 如果您使用 Lambda 控制台进行设置，请在 **Secrets Manager 密钥**字段中提供密钥。
  + 如果您使用 AWS Command Line Interface（AWS CLI）进行设置，请在 `source-access-configurations` 选项中提供此密钥。您可以将此选项包含在 [https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/create-event-source-mapping.html](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/create-event-source-mapping.html) 命令或 [https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/update-event-source-mapping.html](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/update-event-source-mapping.html) 命令中。例如：

    ```
    aws lambda create-event-source-mapping \
        ...
        --source-access-configurations  '[{"Type":"BASIC_AUTH","URI":"arn:aws:secretsmanager:us-west-2:123456789012:secret:DocDBSecret-AbC4E6"}]' \
        ...
    ```
+ **向 Lambda 授予管理与 Amazon DocumentDB 流相关的资源的权限。**将以下角色权限手动添加到您的函数的 [执行角色](lambda-intro-execution-role.md)：
  + [rds:DescribeDBClusters](https://docs.aws.amazon.com/AmazonRDS/latest/APIReference/API_DescribeDBClusters.html)
  + [rds:DescribeDBClusterParameters](https://docs.aws.amazon.com/AmazonRDS/latest/APIReference/API_DescribeDBClusterParameters.html)
  + [rds:DescribeDBSubnetGroups](https://docs.aws.amazon.com/AmazonRDS/latest/APIReference/API_DescribeDBSubnetGroups.html)
  + [ec2:CreateNetworkInterface](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_CreateNetworkInterface.html)
  + [ec2:DescribeNetworkInterfaces](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeNetworkInterfaces.html)
  + [ec2:DescribeVpcs](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeVpcs.html)
  + [ec2:DeleteNetworkInterface](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DeleteNetworkInterface.html)
  + [ec2:DescribeSubnets](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeSubnets.html)
  + [ec2:DescribeSecurityGroups](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeSecurityGroups.html)
  + [kms:Decrypt](https://docs.aws.amazon.com/kms/latest/APIReference/API_Decrypt.html)
  + [secretsmanager:GetSecretValue](https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_GetSecretValue.html)
+ **将您发送到 Lambda 的 Amazon DocumentDB 更改流事件的大小保持在 6MB 以下。**Lambda 支持最大 6MB 的负载大小。如果更改流尝试向 Lambda 发送大于 6MB 的事件，Lambda 会丢弃该消息并发送 `OversizedRecordCount` 指标。Lambda 将尽力发送所有指标。

**注意**  
尽管 Lambda 函数的最大超时限制通常为 15 分钟，但 Amazon MSK、自行管理的 Apache Kafka、Amazon DocumentDB、Amazon MQ for ActiveMQ 和 RabbitMQ 的事件源映射，仅支持最大超时限制为 14 分钟的函数。此约束可确保事件源映射可以正确处理函数错误和重试。

## 配置网络安全
<a name="docdb-network"></a>

要通过事件源映射向 Lambda 提供对 Amazon DocumentDB 的完全访问权限，集群必须使用公有端点（公有 IP 地址），或者您必须提供对您在其中创建了集群的 Amazon VPC 的访问权限。

将 Amazon DocumentDB 与 Lambda 结合使用时，创建 [AWS PrivateLink VPC 端点](https://docs.aws.amazon.com/vpc/latest/privatelink/create-interface-endpoint.html)，以便您的函数可以访问 Amazon VPC 中的资源。

**注意**  
对于使用事件轮询器的默认（按需）模式的事件源映射函数，需要 AWS PrivateLink VPC 端点。如果事件源映射使用[预调配模式](invocation-eventsourcemapping.md#invocation-eventsourcemapping-provisioned-mode)，则无需配置 AWS PrivateLink VPC 端点。

创建端点以提供对以下资源的访问权限：
+  Lambda：为 Lambda 服务主体创建端点。
+  AWS STS：为 AWS STS 创建端点，以便服务主体代您代入角色。
+  Secrets Manager：如果集群使用 Secrets Manager 来存储凭证，请为 Secrets Manager 创建端点。

也可以在 Amazon VPC 中的每个公有子网上配置 NAT 网关。有关更多信息，请参阅 [为连接到 VPC 的 Lambda 函数启用互联网访问权限](configuration-vpc-internet.md)。

为 Amazon DocumentDB 创建事件源映射时，Lambda 会检查为 Amazon VPC 配置的子网和安全组是否已经存在弹性网络接口（ENI）。如果 Lambda 发现现有 ENI，则会尝试重用这些 ENI。否则，Lambda 会创建新的 ENI 来连接到事件源并调用函数。

**注意**  
Lambda 函数始终在 Lambda 服务拥有的 Amazon VPC 中运行。函数的 VPC 配置不会影响事件源映射。只有事件源的网络配置才能决定 Lambda 连接到事件源的方式。

为包含集群的 Amazon VPC 配置安全组。默认情况下，Amazon DocumentDB 使用以下端口：`27017`。
+ 入站规则：允许与事件源关联安全组的默认代理端口的所有流量。或者，您可以使用自引用安全组规则允许来自同一安全组内的实例进行访问。
+ 出站规则 – 如果您的函数需要与 AWS 服务进行通信，则允许端口 `443` 上的所有流量向外部目标传输。或者，如果您不需要与其他 AWS 服务通信，也可以使用自引用的安全组规则来限制对代理的访问权限。
+ Amazon VPC 端点入站规则：如果您正在使用 Amazon VPC 端点，则与 Amazon VPC 端点关联的安全组，必须允许来自集群安全组的端口 `443` 上的入站流量。

如果集群使用身份验证，您还可以限制 Secrets Manager 端点的端点策略。要调用 Secrets Manager API，Lambda 会使用函数角色而非 Lambda 服务主体。

**Example VPC 端点策略：Secrets Manager 端点**  

```
{
      "Statement": [
          {
              "Action": "secretsmanager:GetSecretValue",
              "Effect": "Allow",
              "Principal": {
                  "AWS": [
                      "arn:aws::iam::123456789012:role/my-role"
                  ]
              },
              "Resource": "arn:aws::secretsmanager:us-west-2:123456789012:secret:my-secret"
          }
      ]
  }
```

当您使用 Amazon VPC 端点时，AWS 会使用端点的弹性网络接口（ENI）路由 API 调用来调用函数。Lambda 服务主体需要针对使用这些 ENI 的任何角色和函数调用 `lambda:InvokeFunction`。

默认情况下，Amazon VPC 端点具有开放的 IAM 策略，允许对资源进行广泛访问。最佳实践是，将这些策略限制为使用该端点执行所需的操作。为确保事件源映射能够调用 Lambda 函数，VPC 端点策略必须允许 Lambda 服务主体调用 `sts:AssumeRole` 和 `lambda:InvokeFunction`。将 VPC 端点策略限制为仅允许来自组织内部的 API 调用，会导致事件源映射无法正常运行，因此这些策略中需要 `"Resource": "*"`。

以下 VPC 端点策略示例展示了如何向 AWS STS 的 Lambda 服务主体和 Lambda 端点授予所需的访问权限。

**Example VPC 端点策略 – AWS STS 端点**  

```
{
      "Statement": [
          {
              "Action": "sts:AssumeRole",
              "Effect": "Allow",
              "Principal": {
                  "Service": [
                      "lambda.amazonaws.com"
                  ]
              },
              "Resource": "*"
          }
      ]
    }
```

**Example VPC 端点策略 – Lambda 端点**  

```
{
      "Statement": [
          {
              "Action": "lambda:InvokeFunction",
              "Effect": "Allow",
              "Principal": {
                  "Service": [
                      "lambda.amazonaws.com"
                  ]
              },
              "Resource": "*"
          }
      ]
  }
```

## 创建 Amazon DocumentDB 事件源映射（控制台）
<a name="docdb-configuration"></a>

要配置 Lambda 函数以从 Amazon DocumentDB 集群更改流中读取数据，请创建一个 [事件源映射](invocation-eventsourcemapping.md)。这一部分介绍了如何从 Lambda 控制台执行此操作。有关 AWS SDK 和 AWS CLI 说明，请参阅 [创建 Amazon DocumentDB 事件源映射（SDK 或 CLI）](#docdb-api)。

**要创建 Amazon DocumentDB 事件源映射（控制台）**

1. 打开 Lamba 控制台的[函数](https://console.aws.amazon.com/lambda/home#/functions)页面。

1. 选择一个函数的名称。

1. 在 **Function overview**（函数概览）下，选择 **Add trigger**（添加触发器）。

1. 在**触发器配置**下的下拉列表中，选择 **DocumentDB**。

1. 配置必填选项，然后选择 **Add**（添加）。

Lambda 支持 Amazon DocumentDB 事件源的以下选项：
+ **DocumentDB 集群** – 选择一个 Amazon DocumentDB 集群。
+ **激活触发器** – 选择是否要立即激活触发器。如果选中此复选框，则在创建事件源映射后，您的函数会立即开始接收来自指定 Amazon DocumentDB 更改流的流量。我们建议取消选中此复选框，以便在禁用状态下创建事件源映射以进行测试。创建事件源映射后，您可以随时将其激活。
+ **数据库名称** – 输入集群中要使用的数据库的名称。
+ （可选）**集合名称** – 输入数据库中要使用的集合的名称。如果您未指定集合，Lambda 将侦听来自数据库中每个集合的所有事件。
+ **批处理大小** – 设置要在单个批处理中检索的最大消息数，最高为 1 万。默认批处理大小为 100。
+ **开始位置** – 选择将从流中开始读取记录的位置。
  + **最新** – 仅处理添加到流中的新记录。您的函数仅在 Lambda 完成创建事件源后才会开始处理记录。这意味着在成功创建事件源之前，某些记录可能会被丢弃。
  + **最早**：处理流中的所有记录。Lambda 使用集群的日志保留期来确定从哪里开始读取事件。具体而言，Lambda 将从 `current_time - log_retention_duration` 开始读取事件。更改流必须在此时间戳之前已经处于活动状态，才能确保 Lambda 正确读取所有事件。
  + **在时间戳处**：处理从特定时间开始的记录。更改流必须在指定的时间戳之前已经处于活动状态，才能确保 Lambda 正确读取所有事件。
+ **身份验证** – 选择访问集群中的代理时将使用的身份验证方法。
  + **BASIC\$1AUTH** – 使用基本身份验证时，您必须提供包含访问集群凭据所需凭证的 Secrets Manager 密钥。
+ **Secrets Manager 密钥** – 选择包含访问 Amazon DocumentDB 集群所需身份验证详细信息（用户名和密码）的 Secrets Manager 密钥。
+ （可选）**批处理时长** – 在调用函数之前收集记录的最长时间（以秒为单位，最大为 300）。
+ （可选）**完整文档配置** – 对于文档更新操作，请选择要发送到流中的内容。默认值为 `Default`，这意味着对于每个更改流事件，Amazon DocumentDB 仅发送一个描述所发生更改的增量。有关此字段的更多信息，请参阅 MongoDB Javadocs API 文档中的 [FullDocument](https://mongodb.github.io/mongo-java-driver/3.9/javadoc/com/mongodb/client/model/changestream/FullDocument.html#DEFAULT)。
  + **默认** – Lambda 将仅发送描述所发生更改的部分文档。
  + **UpdateLookup** – Lambda 将发送一个描述所发生更改的增量以及完整文档的副本。

## 创建 Amazon DocumentDB 事件源映射（SDK 或 CLI）
<a name="docdb-api"></a>

要使用 [AWS SDK](https://aws.amazon.com/developer/tools/) 来管理 Amazon DocumentDB 事件源映射，您可以使用以下 API 操作：
+ [CreateEventSourceMapping](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html)
+ [ListEventSourceMappings](https://docs.aws.amazon.com/lambda/latest/api/API_ListEventSourceMappings.html)
+ [GetEventSourceMapping](https://docs.aws.amazon.com/lambda/latest/api/API_GetEventSourceMapping.html)
+ [UpdateEventSourceMapping](https://docs.aws.amazon.com/lambda/latest/api/API_UpdateEventSourceMapping.html)
+ [DeleteEventSourceMapping](https://docs.aws.amazon.com/lambda/latest/api/API_DeleteEventSourceMapping.html)

要使用 AWS CLI 创建事件源映射，请使用 [https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/create-event-source-mapping.html](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/create-event-source-mapping.html) 命令。以下示例使用此命令将名为 `my-function` 的函数映射到一个 Amazon DocumentDB 更改流。事件源由 Amazon 资源名称（ARN）指定，批处理大小为 500，从以 Unix 时间表示的时间戳开始。该命令还指定了 Lambda 用来连接到 Amazon DocumentDB 的 Secrets Manager 密钥。此外，它还包含用于指定要从中读取数据的数据库和集合的 `document-db-event-source-config` 参数。

```
aws lambda create-event-source-mapping --function-name my-function \
    --event-source-arn arn:aws:rds:us-west-2:123456789012:cluster:privatecluster7de2-epzcyvu4pjoy
    --batch-size 500 \
    --starting-position AT_TIMESTAMP \
    --starting-position-timestamp 1541139109 \
    --source-access-configurations '[{"Type":"BASIC_AUTH","URI":"arn:aws:secretsmanager:us-east-1:123456789012:secret:DocDBSecret-BAtjxi"}]' \
    --document-db-event-source-config '{"DatabaseName":"test_database", "CollectionName": "test_collection"}' \
```

应看到类似如下内容的输出：

```
{
    "UUID": "2b733gdc-8ac3-cdf5-af3a-1827b3b11284",
    "BatchSize": 500,
    "DocumentDBEventSourceConfig": {
        "CollectionName": "test_collection",
        "DatabaseName": "test_database",
        "FullDocument": "Default"
    },
    "MaximumBatchingWindowInSeconds": 0,
    "EventSourceArn": "arn:aws:rds:us-west-2:123456789012:cluster:privatecluster7de2-epzcyvu4pjoy",
    "FunctionArn": "arn:aws:lambda:us-west-2:123456789012:function:my-function",
    "LastModified": 1541348195.412,
    "LastProcessingResult": "No records processed",
    "State": "Creating",
    "StateTransitionReason": "User action"
}
```

创建后，您可以使用 [https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/update-event-source-mapping.html](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/update-event-source-mapping.html) 命令更新 Amazon DocumentDB 事件源的设置。以下示例会将批处理大小更新为 1000，并将批处理时长更新为 10 秒。对于此命令，您需要有事件源映射的 UUID，您可以通过 `list-event-source-mapping` 命令或 Lambda 控制台检索该值。

```
aws lambda update-event-source-mapping --function-name my-function \
    --uuid f89f8514-cdd9-4602-9e1f-01a5b77d449b \
    --batch-size 1000 \
    --batch-window 10
```

您应看到如下输出：

```
{
    "UUID": "2b733gdc-8ac3-cdf5-af3a-1827b3b11284",
    "BatchSize": 500,
    "DocumentDBEventSourceConfig": {
        "CollectionName": "test_collection",
        "DatabaseName": "test_database",
        "FullDocument": "Default"
    },
    "MaximumBatchingWindowInSeconds": 0,
    "EventSourceArn": "arn:aws:rds:us-west-2:123456789012:cluster:privatecluster7de2-epzcyvu4pjoy",
    "FunctionArn": "arn:aws:lambda:us-west-2:123456789012:function:my-function",
    "LastModified": 1541359182.919,
    "LastProcessingResult": "OK",
    "State": "Updating",
    "StateTransitionReason": "User action"
}
```

Lambda 会异步更新设置，因此在更新完成之前，您可能无法在输出中看到这些更改。要查看事件源映射的当前设置，请使用 [https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/get-event-source-mapping.html](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/get-event-source-mapping.html) 命令。

```
aws lambda get-event-source-mapping --uuid f89f8514-cdd9-4602-9e1f-01a5b77d449b
```

您应看到如下输出：

```
{
    "UUID": "2b733gdc-8ac3-cdf5-af3a-1827b3b11284",
    "DocumentDBEventSourceConfig": {
        "CollectionName": "test_collection",
        "DatabaseName": "test_database",
        "FullDocument": "Default"
    },
    "BatchSize": 1000,
    "MaximumBatchingWindowInSeconds": 10,
    "EventSourceArn": "arn:aws:rds:us-west-2:123456789012:cluster:privatecluster7de2-epzcyvu4pjoy",
    "FunctionArn": "arn:aws:lambda:us-west-2:123456789012:function:my-function",
    "LastModified": 1541359182.919,
    "LastProcessingResult": "OK",
    "State": "Enabled",
    "StateTransitionReason": "User action"
}
```

要删除 Amazon DocumentDB 事件源映射，请使用 [https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/delete-event-source-mapping.html](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/delete-event-source-mapping.html) 命令：

```
aws lambda delete-event-source-mapping \
    --uuid 2b733gdc-8ac3-cdf5-af3a-1827b3b11284
```

## 轮询和流的起始位置
<a name="docdb-stream-polling"></a>

请注意，事件源映射创建和更新期间的流轮询最终是一致的。
+ 在事件源映射创建期间，可能需要几分钟才能开始轮询来自流的事件。
+ 在事件源映射更新期间，可能需要几分钟才能停止和重新开始轮询来自流的事件。

此行为意味着，如果你指定 `LATEST` 作为流的起始位置，事件源映射可能会在创建或更新期间错过事件。为确保不会错过任何事件，请将流的起始位置指定为 `TRIM_HORIZON` 或 `AT_TIMESTAMP`。

## 监控 Amazon DocumentDB 事件源
<a name="docdb-monitoring"></a>

为便于您监控 Amazon DocumentDB 事件源，在函数处理完一批记录后，Lambda 将发送 `IteratorAge` 指标。*迭代器期限*是最近事件的时间戳和当前时间戳之间的差值。基本上，`IteratorAge` 指标表示自处理该批处理中最后一条记录后已经过的时间。如果函数当前正在处理新事件，则可使用迭代器期限来估算新记录的添加时间与函数处理该记录的时间之间的延迟。如果 `IteratorAge` 呈上升趋势，则可能说明您的函数存在问题。有关更多信息，请参阅 [将 CloudWatch 指标与 Lambda 结合使用](monitoring-metrics.md)。

Amazon DocumentDB 更改流未经过优化，无法处理事件之间的较大时间间隔。如果 Amazon DocumentDB 事件源在很长一段时间内都没有收到任何事件，那么 Lambda 可能会禁用事件源映射。此时段的长度可能从几周到几个月不等，具体取决于集群规模和其他工作负载。

Lambda 支持最大 6MB 的负载。但是，Amazon DocumentDB 更改流事件的大小可达 16MB。如果更改流尝试向 Lambda 发送大于 6MB 的更改流事件，Lambda 会丢弃该消息并发送 `OversizedRecordCount` 指标。Lambda 将尽力发送所有指标。

# 教程：将 AWS Lambda 与 Amazon DocumentDB 流结合使用
<a name="with-documentdb-tutorial"></a>

 在本教程中，您将创建一个基本的 Lambda 函数，该函数会消耗来自 Amazon DocumentDB（与 MongoDB 兼容）更改流的事件。要完成本教程，您需要经历以下阶段：
+ 设置 Amazon DocumentDB 集群，连接到该集群，并在其上激活更改流。
+ 创建 Lambda 函数，并将 Amazon DocumentDB 集群配置为函数的事件源。
+ 通过将项目插入到 Amazon DocumentDB 数据库中来测试设置。

## 创建 Amazon DocumentDB 集群
<a name="docdb-documentdb-cluster"></a>

1. 打开 [Amazon DocumentDB 控制台](https://console.aws.amazon.com/docdb/home#)。在**集群**下，选择**创建**。

1. 使用以下配置创建集群：
   + 对于**集群类型**，选择**基于实例的集群**。这是默认选项。
   + 在**集群配置**下，确保已选择**引擎版本** 5.0.0。这是默认选项。
   + 在**实例配置**下：
     + 对于**数据库实例类**，选择**内存优化类**。这是默认选项。
     + 对于**常规副本实例数**，请选择 1。
     + 对于**实例类**，使用默认选择。
   + 在**身份验证**下，输入主要用户的用户名，然后选择**自行管理**。输入密码，然后确认密码。
   + 保留所有其他默认设置。

1. 选择**创建集群**。

## 在 Secrets Manager 中创建密钥
<a name="docdb-secret-in-secrets-manager"></a>

在 Amazon DocumentDB 创建您的集群时，请创建一个 AWS Secrets Manager 密钥来存储数据库凭证。在稍后的步骤中创建 Lambda 事件源映射时，您将提供此密钥。

**在 Secrets Manager 中创建密钥**

1. 打开 [Secrets Manager](https://console.aws.amazon.com/secretsmanager/home#) 控制台并选择**存储新密钥**。

1. 对于**选择密钥类型**，请选择以下选项之一：
   + 在**基本详细信息**下：
     + **密钥类型**：用于 Amazon DocumentDB 数据库的凭证
     + 在**凭证**下，输入用于创建 Amazon DocumentDB 集群的相同用户名和密码。
     + **数据库**：选择 Amazon DocumentDB 集群。
     + 选择**下一步**。

1. 对于**配置密钥**，请选择以下选项之一：
   + **密钥名称**：`DocumentDBSecret`
   + 选择**下一步**。

1. 选择**下一步**。

1. 选择**存储**。

1. 刷新控制台以验证 `DocumentDBSecret` 密钥是否成功存储。

记下**密钥 ARN**。您将在后面的步骤中用到它。

## 连接到集群
<a name="docdb-connect-to-cluster"></a>

**使用 AWS CloudShell 连接到 Amazon DocumentDB 集群**

1. 在 Amazon DocumentDB 管理控制台上的**集群**下，找到您创建的集群。单击集群旁边的复选框，选择您的集群。

1. 选择**连接到集群**。将出现 CloudShell **运行命令**屏幕。

1. 在**新环境名称**字段中，输入唯一的名称，例如“test”，然后选择**创建并运行**。

1. 出现提示时请输入密码。当提示符变成 `rs0 [direct: primary] <env-name>>` 时，您成功连接到您的 Amazon DocumentDB 集群。

## 激活更改流
<a name="docdb-activate-change-streams"></a>

在本教程中，您将跟踪对 Amazon DocumentDB 集群中 `docdbdemo` 数据库 `products` 集合的更改。您可以通过激活[更改流](https://docs.aws.amazon.com/documentdb/latest/developerguide/change_streams.html)来完成此操作。

**在集群内创建新数据库**

1. 运行以下命令，创建名为 `docdbdemo` 的新数据库：

   ```
   use docdbdemo
   ```

1. 在终端窗口中，使用以下命令将记录插入到 `docdbdemo` 中：

   ```
   db.products.insertOne({"hello":"world"})
   ```

   您应看到类似如下的输出：

   ```
   {
     acknowledged: true,
     insertedId: ObjectId('67f85066ca526410fd531d59')
   }
   ```

1. 接下来，使用以下命令激活 `docdbdemo` 数据库 `products` 集合上的更改流：

   ```
   db.adminCommand({modifyChangeStreams: 1,
       database: "docdbdemo",
       collection: "products", 
       enable: true});
   ```

    应看到类似如下内容的输出：

   ```
   { "ok" : 1, "operationTime" : Timestamp(1680126165, 1) }
   ```

## 创建接口 VPC 端点
<a name="docdb-create-interface-vpc-endpoints"></a>

接下来，创建[接口 VPC 端点](https://docs.aws.amazon.com/vpc/latest/privatelink/create-interface-endpoint.html#create-interface-endpoint-aws)，以确保 Lambda 和 Secrets Manager（稍后用于存储集群访问凭证）能够连接到默认 VPC。

**创建接口 VPC 端点**

1. 打开 [VPC 控制台](https://console.aws.amazon.com/vpc/home#)。在左侧菜单的**虚拟私有云**下，选择**端点**。

1. 选择**创建端点**。使用以下配置创建端点：
   + 对于**名称标签**，输入 `lambda-default-vpc`。
   + 对于**服务类别**，选择 AWS 服务。
   + 对于**服务**，在搜索框中输入 `lambda`。选择格式为 `com.amazonaws.<region>.lambda` 的服务。
   + 对于 **VPC**，选择您的 Amazon DocumentDB 集群所在的 VPC。这通常是[默认 VPC](https://docs.aws.amazon.com/vpc/latest/userguide/default-vpc.html)。
   + 对于**子网**，选中每个可用区旁边的复选框。为每个可用区选择正确的子网 ID。
   + 对于 **IP 地址类型**，选择 IPv4。
   + 对于**安全组**，选择 Amazon DocumentDB 集群使用的安全组。这通常是 `default` 安全组。
   + 保留所有其他默认设置。
   + 选择**创建端点**。

1. 再次选择**创建端点**。使用以下配置创建端点：
   + 对于**名称标签**，输入 `secretsmanager-default-vpc`。
   + 对于**服务类别**，选择 AWS 服务。
   + 对于**服务**，在搜索框中输入 `secretsmanager`。选择格式为 `com.amazonaws.<region>.secretsmanager` 的服务。
   + 对于 **VPC**，选择您的 Amazon DocumentDB 集群所在的 VPC。这通常是[默认 VPC](https://docs.aws.amazon.com/vpc/latest/userguide/default-vpc.html)。
   + 对于**子网**，选中每个可用区旁边的复选框。为每个可用区选择正确的子网 ID。
   + 对于 **IP 地址类型**，选择 IPv4。
   + 对于**安全组**，选择 Amazon DocumentDB 集群使用的安全组。这通常是 `default` 安全组。
   + 保留所有其他默认设置。
   + 选择**创建端点**。

 本教程的集群设置部分到此完成。

## 创建执行角色
<a name="docdb-create-the-execution-role"></a>

 在接下来的一组步骤中，您将创建 Lambda 函数。首先，您需要创建执行角色，以向函数授予访问集群的权限。为此，您可以先创建 IAM policy，然后将此策略附加到 IAM 角色。

**创建 IAM policy**

1. 在 IAM 控制台中打开[策略页面](https://console.aws.amazon.com/iam/home#/policies)，然后选择**创建策略**。

1. 选择 **JSON** 选项卡。在以下策略中，将语句最后一行中的 Secrets Manager 资源 ARN 替换为之前的密钥 ARN，然后将策略复制到编辑器中。

------
#### [ JSON ]

****  

   ```
   {
       "Version":"2012-10-17",		 	 	 
       "Statement": [
           {
               "Sid": "LambdaESMNetworkingAccess",
               "Effect": "Allow",
               "Action": [
                   "ec2:CreateNetworkInterface",
                   "ec2:DescribeNetworkInterfaces",
                   "ec2:DescribeVpcs",
                   "ec2:DeleteNetworkInterface",
                   "ec2:DescribeSubnets",
                   "ec2:DescribeSecurityGroups",
                   "kms:Decrypt"
               ],
               "Resource": "*"
           },
           {
               "Sid": "LambdaDocDBESMAccess",
               "Effect": "Allow",
               "Action": [
                   "rds:DescribeDBClusters",
                   "rds:DescribeDBClusterParameters",
                   "rds:DescribeDBSubnetGroups"
               ],
               "Resource": "*"
           },
           {
               "Sid": "LambdaDocDBESMGetSecretValueAccess",
               "Effect": "Allow",
               "Action": [
                   "secretsmanager:GetSecretValue"
               ],
               "Resource": "arn:aws:secretsmanager:us-east-1:123456789012:secret:DocumentDBSecret"
           }
       ]
   }
   ```

------

1. 选择**下一步：标签**，然后选择**下一步：审核**。

1. 对于 **Name (名称)**，请输入 `AWSDocumentDBLambdaPolicy`。

1. 选择**创建策略**。

**创建 IAM 角色**

1. 在 IAM 控制台中打开[角色页面](https://console.aws.amazon.com/iam/home#/roles)，然后选择**创建角色**。

1. 对于**选择可信实体**，请选择以下选项之一：
   + **可信实体类型**：AWS 服务
   + **服务或使用案例**：Lambda
   + 选择**下一步**。

1. 对于**添加权限**，选择刚刚创建的 `AWSDocumentDBLambdaPolicy` 策略以及 `AWSLambdaBasicExecutionRole`，以向函数授予写入 Amazon CloudWatch Logs 的权限。

1. 选择**下一步**。

1. 对于 **Role name（角色名称）**，输入 `AWSDocumentDBLambdaExecutionRole`。

1. 请选择**创建角色**。

## 创建 Lambda 函数
<a name="docdb-create-the-lambda-function"></a>

本教程使用 Python 3.14 运行时系统，但我们还提供了适用于其他运行时系统的示例代码文件。您可以选择以下框中的选项卡，查看适用于您感兴趣的运行时系统的代码。

代码接收 Amazon DocumentDB 事件输入并对其所包含的消息进行处理。

**创建 Lambda 函数**

1. 打开 Lamba 控制台的 [Functions page](https://console.aws.amazon.com/lambda/home#/functions)（函数页面）。

1. 选择 **Create function**（创建函数）。

1. 选择**从头开始编写**。

1. 在**基本信息**中，执行以下操作：

   1. 对于**函数名称**，输入 `ProcessDocumentDBRecords`。

   1. 对于**运行时系统**，选择 **Python 3.14**。

   1. 对于**架构**，选择 **x86\$164**。

1. 在**更改默认执行角色**选项卡中，执行以下操作：

   1. 展开选项卡，然后选择**使用现有角色**。

   1. 选择您之前创建的 `AWSDocumentDBLambdaExecutionRole`。

1. 选择**创建函数**。

**要部署函数代码**

1. 在下框中选择 **Python** 选项卡并复制代码。

------
#### [ .NET ]

**适用于 .NET 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-docdb-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
使用 .NET 将 Amazon DocumentDB 事件与 Lambda 结合使用。  

   ```
   using Amazon.Lambda.Core;
   using System.Text.Json;
   using System;
   using System.Collections.Generic;
   using System.Text.Json.Serialization;
   //Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
   [assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]
   
   namespace LambdaDocDb;
   
   public class Function
   {
       
        /// <summary>
       /// Lambda function entry point to process Amazon DocumentDB events.
       /// </summary>
       /// <param name="event">The Amazon DocumentDB event.</param>
       /// <param name="context">The Lambda context object.</param>
       /// <returns>A string to indicate successful processing.</returns>
       public string FunctionHandler(Event evnt, ILambdaContext context)
       {
           
           foreach (var record in evnt.Events)
           {
               ProcessDocumentDBEvent(record, context);
           }
   
           return "OK";
       }
   
        private void ProcessDocumentDBEvent(DocumentDBEventRecord record, ILambdaContext context)
       {
           
           var eventData = record.Event;
           var operationType = eventData.OperationType;
           var databaseName = eventData.Ns.Db;
           var collectionName = eventData.Ns.Coll;
           var fullDocument = JsonSerializer.Serialize(eventData.FullDocument, new JsonSerializerOptions { WriteIndented = true });
   
           context.Logger.LogLine($"Operation type: {operationType}");
           context.Logger.LogLine($"Database: {databaseName}");
           context.Logger.LogLine($"Collection: {collectionName}");
           context.Logger.LogLine($"Full document:\n{fullDocument}");
       }
   
   
   
       public class Event
       {
           [JsonPropertyName("eventSourceArn")]
           public string EventSourceArn { get; set; }
   
           [JsonPropertyName("events")]
           public List<DocumentDBEventRecord> Events { get; set; }
   
           [JsonPropertyName("eventSource")]
           public string EventSource { get; set; }
       }
   
       public class DocumentDBEventRecord
       {
           [JsonPropertyName("event")]
           public EventData Event { get; set; }
       }
   
       public class EventData
       {
           [JsonPropertyName("_id")]
           public IdData Id { get; set; }
   
           [JsonPropertyName("clusterTime")]
           public ClusterTime ClusterTime { get; set; }
   
           [JsonPropertyName("documentKey")]
           public DocumentKey DocumentKey { get; set; }
   
           [JsonPropertyName("fullDocument")]
           public Dictionary<string, object> FullDocument { get; set; }
   
           [JsonPropertyName("ns")]
           public Namespace Ns { get; set; }
   
           [JsonPropertyName("operationType")]
           public string OperationType { get; set; }
       }
   
       public class IdData
       {
           [JsonPropertyName("_data")]
           public string Data { get; set; }
       }
   
       public class ClusterTime
       {
           [JsonPropertyName("$timestamp")]
           public Timestamp Timestamp { get; set; }
       }
   
       public class Timestamp
       {
           [JsonPropertyName("t")]
           public long T { get; set; }
   
           [JsonPropertyName("i")]
           public int I { get; set; }
       }
   
       public class DocumentKey
       {
           [JsonPropertyName("_id")]
           public Id Id { get; set; }
       }
   
       public class Id
       {
           [JsonPropertyName("$oid")]
           public string Oid { get; set; }
       }
   
       public class Namespace
       {
           [JsonPropertyName("db")]
           public string Db { get; set; }
   
           [JsonPropertyName("coll")]
           public string Coll { get; set; }
       }
   }
   ```

------
#### [ Go ]

**适用于 Go 的 SDK V2**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-docdb-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
使用 Go 将 Amazon DocumentDB 事件与 Lambda 结合使用。  

   ```
   package main
   
   import (
   	"context"
   	"encoding/json"
   	"fmt"
   
   	"github.com/aws/aws-lambda-go/lambda"
   )
   
   type Event struct {
   	Events []Record `json:"events"`
   }
   
   type Record struct {
   	Event struct {
   		OperationType string `json:"operationType"`
   		NS            struct {
   			DB   string `json:"db"`
   			Coll string `json:"coll"`
   		} `json:"ns"`
   		FullDocument interface{} `json:"fullDocument"`
   	} `json:"event"`
   }
   
   func main() {
   	lambda.Start(handler)
   }
   
   func handler(ctx context.Context, event Event) (string, error) {
   	fmt.Println("Loading function")
   	for _, record := range event.Events {
   		logDocumentDBEvent(record)
   	}
   
   	return "OK", nil
   }
   
   func logDocumentDBEvent(record Record) {
   	fmt.Printf("Operation type: %s\n", record.Event.OperationType)
   	fmt.Printf("db: %s\n", record.Event.NS.DB)
   	fmt.Printf("collection: %s\n", record.Event.NS.Coll)
   	docBytes, _ := json.MarshalIndent(record.Event.FullDocument, "", "  ")
   	fmt.Printf("Full document: %s\n", string(docBytes))
   }
   ```

------
#### [ Java ]

**适用于 Java 的 SDK 2.x**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-docdb-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
使用 Java 将 Amazon DocumentDB 事件与 Lambda 结合使用。  

   ```
   import java.util.List;
   import java.util.Map;
   
   import com.amazonaws.services.lambda.runtime.Context;
   import com.amazonaws.services.lambda.runtime.RequestHandler;
   
   public class Example implements RequestHandler<Map<String, Object>, String> {
   
       @SuppressWarnings("unchecked")
       @Override
       public String handleRequest(Map<String, Object> event, Context context) {
           List<Map<String, Object>> events = (List<Map<String, Object>>) event.get("events");
           for (Map<String, Object> record : events) {
               Map<String, Object> eventData = (Map<String, Object>) record.get("event");
               processEventData(eventData);
           }
   
           return "OK";
       }
   
       @SuppressWarnings("unchecked")
       private void processEventData(Map<String, Object> eventData) {
           String operationType = (String) eventData.get("operationType");
           System.out.println("operationType: %s".formatted(operationType));
   
           Map<String, Object> ns = (Map<String, Object>) eventData.get("ns");
   
           String db = (String) ns.get("db");
           System.out.println("db: %s".formatted(db));
           String coll = (String) ns.get("coll");
           System.out.println("coll: %s".formatted(coll));
   
           Map<String, Object> fullDocument = (Map<String, Object>) eventData.get("fullDocument");
           System.out.println("fullDocument: %s".formatted(fullDocument));
       }
   
   }
   ```

------
#### [ JavaScript ]

**SDK for JavaScript (v3)**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-docdb-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
使用 JavaScript 将 Amazon DocumentDB 事件与 Lambda 结合使用。  

   ```
   console.log('Loading function');
   exports.handler = async (event, context) => {
       event.events.forEach(record => {
           logDocumentDBEvent(record);
       });
       return 'OK';
   };
   
   const logDocumentDBEvent = (record) => {
       console.log('Operation type: ' + record.event.operationType);
       console.log('db: ' + record.event.ns.db);
       console.log('collection: ' + record.event.ns.coll);
       console.log('Full document:', JSON.stringify(record.event.fullDocument, null, 2));
   };
   ```
使用 TypeScript 将 Amazon DocumentDB 事件与 Lambda 结合使用  

   ```
   import { DocumentDBEventRecord, DocumentDBEventSubscriptionContext } from 'aws-lambda';
   
   console.log('Loading function');
   
   export const handler = async (
     event: DocumentDBEventSubscriptionContext,
     context: any
   ): Promise<string> => {
     event.events.forEach((record: DocumentDBEventRecord) => {
       logDocumentDBEvent(record);
     });
     return 'OK';
   };
   
   const logDocumentDBEvent = (record: DocumentDBEventRecord): void => {
     console.log('Operation type: ' + record.event.operationType);
     console.log('db: ' + record.event.ns.db);
     console.log('collection: ' + record.event.ns.coll);
     console.log('Full document:', JSON.stringify(record.event.fullDocument, null, 2));
   };
   ```

------
#### [ PHP ]

**适用于 PHP 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-docdb-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
使用 PHP 将 Amazon DocumentDB 事件与 Lambda 结合使用。  

   ```
   <?php
   
   require __DIR__.'/vendor/autoload.php';
   
   use Bref\Context\Context;
   use Bref\Event\Handler;
   
   class DocumentDBEventHandler implements Handler
   {
       public function handle($event, Context $context): string
       {
   
           $events = $event['events'] ?? [];
           foreach ($events as $record) {
               $this->logDocumentDBEvent($record['event']);
           }
           return 'OK';
       }
   
       private function logDocumentDBEvent($event): void
       {
           // Extract information from the event record
   
           $operationType = $event['operationType'] ?? 'Unknown';
           $db = $event['ns']['db'] ?? 'Unknown';
           $collection = $event['ns']['coll'] ?? 'Unknown';
           $fullDocument = $event['fullDocument'] ?? [];
   
           // Log the event details
   
           echo "Operation type: $operationType\n";
           echo "Database: $db\n";
           echo "Collection: $collection\n";
           echo "Full document: " . json_encode($fullDocument, JSON_PRETTY_PRINT) . "\n";
       }
   }
   return new DocumentDBEventHandler();
   ```

------
#### [ Python ]

**适用于 Python 的 SDK（Boto3）**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-docdb-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
使用 Python 将 Amazon DocumentDB 事件与 Lambda 结合使用。  

   ```
   import json
   
   def lambda_handler(event, context):
       for record in event.get('events', []):
           log_document_db_event(record)
       return 'OK'
   
   def log_document_db_event(record):
       event_data = record.get('event', {})
       operation_type = event_data.get('operationType', 'Unknown')
       db = event_data.get('ns', {}).get('db', 'Unknown')
       collection = event_data.get('ns', {}).get('coll', 'Unknown')
       full_document = event_data.get('fullDocument', {})
   
       print(f"Operation type: {operation_type}")
       print(f"db: {db}")
       print(f"collection: {collection}")
       print("Full document:", json.dumps(full_document, indent=2))
   ```

------
#### [ Ruby ]

**适用于 Ruby 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-docdb-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
使用 Ruby 将 Amazon DocumentDB 事件与 Lambda 结合使用。  

   ```
   require 'json'
   
   def lambda_handler(event:, context:)
     event['events'].each do |record|
       log_document_db_event(record)
     end
     'OK'
   end
   
   def log_document_db_event(record)
     event_data = record['event'] || {}
     operation_type = event_data['operationType'] || 'Unknown'
     db = event_data.dig('ns', 'db') || 'Unknown'
     collection = event_data.dig('ns', 'coll') || 'Unknown'
     full_document = event_data['fullDocument'] || {}
   
     puts "Operation type: #{operation_type}"
     puts "db: #{db}"
     puts "collection: #{collection}"
     puts "Full document: #{JSON.pretty_generate(full_document)}"
   end
   ```

------
#### [ Rust ]

**适用于 Rust 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-docdb-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
使用 Rust 将 Amazon DocumentDB 事件与 Lambda 结合使用。  

   ```
   use lambda_runtime::{service_fn, tracing, Error, LambdaEvent};
   use aws_lambda_events::{
       event::documentdb::{DocumentDbEvent, DocumentDbInnerEvent},
      };
   
   
   // Built with the following dependencies:
   //lambda_runtime = "0.11.1"
   //serde_json = "1.0"
   //tokio = { version = "1", features = ["macros"] }
   //tracing = { version = "0.1", features = ["log"] }
   //tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt"] }
   //aws_lambda_events = "0.15.0"
   
   async fn function_handler(event: LambdaEvent<DocumentDbEvent>) ->Result<(), Error> {
       
       tracing::info!("Event Source ARN: {:?}", event.payload.event_source_arn);
       tracing::info!("Event Source: {:?}", event.payload.event_source);
     
       let records = &event.payload.events;
      
       if records.is_empty() {
           tracing::info!("No records found. Exiting.");
           return Ok(());
       }
   
       for record in records{
           log_document_db_event(record);
       }
   
       tracing::info!("Document db records processed");
   
       // Prepare the response
       Ok(())
   
   }
   
   fn log_document_db_event(record: &DocumentDbInnerEvent)-> Result<(), Error>{
       tracing::info!("Change Event: {:?}", record.event);
       
       Ok(())
   
   }
   
   #[tokio::main]
   async fn main() -> Result<(), Error> {
       tracing_subscriber::fmt()
       .with_max_level(tracing::Level::INFO)
       .with_target(false)
       .without_time()
       .init();
   
       let func = service_fn(function_handler);
       lambda_runtime::run(func).await?;
       Ok(())
       
   }
   ```

------

1. 在 Lambda 控制台的**代码源**窗格中，将代码粘贴到代码编辑器中，替换 Lambda 创建的代码。

1. 在**部署**部分，选择**部署**以更新函数的代码：  
![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/getting-started-tutorial/deploy-console.png)

## 创建 Lambda 事件源映射
<a name="docdb-create-the-lambda-event-source-mapping"></a>

 创建事件源映射，将 Amazon DocumentDB 更改流与 Lambda 函数相关联。创建此事件源映射后，AWS Lambda 即开始轮询该流。

**创建事件源映射**

1. 在 Lambda 控制台中打开[函数页面](https://console.aws.amazon.com/lambda/home#/functions)。

1. 选择您之前创建的 `ProcessDocumentDBRecords` 函数。

1. 选择**配置**选项卡，然后从左侧菜单中选择**触发器**。

1. 选择**添加触发器**。

1. 在**触发器配置**下，为源选择 **Amazon DocumentDB**。

1. 使用以下配置创建事件源映射：
   + **Amazon DocumentDB 集群**：选择之前创建的集群。
   + **数据库名称**：docdbdemo
   + **集合名称**：产品
   + **批处理大小**：1
   + **起始位置**：最新
   + **身份验证**：BASIC\$1AUTH
   + **Secrets Manager 密钥**：选择 Amazon DocumentDB 集群的密钥。其名称类似于 `rds!cluster-12345678-a6f0-52c0-b290-db4aga89274f`。
   + **批处理时段**：1
   + **完整文档配置**：UpdateLookup

1. 选择**添加**。创建事件源映射可能需要花费几分钟的时间。

## 测试函数
<a name="docdb-test-insert"></a>

等待事件源映射达到**已启用**状态。这个过程可能需要几分钟。然后，通过插入、更新和删除数据库记录来测试端到端设置。开始前的准备工作：

1. 在 CloudShell 环境中[重新连接到 Amazon DocumentDB 集群](#docdb-connect-to-cluster)。

1. 运行以下命令以确保使用的是 `docdbdemo` 数据库：

   ```
   use docdbdemo
   ```

### 插入记录
<a name="docdb-test-insert"></a>

在 `docdbdemo` 数据库的 `products` 集合中插入记录：

```
db.products.insertOne({"name":"Pencil", "price": 1.00})
```

通过[查看 CloudWatch Logs](monitoring-cloudwatchlogs-view.md#monitoring-cloudwatchlogs-console) 来验证函数是否成功处理该事件。您会看到如下日志条目：

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/documentdb-insert-log.png)


### 更新记录
<a name="docdb-test-update"></a>

使用以下命令更新您刚刚插入的记录：

```
db.products.updateOne(
    { "name": "Pencil" },
    { $set: { "price": 0.50 }}
)
```

通过[查看 CloudWatch Logs](monitoring-cloudwatchlogs-view.md#monitoring-cloudwatchlogs-console) 来验证函数是否成功处理该事件。您会看到如下日志条目：

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/documentdb-update-log.png)


### 删除记录
<a name="docdb-test-delete"></a>

使用以下命令删除您刚刚更新的记录：

```
db.products.deleteOne( { "name": "Pencil" } )
```

通过[查看 CloudWatch Logs](monitoring-cloudwatchlogs-view.md#monitoring-cloudwatchlogs-console) 来验证函数是否成功处理该事件。您会看到如下日志条目：

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/documentdb-delete-log.png)


## 问题排查
<a name="docdb-lambda-troubleshooting"></a>

如果您在函数的 CloudWatch 日志中没有看到任何数据库事件，请检查以下各项：
+ 确保 Lambda 事件源映射（也称为触发器）处于**已启用**状态。创建事件源映射可能需要几分钟时间。
+ 如果事件源映射**已启用**，但您仍然无法在 CloudWatch 中看到数据库事件：
  + 确保事件源映射中的**数据库名称**设置为 `docdbdemo`。  
![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/documentdb-trigger.png)
  + 检查事件源映射**上次处理结果**字段中是否显示以下消息：“问题：连接错误。您的 VPC 必须能够连接到 Lambda 和 STS，以及 Secrets Manager（如果需要身份验证）。” 如果看到此错误，请确保您[已创建 Lambda 和 Secrets Manager VPC 接口端点](#docdb-create-interface-vpc-endpoints)，并且这些端点使用与 Amazon DocumentDB 集群相同的 VPC 和子网。  
![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/documentdb-lastprocessingresult.png)

## 清除资源
<a name="docdb-cleanup"></a>

 除非您想要保留为本教程创建的资源，否则可立即将其删除。通过删除您不再使用的 AWS 资源，可防止您的 AWS 账户 产生不必要的费用。

**删除 Lambda 函数**

1. 打开 Lamba 控制台的 [Functions（函数）页面](https://console.aws.amazon.com/lambda/home#/functions)。

1. 选择您创建的函数。

1. 依次选择**操作**和**删除**。

1. 在文本输入字段中键入 **confirm**，然后选择**删除**。

**删除执行角色**

1. 打开 IAM 控制台的[角色页面](https://console.aws.amazon.com/iam/home#/roles)。

1. 选择您创建的执行角色。

1. 选择**删除**。

1. 在文本输入字段中输入角色名称，然后选择**删除**。

**删除 VPC 端点**

1. 打开 [VPC 控制台](https://console.aws.amazon.com/vpc/home#)。在左侧菜单的**虚拟私有云**下，选择**端点**。

1. 选择您创建的端点。

1. 选择**操作**、**删除 VPC 端点**。

1. 在文本输入字段中输入 **delete**。

1. 选择**删除**。

**删除 Amazon DocumentDB 集群**

1. 打开 [Amazon DocumentDB 控制台](https://console.aws.amazon.com/docdb/home#)。

1. 选择您为本教程创建的 Amazon DocumentDB 集群，并禁用删除保护。

1. 在主**集群**页面中，再次选择 Amazon DocumentDB 集群。

1. 依次选择**操作**和**删除**。

1. 对于**创建最终集群快照**，选择**否**。

1. 在文本输入字段中输入 **delete**。

1. 选择**删除**。

**在 Secrets Manager 中删除密钥**

1. 打开 [Secrets Manager 控制台](https://console.aws.amazon.com/secretsmanager/home#)。

1. 选择您为本教程创建的密钥。

1. 依次选择**操作**、**删除密钥**。

1. 选择**计划删除**。

# 将 AWS Lambda 与 Amazon DynamoDB 结合使用
<a name="with-ddb"></a>

**注意**  
如果想要将数据发送到 Lambda 函数以外的目标，或要在发送数据之前丰富数据，请参阅 [Amazon EventBridge Pipes](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-pipes.html)（Amazon EventBridge 管道）。

您可以使用 AWS Lambda 函数来处理 [Amazon DynamoDB Streams](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Streams.html)中的记录。使用 DynamoDB Streams，每次更新 DynamoDB 表时，您都可以触发 Lambda 函数以执行其他工作。

在处理 DynamoDB 流时，您需要实施部分批处理响应逻辑，以防止在批处理中的某些记录失败时重试成功处理的记录。Powertools for AWS Lambda 的[批处理器实用程序](https://docs.powertools.aws.dev/lambda/python/latest/utilities/batch/)适用于 Python、TypeScript、.NET 和 Java，并通过自动处理部分批处理响应逻辑简化了此实现，从而减少了开发时间并提高了可靠性。

**Topics**
+ [

## 轮询和批处理流
](#dynamodb-polling-and-batching)
+ [

## 轮询和流的起始位置
](#dyanmo-db-stream-poll)
+ [

## DynamoDB Streams 中的分片同时读取器
](#events-dynamodb-simultaneous-readers)
+ [

## 事件示例
](#events-sample-dynamodb)
+ [

# 使用 Lambda 处理 DynamoDB 记录
](services-dynamodb-eventsourcemapping.md)
+ [

# 使用 DynamoDB 和 Lambda 配置部分批处理响应
](services-ddb-batchfailurereporting.md)
+ [

# 在 Lambda 中保留 DynamoDB 事件源的丢弃记录
](services-dynamodb-errors.md)
+ [

# 在 Lambda 中实现有状态的 DynamoDB 流处理
](services-ddb-windows.md)
+ [

# Amazon DynamoDB 事件源映射的 Lambda 参数
](services-ddb-params.md)
+ [

# 对 DynamoDB 事件源使用事件筛选
](with-ddb-filtering.md)
+ [

# 教程：将 AWS Lambda 与 Amazon DynamoDB Streams 结合使用
](with-ddb-example.md)

## 轮询和批处理流
<a name="dynamodb-polling-and-batching"></a>

Lambda 以每秒 4 次的基本频率轮询 DynamoDB 流中的分区来获取记录。如果记录可用，Lambda 会调用函数并等待结果。如果处理成功，Lambda 会恢复轮询，直到其收到更多记录。

默认情况下，Lambda 会在记录可用时尽快调用您的函数。如果 Lambda 从事件源中读取的批处理只有一条记录，则 Lambda 将会只向该函数发送一条记录。为避免在记录数量较少的情况下调用该函数，您可以配置 *batching window*（批处理时段），让事件源缓冲最多五分钟的记录。调用函数前，Lambda 会持续从事件源中读取记录，直到收集完整批处理、批处理时段到期或批处理达到 6MB 的有效负载时为止。有关更多信息，请参阅 [批处理行为](invocation-eventsourcemapping.md#invocation-eventsourcemapping-batching)。

**警告**  
Lambda 事件源映射至少处理每个事件一次，有可能出现重复处理记录的情况。为避免与重复事件相关的潜在问题，我们强烈建议您将函数代码设为幂等性。要了解更多信息，请参阅 AWS 知识中心的[如何让我的 Lambda 函数保持幂等性](https://repost.aws/knowledge-center/lambda-function-idempotent)。

Lambda 在发送下次批处理之前不会等待任何配置的[扩展](lambda-extensions.md)完成。换句话说，扩展可能会在 Lambda 处理下一批记录时继续运行。如果您违反了账户的任何[并发](lambda-concurrency.md)设置或限制，可能会导致节流问题。要检测这是否是潜在问题，请监控函数并检查所显示的[并发指标](monitoring-concurrency.md#general-concurrency-metrics)是否高于事件源映射的预期。由于调用间隔时间较短，Lambda 可能会短暂报告高于分片数量的并发使用量。即使对于没有扩展名的 Lambda 函数也是如此。

配置 [ParallelizationFactor](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html#lambda-CreateEventSourceMapping-request-ParallelizationFactor) 设置以同时使用多个 Lambda 调用处理 DynamoDB 流的一个分片。您可以指定 Lambda 通过从 1（默认值）到 10 的并行化因子从分区中轮询的并发批次数。例如，假设您将 `ParallelizationFactor` 设置为 2，则最多可以有 200 个并发 Lambda 调用来处理 100 个 DynamoDB 流分片（但您可能实际上会看到不同的 `ConcurrentExecutions` 指标值）。这有助于在数据量不稳定并且 [IteratorAge](monitoring-metrics-types.md#performance-metrics) 较高时纵向扩展处理吞吐量。增加每个分片的并发批次数后，Lambda 仍然可以确保项目（分区和排序键）级别的顺序处理。

## 轮询和流的起始位置
<a name="dyanmo-db-stream-poll"></a>

请注意，事件源映射创建和更新期间的流轮询最终是一致的。
+ 在事件源映射创建期间，可能需要几分钟才能开始轮询来自流的事件。
+ 在事件源映射更新期间，可能需要几分钟才能停止和重新开始轮询来自流的事件。

此行为意味着，如果你指定 `LATEST` 作为流的起始位置，事件源映射可能会在创建或更新期间错过事件。为确保不会错过任何事件，请将流的起始位置指定为 `TRIM_HORIZON`。

## DynamoDB Streams 中的分片同时读取器
<a name="events-dynamodb-simultaneous-readers"></a>

对于作为非全局表的单区域表，您可以设计最多两个 Lambda 函数来同时从同一个 DynamoDB Streams 分片读取数据。超过此限制会导致请求被拒。对于全局表，我们建议您将并行函数的数量限制为一个，以避免请求节流。

## 事件示例
<a name="events-sample-dynamodb"></a>

**Example**  

```
{
  "Records": [
    {
      "eventID": "1",
      "eventVersion": "1.0",
      "dynamodb": {
        "Keys": {
          "Id": {
            "N": "101"
          }
        },
        "NewImage": {
          "Message": {
            "S": "New item!"
          },
          "Id": {
            "N": "101"
          }
        },
        "StreamViewType": "NEW_AND_OLD_IMAGES",
        "SequenceNumber": "111",
        "SizeBytes": 26
      },
      "awsRegion": "us-west-2",
      "eventName": "INSERT",
      "eventSourceARN": "arn:aws:dynamodb:us-east-2:123456789012:table/my-table/stream/2024-06-10T19:26:16.525",
      "eventSource": "aws:dynamodb"
    },
    {
      "eventID": "2",
      "eventVersion": "1.0",
      "dynamodb": {
        "OldImage": {
          "Message": {
            "S": "New item!"
          },
          "Id": {
            "N": "101"
          }
        },
        "SequenceNumber": "222",
        "Keys": {
          "Id": {
            "N": "101"
          }
        },
        "SizeBytes": 59,
        "NewImage": {
          "Message": {
            "S": "This item has changed"
          },
          "Id": {
            "N": "101"
          }
        },
        "StreamViewType": "NEW_AND_OLD_IMAGES"
      },
      "awsRegion": "us-west-2",
      "eventName": "MODIFY",
      "eventSourceARN": "arn:aws:dynamodb:us-east-2:123456789012:table/my-table/stream/2024-06-10T19:26:16.525",
      "eventSource": "aws:dynamodb"
    }
  ]}
```

# 使用 Lambda 处理 DynamoDB 记录
<a name="services-dynamodb-eventsourcemapping"></a>

创建事件源映射以指示 Lambda 将流中的记录发送到 Lambda 函数。您可以创建多个事件源映射，以使用多个 Lambda 函数处理相同的数据，或使用单个函数处理来自多个流的项目。

您可以配置事件源映射来处理来自不同 AWS 账户中的流的记录。要了解更多信息，请参阅[创建跨账户事件源映射](#services-dynamodb-eventsourcemapping-cross-account)。

要将函数配置为从 DynamoDB Streams 中读取，请将 [AWSLambdaDynamoDBExecutionRole](https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AWSLambdaDynamoDBExecutionRole.html) AWS 托管策略附加到执行角色，然后创建 **DynamoDB** 触发器。

**要添加权限并创建触发器**

1. 打开 Lamba 控制台的[函数](https://console.aws.amazon.com/lambda/home#/functions)页面。

1. 选择一个函数的名称。

1. 选择 **Configuration**（配置）选项卡，然后选择 **Permissions**（权限）。

1. 在**角色名称**下，选择至执行角色的链接。此角色将在 IAM 控制台中打开角色。  
![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/execution-role.png)

1. 选择**添加权限**，然后选择**附加策略**。  
![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/attach-policies.png)

1. 在搜索字段中输入 `AWSLambdaDynamoDBExecutionRole`。向执行角色添加此策略。这是一项 AWS 托管策略，其中包含您的函数从 DynamoDB 流中读取所需的权限。有关此策略的更多信息，请参阅《AWS 托管式策略参考》**中的 [AWSLambdaDynamoDBExecutionRole](https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AWSLambdaDynamoDBExecutionRole.html)。

1. 在 Lambda 控制台中返回您的函数。在 **Function overview**（函数概览）下，选择 **Add trigger**（添加触发器）。  
![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/add-trigger.png)

1. 选择触发器类型。

1. 配置必填选项，然后选择 **Add**（添加）。

Lambda 支持 DynamoDB 事件源的以下选项：

**事件源选项**
+ **DynamoDB table**（DynamoDB 表）– 要从中读取记录的 DynamoDB 表。
+ **Batch size**（批处理大小）– 每个批次中要发送到函数的记录数，最高为 10000。Lambda 通过单个调用将批处理中的所有记录传递给函数，前提是事件的总大小未超出同步调用的[有效载荷限制](gettingstarted-limits.md)（6 MB）。
+ **Batch window**（批处理时段）– 指定在调用函数之前收集记录的最长时间（以秒为单位）。
+ **Starting position**（开始位置）– 仅处理新记录或所有现有记录。
  + **Latest**（最新）– 处理已添加到流中的新记录。
  + **Trim horizon**（时间范围）– 处理流中的所有记录。

  在处理任何现有记录后，函数将继续处理新记录。
+ **失败时的目标** – 无法处理的记录的标准 SQS 队列或标准 SNS 主题。当 Lambda 因为某批记录太旧或已用尽所有重试而将其丢弃时，Lambda 会将有关该批处理的详细信息发送到该队列或主题。
+ **Retry attempts**（重试次数）– 函数返回错误时 Lambda 重试的最大次数。这不适用于批处理未到达函数的服务错误或限制。
+ **Maximum age of record**（记录的最长时限）– Lambda 发送到您的函数的记录的最长期限。
+ **Split batch on error**（出现错误时拆分批）– 当函数返回错误时，在重试之前将批次拆分为两批。原始批量大小设置会保持不变。
+ **Concurrent batches per shard**（每个分片的并发批处理数）– 同时处理来自同一个分片的多个批处理。
+ **Enabled**（已启用）– 设置为 true 可启用事件源映射。设置为 false 可停止处理记录。Lambda 会跟踪已处理的最后一条记录，并在重新启用映射后从停止位置重新开始处理。

**注意**  
对于 Lambda 作为 DynamoDB 触发器的一部分而调用的 GetRecords API 调用，您无需付费。

之后，要管理事件源配置，请在设计器中选择触发器。

## 创建跨账户事件源映射
<a name="services-dynamodb-eventsourcemapping-cross-account"></a>

Amazon DynamoDB 现在支持[基于资源的策略](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/access-control-resource-based.html)。利用此功能，您可以使用另一个账户中的 Lambda 函数处理来自一个 AWS 账户的 DynamoDB 流中的数据。

要使用其他 AWS 账户中的 DynamoDB 流为您的 Lambda 函数创建事件源映射，您必须使用基于资源的策略配置该流，以向您的 Lambda 函数授予读取相关记录的权限。要了解如何配置流以实现跨账户访问，请参阅《Amazon DynamoDB 开发人员指南》**中的[与跨账户 Lambda 函数共享访问权限](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/rbac-cross-account-access.html#rbac-analyze-cross-account-lambda-access)。

使用基于资源的策略配置流以向 Lambda 函数授予所需权限后，请使用跨账户流 ARN 创建事件源映射。您可以在跨账户 DynamoDB 控制台中表的**导出和流**选项卡下找到流 ARN。

使用 Lambda 控制台时，请将流 ARN 直接粘贴到事件源映射创建页面的 DynamoDB 表输入字段中。

 **注意：**不支持跨区域触发器。

# 使用 DynamoDB 和 Lambda 配置部分批处理响应
<a name="services-ddb-batchfailurereporting"></a>

在使用和处理来自事件源的流式数据时，默认情况下，Lambda 仅在批处理完全成功时，才会在批次的最高序列号处设置检查点。Lambda 会将所有其他结果视为完全失败并重试批处理，直至达到重试次数上限。要允许在处理来自流的批次时部分成功，请开启 `ReportBatchItemFailures`。允许部分成功有助于减少对记录重试的次数，尽管这并不能完全阻止在成功记录中重试的可能性。

要开启 `ReportBatchItemFailures`，请在 [FunctionResponseTypes](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html#lambda-CreateEventSourceMapping-request-FunctionResponseTypes) 列表中包含枚举值 **ReportBatchItemFailures**。此列表指示为函数启用了哪些响应类型。您可以在[创建](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html)或[更新](https://docs.aws.amazon.com/lambda/latest/api/API_UpdateEventSourceMapping.html)事件源映射时配置此列表。

**注意**  
即使函数代码返回部分批处理失败响应，除非为事件源映射显式启用 `ReportBatchItemFailures` 功能，否则 Lambda 也不会处理这些响应。

## 报告语法
<a name="streams-batchfailurereporting-syntax"></a>

配置批处理项目失败的报告时，将返回 `StreamsEventResponse` 类，其中包含批处理项目失败列表。您可以使用 `StreamsEventResponse` 对象返回批处理中第一个失败记录的序列号。您还可以使用正确的响应语法来创建自己的自定义类。以下 JSON 结构显示了所需的响应语法：

```
{ 
  "batchItemFailures": [ 
        {
            "itemIdentifier": "<SequenceNumber>"
        }
    ]
}
```

**注意**  
如果 `batchItemFailures` 数组包含多个项目，Lambda 会使用序列号最小的记录作为检查点。然后，Lambda 会重试从该检查点开始的所有记录。

## 成功和失败的条件
<a name="streams-batchfailurereporting-conditions"></a>

如果返回以下任意一项，则 Lambda 会将批处理视为完全成功：
+ 空的 `batchItemFailure` 列表
+ Null `batchItemFailure` 列表
+ 空的 `EventResponse`
+ Null `EventResponse`

如果返回以下任何一项，则 Lambda 会将批处理视为完全失败：
+ 空字符串 `itemIdentifier`
+ Null `itemIdentifier`
+ 包含错误密钥名的 `itemIdentifier`

Lambda 会根据您的重试策略在失败时重试。

## 将批次一分为二
<a name="streams-batchfailurereporting-bisect"></a>

如果调用失败并且已开启 `BisectBatchOnFunctionError`，则无论您的 `ReportBatchItemFailures` 设置如何，批次都将一分为二。

当收到批处理部分成功响应且同时开启 `BisectBatchOnFunctionError` 和 `ReportBatchItemFailures` 时，批次将在返回的序列号处一分为二，并且 Lambda 将仅重试剩余记录。

为了简化部分批处理响应逻辑的实现，请考虑使用 Powertools for AWS Lambda 中的[批处理器实用程序](https://docs.powertools.aws.dev/lambda/python/latest/utilities/batch/)，它可以自动为您处理这些复杂问题。

以下函数代码示例将返回批处理中处理失败消息的 ID 列表：

------
#### [ .NET ]

**适用于 .NET 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-ddb-to-lambda-with-batch-item-handling)存储库中查找完整示例，并了解如何进行设置和运行。
报告使用 .NET 通过 Lambda 进行 DynamoDB 批处理项目失败。  

```
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
using System.Text.Json;
using System.Text;
using Amazon.Lambda.Core;
using Amazon.Lambda.DynamoDBEvents;

// Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]

namespace AWSLambda_DDB;

public class Function
{
    public StreamsEventResponse FunctionHandler(DynamoDBEvent dynamoEvent, ILambdaContext context)

    {
        context.Logger.LogInformation($"Beginning to process {dynamoEvent.Records.Count} records...");
        List<StreamsEventResponse.BatchItemFailure> batchItemFailures = new List<StreamsEventResponse.BatchItemFailure>();
        StreamsEventResponse streamsEventResponse = new StreamsEventResponse();

        foreach (var record in dynamoEvent.Records)
        {
            try
            {
                var sequenceNumber = record.Dynamodb.SequenceNumber;
                context.Logger.LogInformation(sequenceNumber);
            }
            catch (Exception ex)
            {
                context.Logger.LogError(ex.Message);
                batchItemFailures.Add(new StreamsEventResponse.BatchItemFailure() { ItemIdentifier = record.Dynamodb.SequenceNumber });
            }
        }

        if (batchItemFailures.Count > 0)
        {
            streamsEventResponse.BatchItemFailures = batchItemFailures;
        }

        context.Logger.LogInformation("Stream processing complete.");
        return streamsEventResponse;
    }
}
```

------
#### [ Go ]

**适用于 Go 的 SDK V2**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-ddb-to-lambda-with-batch-item-handling)存储库中查找完整示例，并了解如何进行设置和运行。
报告使用 Go 通过 Lambda 进行 DynamoDB 批处理项目失败。  

```
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package main

import (
	"context"
	"github.com/aws/aws-lambda-go/events"
	"github.com/aws/aws-lambda-go/lambda"
)

type BatchItemFailure struct {
	ItemIdentifier string `json:"ItemIdentifier"`
}

type BatchResult struct {
	BatchItemFailures []BatchItemFailure `json:"BatchItemFailures"`
}

func HandleRequest(ctx context.Context, event events.DynamoDBEvent) (*BatchResult, error) {
	var batchItemFailures []BatchItemFailure
	curRecordSequenceNumber := ""

	for _, record := range event.Records {
		// Process your record
		curRecordSequenceNumber = record.Change.SequenceNumber
	}

	if curRecordSequenceNumber != "" {
		batchItemFailures = append(batchItemFailures, BatchItemFailure{ItemIdentifier: curRecordSequenceNumber})
	}
	
	batchResult := BatchResult{
		BatchItemFailures: batchItemFailures,
	}

	return &batchResult, nil
}

func main() {
	lambda.Start(HandleRequest)
}
```

------
#### [ Java ]

**适用于 Java 的 SDK 2.x**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-ddb-to-lambda-with-batch-item-handling)存储库中查找完整示例，并了解如何进行设置和运行。
报告使用 Java 通过 Lambda 进行 DynamoDB 批处理项目失败。  

```
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.DynamodbEvent;
import com.amazonaws.services.lambda.runtime.events.StreamsEventResponse;
import com.amazonaws.services.lambda.runtime.events.models.dynamodb.StreamRecord;

import java.util.ArrayList;
import java.util.List;

public class ProcessDynamodbRecords implements RequestHandler<DynamodbEvent, StreamsEventResponse> {

    @Override
    public StreamsEventResponse handleRequest(DynamodbEvent input, Context context) {

        List<StreamsEventResponse.BatchItemFailure> batchItemFailures = new ArrayList<>();
        String curRecordSequenceNumber = "";

        for (DynamodbEvent.DynamodbStreamRecord dynamodbStreamRecord : input.getRecords()) {
          try {
                //Process your record
                StreamRecord dynamodbRecord = dynamodbStreamRecord.getDynamodb();
                curRecordSequenceNumber = dynamodbRecord.getSequenceNumber();
                
            } catch (Exception e) {
                /* Since we are working with streams, we can return the failed item immediately.
                   Lambda will immediately begin to retry processing from this failed item onwards. */
                batchItemFailures.add(new StreamsEventResponse.BatchItemFailure(curRecordSequenceNumber));
                return new StreamsEventResponse(batchItemFailures);
            }
        }
       
       return new StreamsEventResponse();   
    }
}
```

------
#### [ JavaScript ]

**SDK for JavaScript（v3）**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-ddb-to-lambda-with-batch-item-handling)存储库中查找完整示例，并了解如何进行设置和运行。
使用 JavaScript 报告 Lambda 的 DynamoDB 批处理项目失败。  

```
export const handler = async (event) => {
  const records = event.Records;
  let curRecordSequenceNumber = "";

  for (const record of records) {
    try {
      // Process your record
      curRecordSequenceNumber = record.dynamodb.SequenceNumber;
    } catch (e) {
      // Return failed record's sequence number
      return { batchItemFailures: [{ itemIdentifier: curRecordSequenceNumber }] };
    }
  }

  return { batchItemFailures: [] };
};
```
使用 TypeScript 报告 Lambda 的 DynamoDB 批处理项目失败。  

```
import {
  DynamoDBBatchResponse,
  DynamoDBBatchItemFailure,
  DynamoDBStreamEvent,
} from "aws-lambda";

export const handler = async (
  event: DynamoDBStreamEvent
): Promise<DynamoDBBatchResponse> => {
  const batchItemFailures: DynamoDBBatchItemFailure[] = [];
  let curRecordSequenceNumber;

  for (const record of event.Records) {
    curRecordSequenceNumber = record.dynamodb?.SequenceNumber;

    if (curRecordSequenceNumber) {
      batchItemFailures.push({
        itemIdentifier: curRecordSequenceNumber,
      });
    }
  }

  return { batchItemFailures: batchItemFailures };
};
```

------
#### [ PHP ]

**适用于 PHP 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-ddb-to-lambda-with-batch-item-handling)存储库中查找完整示例，并了解如何进行设置和运行。
报告使用 PHP 通过 Lambda 进行 DynamoDB 批处理项目失败。  

```
<?php

# using bref/bref and bref/logger for simplicity

use Bref\Context\Context;
use Bref\Event\DynamoDb\DynamoDbEvent;
use Bref\Event\Handler as StdHandler;
use Bref\Logger\StderrLogger;

require __DIR__ . '/vendor/autoload.php';

class Handler implements StdHandler
{
    private StderrLogger $logger;
    public function __construct(StderrLogger $logger)
    {
        $this->logger = $logger;
    }

    /**
     * @throws JsonException
     * @throws \Bref\Event\InvalidLambdaEvent
     */
    public function handle(mixed $event, Context $context): array
    {
        $dynamoDbEvent = new DynamoDbEvent($event);
        $this->logger->info("Processing records");

        $records = $dynamoDbEvent->getRecords();
        $failedRecords = [];
        foreach ($records as $record) {
            try {
                $data = $record->getData();
                $this->logger->info(json_encode($data));
                // TODO: Do interesting work based on the new data
            } catch (Exception $e) {
                $this->logger->error($e->getMessage());
                // failed processing the record
                $failedRecords[] = $record->getSequenceNumber();
            }
        }
        $totalRecords = count($records);
        $this->logger->info("Successfully processed $totalRecords records");

        // change format for the response
        $failures = array_map(
            fn(string $sequenceNumber) => ['itemIdentifier' => $sequenceNumber],
            $failedRecords
        );

        return [
            'batchItemFailures' => $failures
        ];
    }
}

$logger = new StderrLogger();
return new Handler($logger);
```

------
#### [ Python ]

**适用于 Python 的 SDK（Boto3）**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-ddb-to-lambda-with-batch-item-handling)存储库中查找完整示例，并了解如何进行设置和运行。
报告使用 Python 通过 Lambda 进行 DynamoDB 批处理项目失败。  

```
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
def handler(event, context):
    records = event.get("Records")
    curRecordSequenceNumber = ""
    
    for record in records:
        try:
            # Process your record
            curRecordSequenceNumber = record["dynamodb"]["SequenceNumber"]
        except Exception as e:
            # Return failed record's sequence number
            return {"batchItemFailures":[{"itemIdentifier": curRecordSequenceNumber}]}

    return {"batchItemFailures":[]}
```

------
#### [ Ruby ]

**适用于 Ruby 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-ddb-to-lambda-with-batch-item-handling)存储库中查找完整示例，并了解如何进行设置和运行。
报告使用 Ruby 通过 Lambda 进行 DynamoDB 批处理项目失败。  

```
def lambda_handler(event:, context:)
    records = event["Records"]
    cur_record_sequence_number = ""
  
    records.each do |record|
      begin
        # Process your record
        cur_record_sequence_number = record["dynamodb"]["SequenceNumber"]
      rescue StandardError => e
        # Return failed record's sequence number
        return {"batchItemFailures" => [{"itemIdentifier" => cur_record_sequence_number}]}
      end
    end
  
    {"batchItemFailures" => []}
  end
```

------
#### [ Rust ]

**适用于 Rust 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-ddb-to-lambda-with-batch-item-handling)存储库中查找完整示例，并了解如何进行设置和运行。
报告使用 Rust 通过 Lambda 进行 DynamoDB 批处理项目失败。  

```
use aws_lambda_events::{
    event::dynamodb::{Event, EventRecord, StreamRecord},
    streams::{DynamoDbBatchItemFailure, DynamoDbEventResponse},
};
use lambda_runtime::{run, service_fn, Error, LambdaEvent};

/// Process the stream record
fn process_record(record: &EventRecord) -> Result<(), Error> {
    let stream_record: &StreamRecord = &record.change;

    // process your stream record here...
    tracing::info!("Data: {:?}", stream_record);

    Ok(())
}

/// Main Lambda handler here...
async fn function_handler(event: LambdaEvent<Event>) -> Result<DynamoDbEventResponse, Error> {
    let mut response = DynamoDbEventResponse {
        batch_item_failures: vec![],
    };

    let records = &event.payload.records;

    if records.is_empty() {
        tracing::info!("No records found. Exiting.");
        return Ok(response);
    }

    for record in records {
        tracing::info!("EventId: {}", record.event_id);

        // Couldn't find a sequence number
        if record.change.sequence_number.is_none() {
            response.batch_item_failures.push(DynamoDbBatchItemFailure {
                item_identifier: Some("".to_string()),
            });
            return Ok(response);
        }

        // Process your record here...
        if process_record(record).is_err() {
            response.batch_item_failures.push(DynamoDbBatchItemFailure {
                item_identifier: record.change.sequence_number.clone(),
            });
            /* Since we are working with streams, we can return the failed item immediately.
            Lambda will immediately begin to retry processing from this failed item onwards. */
            return Ok(response);
        }
    }

    tracing::info!("Successfully processed {} record(s)", records.len());

    Ok(response)
}

#[tokio::main]
async fn main() -> Result<(), Error> {
    tracing_subscriber::fmt()
        .with_max_level(tracing::Level::INFO)
        // disable printing the name of the module in every log line.
        .with_target(false)
        // disabling time is handy because CloudWatch will add the ingestion time.
        .without_time()
        .init();

    run(service_fn(function_handler)).await
}
```

------

## 使用 Powertools for AWS Lambda 批处理器
<a name="services-ddb-batchfailurereporting-powertools"></a>

Powertools for AWS Lambda 中的批处理器实用程序会自动处理部分批处理响应逻辑，从而降低实施批处理故障报告的复杂性。下面是使用批处理器的示例：

**Python**  
有关完整的示例和设置说明，请参阅[批处理器文档](https://docs.powertools.aws.dev/lambda/python/latest/utilities/batch/)。
使用 AWS Lambda 批处理器处理 DynamoDB 流记录。  

```
import json
from aws_lambda_powertools import Logger
from aws_lambda_powertools.utilities.batch import BatchProcessor, EventType, process_partial_response
from aws_lambda_powertools.utilities.data_classes import DynamoDBStreamEvent
from aws_lambda_powertools.utilities.typing import LambdaContext

processor = BatchProcessor(event_type=EventType.DynamoDBStreams)
logger = Logger()

def record_handler(record):
    logger.info(record)
    # Your business logic here
    # Raise an exception to mark this record as failed
    
def lambda_handler(event, context: LambdaContext):
    return process_partial_response(
        event=event, 
        record_handler=record_handler, 
        processor=processor,
        context=context
    )
```

**TypeScript**  
有关完整的示例和设置说明，请参阅[批处理器文档](https://docs.aws.amazon.com/powertools/typescript/latest/features/batch/)。
使用 AWS Lambda 批处理器处理 DynamoDB 流记录。  

```
import { BatchProcessor, EventType, processPartialResponse } from '@aws-lambda-powertools/batch';
import { Logger } from '@aws-lambda-powertools/logger';
import type { DynamoDBStreamEvent, Context } from 'aws-lambda';

const processor = new BatchProcessor(EventType.DynamoDBStreams);
const logger = new Logger();

const recordHandler = async (record: any): Promise<void> => {
    logger.info('Processing record', { record });
    // Your business logic here
    // Throw an error to mark this record as failed
};

export const handler = async (event: DynamoDBStreamEvent, context: Context) => {
    return processPartialResponse(event, recordHandler, processor, {
        context,
    });
};
```

**Java**  
有关完整的示例和设置说明，请参阅[批处理器文档](https://docs.powertools.aws.dev/lambda/java/latest/utilities/batch/)。
使用 AWS Lambda 批处理器处理 DynamoDB 流记录。  

```
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.DynamodbEvent;
import com.amazonaws.services.lambda.runtime.events.StreamsEventResponse;
import software.amazon.lambda.powertools.batch.BatchMessageHandlerBuilder;
import software.amazon.lambda.powertools.batch.handler.BatchMessageHandler;

public class DynamoDBStreamBatchHandler implements RequestHandler<DynamodbEvent, StreamsEventResponse> {

    private final BatchMessageHandler<DynamodbEvent, StreamsEventResponse> handler;

    public DynamoDBStreamBatchHandler() {
        handler = new BatchMessageHandlerBuilder()
                .withDynamoDbBatchHandler()
                .buildWithRawMessageHandler(this::processMessage);
    }

    @Override
    public StreamsEventResponse handleRequest(DynamodbEvent ddbEvent, Context context) {
        return handler.processBatch(ddbEvent, context);
    }

    private void processMessage(DynamodbEvent.DynamodbStreamRecord dynamodbStreamRecord, Context context) {
        // Process the change record
    }
}
```

**.NET**  
有关完整的示例和设置说明，请参阅[批处理器文档](https://docs.aws.amazon.com/powertools/dotnet/utilities/batch-processing/)。
使用 AWS Lambda 批处理器处理 DynamoDB 流记录。  

```
using System;
using System.Threading;
using System.Threading.Tasks;
using Amazon.Lambda.Core;
using Amazon.Lambda.DynamoDBEvents;
using Amazon.Lambda.Serialization.SystemTextJson;
using AWS.Lambda.Powertools.BatchProcessing;

[assembly: LambdaSerializer(typeof(DefaultLambdaJsonSerializer))]

namespace HelloWorld;

public class Customer
{
    public string? CustomerId { get; set; }
    public string? Name { get; set; }
    public string? Email { get; set; }
    public DateTime CreatedAt { get; set; }
}

internal class TypedDynamoDbRecordHandler : ITypedRecordHandler<Customer> 
{
    public async Task<RecordHandlerResult> HandleAsync(Customer customer, CancellationToken cancellationToken)
    {
        if (string.IsNullOrEmpty(customer.Email)) 
        {
            throw new ArgumentException("Customer email is required");
        }

        return await Task.FromResult(RecordHandlerResult.None); 
    }
}

public class Function
{
    [BatchProcessor(TypedRecordHandler = typeof(TypedDynamoDbRecordHandler))]
    public BatchItemFailuresResponse HandlerUsingTypedAttribute(DynamoDBEvent _)
    {
        return TypedDynamoDbStreamBatchProcessor.Result.BatchItemFailuresResponse; 
    }
}
```

# 在 Lambda 中保留 DynamoDB 事件源的丢弃记录
<a name="services-dynamodb-errors"></a>

DynamoDB 事件源映射的错误处理取决于错误是在调用函数之前还是在函数调用期间发生的：
+ **调用前：**如果 Lambda 事件源映射由于节流或其他问题而无法调用该函数，则它会一直重试，直到记录过期或超过事件源映射上配置的最大期限（[MaximumRecordAgeInSeconds](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html#lambda-CreateEventSourceMapping-request-MaximumRecordAgeInSeconds)）。
+ **调用期间：**如果调用函数但返回错误，Lambda 会重试，直到记录过期、超过最大期限（[MaximumRecordAgeInSeconds](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html#lambda-CreateEventSourceMapping-request-MaximumRecordAgeInSeconds)）或达到配置的重试配额（[MaximumRetryAttempts](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html#lambda-CreateEventSourceMapping-request-MaximumRetryAttempts)）。对于函数错误，您还可以配置 [BisectBatchOnFunctionError](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html#lambda-CreateEventSourceMapping-response-BisectBatchOnFunctionError)，将失败的批次拆分为两个较小的批次，从而隔离错误记录并避免超时。拆分批次不会消耗重试配额。

如果错误处理措施失败，Lambda 将丢弃记录并继续处理数据流中的批次。使用默认设置时，这意味着错误的记录可能会阻止受影响的分区上的处理，时间长达一天。为了避免这种情况，请配置函数的事件源映射，使用合理的重试次数和适合您的使用案例的最长记录期限。

## 配置失败调用的目标
<a name="dynamodb-on-failure-destination-console"></a>

要保留失败的事件源映射调用的记录，请在函数的事件源映射中添加一个目标。发送到目标的每条记录都是一个 JSON 文档，其中包含有关失败调用的元数据。对于 Amazon S3 目标，Lambda 还会发送整个调用记录以及元数据。您可以将任何 Amazon SNS 主题、Amazon SQS 队列、Amazon S3 存储桶或 Kafka 配置为目标。

借助 Amazon S3 目标，您可以使用 [Amazon S3 事件通知](https://docs.aws.amazon.com/)功能在对象上传到目标 S3 存储桶时接收通知。您还可以将 S3 事件通知配置为调用另一个 Lambda 函数来对失败的批次执行自动处理。

您的执行角色必须具有目标的权限：
+ **对于 SQS 目标：**[sqs:SendMessage](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_SendMessage.html)
+ **对于 SNS 目标：**[sns:Publish](https://docs.aws.amazon.com/sns/latest/api/API_Publish.html)
+ **对于 S3 目标：**[ s3:PutObject](https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html) 和 [s3:ListBucket](https://docs.aws.amazon.com/AmazonS3/latest/API/ListObjectsV2.html)
+ **对于 Kafka 目标：**[kafka-cluster:WriteData](https://docs.aws.amazon.com/msk/latest/developerguide/kafka-actions.html)

您可以将 Kafka 主题配置为 Kafka 事件源映射失败时的目标。当 Lambda 在重试次数用尽后仍无法处理记录，或者当记录的保存时间超过最大期限时，Lambda 会将这些失败的记录发送至指定的 Kafka 主题，以便后续进行处理。请参考[使用 Kafka 主题作为失败时的目标](kafka-on-failure-destination.md)。

如果您已使用自己的 KMS 密钥为 S3 目标启用加密，则函数的执行角色还必须具有调用 [kms:GenerateDataKey](https://docs.aws.amazon.com/kms/latest/APIReference/API_GenerateDataKey.html) 的权限。如果 KMS 密钥和 S3 存储桶目标与您的 Lambda 函数和执行角色位于不同的账户中，请将 KMS 密钥配置为信任执行角色以允许 kms:GenerateDataKey。

要使用控制台配置失败时的目标，请执行以下步骤：

1. 打开 Lamba 控制台的[函数](https://console.aws.amazon.com/lambda/home#/functions)页面。

1. 选择函数。

1. 在 **Function overview (函数概览)** 下，选择 **Add destination (添加目标)**。

1. 对于**源**，请选择**事件源映射调用**。

1. 对于**事件源映射**，请选择为此函数配置的事件源。

1. 在**条件**中，选择**失败时**。对于事件源映射调用，这是唯一可接受的条件。

1. 对于**目标类型**，请选择 Lambda 要发送调用记录的目标类型。

1. 对于 **Destination (目标)**，请选择一个资源。

1. 选择**保存**。

您还可以使用 AWS Command Line Interface（AWS CLI）配置失败时的目标。例如，以 [create-event-source-mapping](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/create-event-source-mapping.html) 命令将带有 SQS 失败时目标的事件源映射添加到 `MyFunction`：

```
aws lambda create-event-source-mapping \
--function-name "MyFunction" \
--event-source-arn arn:aws:dynamodb:us-east-2:123456789012:table/my-table/stream/2024-06-10T19:26:16.525 \
--destination-config '{"OnFailure": {"Destination": "arn:aws:sqs:us-east-1:123456789012:dest-queue"}}'
```

以下 [update-event-source-mapping](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/update-event-source-mapping.html) 命令更新事件源映射，以在两次重试之后或记录超过一小时后将失败的调用记录发送到 SNS 目标。

```
aws lambda update-event-source-mapping \
--uuid f89f8514-cdd9-4602-9e1f-01a5b77d449b \
--maximum-retry-attempts 2 \
--maximum-record-age-in-seconds 3600 \
--destination-config '{"OnFailure": {"Destination": "arn:aws:sns:us-east-1:123456789012:dest-topic"}}'
```

更新的设置是异步应用的，并且直到该过程完成才反映在输出中。使用 [get-event-source-mapping](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/get-event-source-mapping.html) 命令查看当前状态。

要移除目标，请提供一个空字符串作为 `destination-config` 参数的实际参数：

```
aws lambda update-event-source-mapping \
--uuid f89f8514-cdd9-4602-9e1f-01a5b77d449b \
--destination-config '{"OnFailure": {"Destination": ""}}'
```

### Amazon S3 目标的安全最佳实践
<a name="ddb-s3-destination-security"></a>

如果删除配置为目标的 S3 存储桶而不将目标从函数配置中删除，则可能会造成安全风险。如果其他用户知道您的目标存储桶的名称，则他们可以在其 AWS 账户中重新创建存储桶。调用失败的记录将发送到存储桶，这可能会暴露您函数中的数据。

**警告**  
为确保您的函数中的调用记录不会发送到另一个 AWS 账户中的 S3 存储桶，请向函数的执行角色添加条件，以将 `s3:PutObject` 权限限制为您账户中的存储桶。

以下示例显示了一个 IAM 策略，该策略将您函数的 `s3:PutObject` 权限限制为您账户中的存储桶。该策略还为 Lambda 提供了使用 S3 存储桶作为目标所需的 `s3:ListBucket` 权限。

```
{
    "Version": "2012-10-17",		 	 	 
    "Statement": [
        {
            "Sid": "S3BucketResourceAccountWrite",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:ListBucket"
            ],
            "Resource": [
                "arn:aws:s3:::*/*",
                "arn:aws:s3:::*"
            ],
            "Condition": {
                "StringEquals": {
                    "s3:ResourceAccount": "111122223333"
                }
            }
        }
    ]
}
```

要使用 AWS 管理控制台 或 AWS CLI 向函数的执行角色添加权限策略，请参阅以下程序中的说明：

------
#### [ Console ]

**向函数的执行角色添加权限策略（控制台）**

1. 打开 Lamba 控制台的[函数](https://console.aws.amazon.com/lambda/home#/functions)页面。

1. 选择想要修改其执行角色的 Lambda 函数。

1. 在**配置**选项卡中，选择**权限**。

1. 在**执行角色**选项卡中，选择您的函数的**角色名称**,以打开该角色的 IAM 控制台页面。

1. 通过执行以下操作，向角色添加权限策略：

   1. 在**权限策略**窗格中，选择**添加权限**，然后选择**创建内联策略**。

   1. 在**策略编辑器**中，选择 **JSON**。

   1. 将要添加的策略粘贴到编辑器中（替换现有 JSON），然后选择**下一步**。

   1. 在**策略详细信息**下，输入**策略名称**。

   1. 选择**创建策略**。

------
#### [ AWS CLI ]

**向函数的执行角色添加权限策略（CLI）**

1. 创建具有所需权限的 JSON 策略文档并将其保存在本地目录中。

1. 使用 IAM `put-role-policy` CLI 命令向您函数的执行角色添加权限。在您保存 JSON 策略文档的目录中运行以下命令，并用您自己的值替换角色名称、策略名称和策略文档。

   ```
   aws iam put-role-policy \
   --role-name my_lambda_role \
   --policy-name LambdaS3DestinationPolicy \
   --policy-document file://my_policy.json
   ```

------

### Amazon SNS 和 Amazon SQS 调用记录示例
<a name="kinesis-on-failure-destination-example-sns-sqs"></a>

以下示例显示了 Lambda 发送到 DynamoDB 流的 SQS 或 SNS 目标的调用记录。

```
{
    "requestContext": {
        "requestId": "316aa6d0-8154-xmpl-9af7-85d5f4a6bc81",
        "functionArn": "arn:aws:lambda:us-east-2:123456789012:function:myfunction",
        "condition": "RetryAttemptsExhausted",
        "approximateInvokeCount": 1
    },
    "responseContext": {
        "statusCode": 200,
        "executedVersion": "$LATEST",
        "functionError": "Unhandled"
    },
    "version": "1.0",
    "timestamp": "2019-11-14T00:13:49.717Z",
    "DDBStreamBatchInfo": {
        "shardId": "shardId-00000001573689847184-864758bb",
        "startSequenceNumber": "800000000003126276362",
        "endSequenceNumber": "800000000003126276362",
        "approximateArrivalOfFirstRecord": "2019-11-14T00:13:19Z",
        "approximateArrivalOfLastRecord": "2019-11-14T00:13:19Z",
        "batchSize": 1,
        "streamArn": "arn:aws:dynamodb:us-east-2:123456789012:table/mytable/stream/2019-11-14T00:04:06.388"
    }
}
```

您可以使用此信息从流中检索受影响的记录以进行故障排除。实际记录不包括在内，因此您必须处理此记录并在记录过期和丢失之前从流中检索它们。

### Amazon S3 调用记录示例
<a name="kinesis-on-failure-destination-example-sns-sqs-s3"></a>

以下示例显示了 Lambda 发送到 DynamoDB 流的 S3 存储桶的调用记录。除了针对 SQS 和 SNS 目标的上一示例中的所有字段外，`payload` 字段还包含作为转义 JSON 字符串的原始调用记录。

```
{
    "requestContext": {
        "requestId": "316aa6d0-8154-xmpl-9af7-85d5f4a6bc81",
        "functionArn": "arn:aws:lambda:us-east-2:123456789012:function:myfunction",
        "condition": "RetryAttemptsExhausted",
        "approximateInvokeCount": 1
    },
    "responseContext": {
        "statusCode": 200,
        "executedVersion": "$LATEST",
        "functionError": "Unhandled"
    },
    "version": "1.0",
    "timestamp": "2019-11-14T00:13:49.717Z",
    "DDBStreamBatchInfo": {
        "shardId": "shardId-00000001573689847184-864758bb",
        "startSequenceNumber": "800000000003126276362",
        "endSequenceNumber": "800000000003126276362",
        "approximateArrivalOfFirstRecord": "2019-11-14T00:13:19Z",
        "approximateArrivalOfLastRecord": "2019-11-14T00:13:19Z",
        "batchSize": 1,
        "streamArn": "arn:aws:dynamodb:us-east-2:123456789012:table/mytable/stream/2019-11-14T00:04:06.388"
    },
    "payload": "<Whole Event>" // Only available in S3
}
```

包含了调用记录的 S3 对象使用以下命名约定：

```
aws/lambda/<ESM-UUID>/<shardID>/YYYY/MM/DD/YYYY-MM-DDTHH.MM.SS-<Random UUID>
```

# 在 Lambda 中实现有状态的 DynamoDB 流处理
<a name="services-ddb-windows"></a>

Lambda 函数可以运行连续流处理应用程序。流表示通过您的应用程序持续流动的无边界数据。要分析这种不断更新的输入中的信息，可以使用按时间定义的窗口来限制包含的记录。

滚动窗口是定期打开和关闭的不同窗口。预设情况下，Lambda 调用是无状态的，在没有外部数据库的情况下，无法使用它们跨多次连续调用处理数据。但是，有了滚动窗口后，您可以在不同调用中保持状态。此状态包含之前为当前窗口处理的消息的汇总结果。您的状态最多可以是每个分片 1MB。如果超过该大小，Lambda 将提前终止窗口。

流中的每条记录都属于特定窗口。Lambda 将至少处理每条记录一次，但不保证每条记录只处理一次。在极少数情况下（例如错误处理），某些记录可能会被多次处理。第一次处理记录时始终按顺序处理。如果多次处理记录，则可能会不按顺序处理。

## 聚合和处理
<a name="streams-tumbling-processing"></a>

系统将调用您的用户托管函数以便聚合和处理该聚合的最终结果。Lambda 汇总在该窗口中接收的所有记录。您可以分多个批次接收这些记录，每个批次都作为单独的调用。每次调用都会收到一个状态。因此，当使用滚动窗口时，Lambda 函数响应必须包含 `state` 属性。如果响应不包含 `state` 属性，Lambda 会将其视作失败的调用。为了满足该条件，您的函数可以返回一个具有以下 JSON 形状的 `TimeWindowEventResponse` 对象：

**Example `TimeWindowEventResponse` 值**  

```
{
    "state": {
        "1": 282,
        "2": 715
    },
    "batchItemFailures": []
}
```

**注意**  
对于 Java 函数，我们建议使用 `Map<String, String>` 来表示状态。

在窗口末尾，标志 `isFinalInvokeForWindow` 被设置 `true`，以表示这是最终状态，并且已准备好进行处理。处理完成后，窗口完成，最终调用完成，然后状态将被删除。

在窗口结束时，Lambda 会对针对聚合结果的操作应用最终处理。您的最终处理将同步调用。成功调用后，函数会检查序列号并继续进行流处理。如果调用失败，则您的 Lambda 函数将暂停进一步处理，直到成功调用为止。

**Example DynamodbTimeWindowEvent**  

```
{
   "Records":[
      {
         "eventID":"1",
         "eventName":"INSERT",
         "eventVersion":"1.0",
         "eventSource":"aws:dynamodb",
         "awsRegion":"us-east-1",
         "dynamodb":{
            "Keys":{
               "Id":{
                  "N":"101"
               }
            },
            "NewImage":{
               "Message":{
                  "S":"New item!"
               },
               "Id":{
                  "N":"101"
               }
            },
            "SequenceNumber":"111",
            "SizeBytes":26,
            "StreamViewType":"NEW_AND_OLD_IMAGES"
         },
         "eventSourceARN":"stream-ARN"
      },
      {
         "eventID":"2",
         "eventName":"MODIFY",
         "eventVersion":"1.0",
         "eventSource":"aws:dynamodb",
         "awsRegion":"us-east-1",
         "dynamodb":{
            "Keys":{
               "Id":{
                  "N":"101"
               }
            },
            "NewImage":{
               "Message":{
                  "S":"This item has changed"
               },
               "Id":{
                  "N":"101"
               }
            },
            "OldImage":{
               "Message":{
                  "S":"New item!"
               },
               "Id":{
                  "N":"101"
               }
            },
            "SequenceNumber":"222",
            "SizeBytes":59,
            "StreamViewType":"NEW_AND_OLD_IMAGES"
         },
         "eventSourceARN":"stream-ARN"
      },
      {
         "eventID":"3",
         "eventName":"REMOVE",
         "eventVersion":"1.0",
         "eventSource":"aws:dynamodb",
         "awsRegion":"us-east-1",
         "dynamodb":{
            "Keys":{
               "Id":{
                  "N":"101"
               }
            },
            "OldImage":{
               "Message":{
                  "S":"This item has changed"
               },
               "Id":{
                  "N":"101"
               }
            },
            "SequenceNumber":"333",
            "SizeBytes":38,
            "StreamViewType":"NEW_AND_OLD_IMAGES"
         },
         "eventSourceARN":"stream-ARN"
      }
   ],
    "window": {
        "start": "2020-07-30T17:00:00Z",
        "end": "2020-07-30T17:05:00Z"
    },
    "state": {
        "1": "state1"
    },
    "shardId": "shard123456789",
    "eventSourceARN": "stream-ARN",
    "isFinalInvokeForWindow": false,
    "isWindowTerminatedEarly": false
}
```

## 配置
<a name="streams-tumbling-config"></a>

您可以在创建或更新事件源映射时配置滚动窗口。要配置翻转窗口，请以秒为单位进行指定（[TumblingWindowInSeconds](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html#lambda-CreateEventSourceMapping-request-TumblingWindowInSeconds)）。以下示例 AWS Command Line Interface (AWS CLI) 命令会创建一个滚动窗口为 120 秒的流式事件源映射。为聚合和处理定义的 Lambda 函数被命名为 `tumbling-window-example-function`。

```
aws lambda create-event-source-mapping \
--event-source-arn arn:aws:dynamodb:us-east-2:123456789012:table/my-table/stream/2024-06-10T19:26:16.525 \
--function-name tumbling-window-example-function \
--starting-position TRIM_HORIZON \
--tumbling-window-in-seconds 120
```

Lambda 根据记录插入到流的时间来确定滚动窗口的边界。所有记录都有一个大致的时间戳，供 Lambda 在确定边界时使用。

滚动窗口聚合不支持重新分片。当分区结束时，Lambda 会认为窗口已关闭，子分区将以全新的状态启动自己的窗口。

滚动窗口完全支持现有的重试策略 `maxRetryAttempts` 和 `maxRecordAge`。

**Example Handler.py – 聚合和处理**  
以下 Python 函数演示了如何聚合然后处理您的最终状态：  

```
def lambda_handler(event, context):
    print('Incoming event: ', event)
    print('Incoming state: ', event['state'])

#Check if this is the end of the window to either aggregate or process.
    if event['isFinalInvokeForWindow']:
        # logic to handle final state of the window
        print('Destination invoke')
    else:
        print('Aggregate invoke')

#Check for early terminations
    if event['isWindowTerminatedEarly']:
        print('Window terminated early')

    #Aggregation logic
    state = event['state']
    for record in event['Records']:
        state[record['dynamodb']['NewImage']['Id']] = state.get(record['dynamodb']['NewImage']['Id'], 0) + 1

    print('Returning state: ', state)
    return {'state': state}
```

# Amazon DynamoDB 事件源映射的 Lambda 参数
<a name="services-ddb-params"></a>

所有 Lambda 事件源类型共享相同的 [CreateEventSourceMapping](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html) 和 [UpdateEventSourceMapping](https://docs.aws.amazon.com/lambda/latest/api/API_UpdateEventSourceMapping.html) API 操作。但是，只有部分参数适用于 DynamoDB Streams。


| 参数 | 必需 | 默认值 | 备注 | 
| --- | --- | --- | --- | 
|  BatchSize  |  否  |  100  |  最大值：10000  | 
|  BisectBatchOnFunctionError  |  N  |  false  | none  | 
|  DestinationConfig  |  N  | 不适用  |  丢弃的记录的标准 Amazon SQS 队列或标准 Amazon SNS 主题目标。  | 
|  启用  |  N  |  真实  | none  | 
|  EventSourceArn  |  Y  | 不适用 |  数据流或流使用者的 ARN  | 
|  FilterCriteria  |  N  | 不适用  |  [控制 Lambda 向您的函数发送的事件](invocation-eventfiltering.md)  | 
|  FunctionName  |  是  | 不适用  | none  | 
|  FunctionResponseTypes  |  N  | 不适用 |  要使您的函数报告某个批处理中的特定失败，请在 `FunctionResponseTypes` 中包含值 `ReportBatchItemFailures`。有关更多信息，请参阅 [使用 DynamoDB 和 Lambda 配置部分批处理响应](services-ddb-batchfailurereporting.md)。  | 
|  MaximumBatchingWindowInSeconds  |  N  |  0  | none  | 
|  MaximumRecordAgeInSeconds  |  N  |  –1  |  -1 表示无限：会一直重试失败的记录，直到记录过期。[DynamoDB Streams 的数据留存期限为](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Streams.html#Streams.DataRetention) 24 小时。 最小值：-1 最大值：604800  | 
|  MaximumRetryAttempts  |  N  |  –1  |  -1 表示无限：会一直重试失败的记录，直到记录过期。 最小值：0 最大值：10000  | 
|  ParallelizationFactor  |  N  |  1  |  最大值：10  | 
|  StartingPosition  |  Y  | 不适用  |  TRIM\$1HORIZON 或 LATEST（最新）  | 
|  TumblingWindowInSeconds  |  N  | 不适用  |  最小值：0 最大值：900  | 

# 对 DynamoDB 事件源使用事件筛选
<a name="with-ddb-filtering"></a>

您可以使用事件筛选，控制 Lambda 将流或队列中的哪些记录发送给函数。有关事件筛选工作原理的一般信息，请参阅 [控制 Lambda 向您的函数发送的事件](invocation-eventfiltering.md)。

本节重点介绍 DynamoDB 事件源的事件筛选。

**注意**  
DynamoDB 事件源映射仅支持对 `dynamodb` 键进行筛选。

**Topics**
+ [

## DynamoDB 事件
](#filtering-ddb)
+ [

## 使用表格属性进行筛选
](#filtering-ddb-attributes)
+ [

## 使用布尔表达式进行筛选
](#filtering-ddb-boolean)
+ [

## 使用 Exists 运算符
](#filtering-ddb-exists)
+ [

## 用于 DynamoDB 筛选的 JSON 格式
](#filtering-ddb-JSON-format)

## DynamoDB 事件
<a name="filtering-ddb"></a>

假设您有一个 DynamoDB 表，其中包含主键 `CustomerName`、属性 `AccountManager` 和 `PaymentTerms`。下面显示了来自 DynamoDB 表流的示例记录。

```
{
      "eventID": "1",
      "eventVersion": "1.0",
      "dynamodb": {
          "ApproximateCreationDateTime": "1678831218.0",
          "Keys": {
              "CustomerName": {
                  "S": "AnyCompany Industries"
              }
          },
          "NewImage": {
              "AccountManager": {
                  "S": "Pat Candella"
              },
              "PaymentTerms": {
                  "S": "60 days"
              },
              "CustomerName": {
                  "S": "AnyCompany Industries"
              }
          },
          "SequenceNumber": "111",
          "SizeBytes": 26,
          "StreamViewType": "NEW_IMAGE"
      }
  }
```

要根据 DynamoDB 表中的键和属性值进行筛选，请使用记录中的 `dynamodb` 键。以下部分提供了不同筛选条件类型的示例。

### 使用表键进行筛选
<a name="filtering-ddb-keys"></a>

假设您希望函数仅处理主键 `CustomerName` 为“AnyCompany Industries”的记录。`FilterCriteria` 对象将如下所示。

```
{
     "Filters": [
          {
              "Pattern": "{ \"dynamodb\" : { \"Keys\" : { \"CustomerName\" : { \"S\" : [ \"AnyCompany Industries\" ] } } } }"
          }
      ]
 }
```

为了更清楚起见，以下是在纯 JSON 中展开的筛选条件 `Pattern` 的值。

```
{
     "dynamodb": {
          "Keys": {
              "CustomerName": {
                  "S": [ "AnyCompany Industries" ]
                  }
              }
          }
 }
```

您可以使用控制台、AWS CLI 或 AWS SAM 模板添加筛选条件。

------
#### [ Console ]

要使用控制台添加此筛选条件，请按照 [将筛选条件附加到事件源映射（控制台）](invocation-eventfiltering.md#filtering-console) 中的说明，为**筛选条件**输入以下字符串。

```
{ "dynamodb" : { "Keys" : { "CustomerName" : { "S" : [ "AnyCompany Industries" ] } } } }
```

------
#### [ AWS CLI ]

要使用 AWS Command Line Interface（AWS CLI）创建包含这些筛选条件的新事件源映射，请运行以下命令。

```
aws lambda create-event-source-mapping \
    --function-name my-function \
    --event-source-arn arn:aws:dynamodb:us-east-2:123456789012:table/my-table \
    --filter-criteria '{"Filters": [{"Pattern": "{ \"dynamodb\" : { \"Keys\" : { \"CustomerName\" : { \"S\" : [ \"AnyCompany Industries\" ] } } } }"}]}'
```

要将这些筛选条件添加到现有事件源映射中，请运行以下命令。

```
aws lambda update-event-source-mapping \
    --uuid "a1b2c3d4-5678-90ab-cdef-11111EXAMPLE" \
    --filter-criteria '{"Filters": [{"Pattern": "{ \"dynamodb\" : { \"Keys\" : { \"CustomerName\" : { \"S\" : [ \"AnyCompany Industries\" ] } } } }"}]}'
```

------
#### [ AWS SAM ]

要使用 AWS SAM 添加此筛选条件，请将以下代码段添加到事件源的 YAML 模板中。

```
FilterCriteria:
   Filters:
     - Pattern: '{ "dynamodb" : { "Keys" : { "CustomerName" : { "S" : [ "AnyCompany Industries" ] } } } }'
```

------

## 使用表格属性进行筛选
<a name="filtering-ddb-attributes"></a>

借助 DynamoDB，您还可以使用 `NewImage` 和 `OldImage` 键来筛选属性值。假设您要筛选最新表格图像中 `AccountManager` 属性为“Pat Candella”或“Shirley Rodriguez”的记录。`FilterCriteria` 对象将如下所示。

```
{
    "Filters": [
        {
            "Pattern": "{ \"dynamodb\" : { \"NewImage\" : { \"AccountManager\" : { \"S\" : [ \"Pat Candella\", \"Shirley Rodriguez\" ] } } } }"
        }
    ]
}
```

为了更清楚起见，以下是在纯 JSON 中展开的筛选条件 `Pattern` 的值。

```
{
    "dynamodb": {
        "NewImage": {
            "AccountManager": {
                "S": [ "Pat Candella", "Shirley Rodriguez" ]
            }
        }
    }
}
```

您可以使用控制台、AWS CLI 或 AWS SAM 模板添加筛选条件。

------
#### [ Console ]

要使用控制台添加此筛选条件，请按照 [将筛选条件附加到事件源映射（控制台）](invocation-eventfiltering.md#filtering-console) 中的说明，为**筛选条件**输入以下字符串。

```
{ "dynamodb" : { "NewImage" : { "AccountManager" : { "S" : [ "Pat Candella", "Shirley Rodriguez" ] } } } }
```

------
#### [ AWS CLI ]

要使用 AWS Command Line Interface（AWS CLI）创建包含这些筛选条件的新事件源映射，请运行以下命令。

```
aws lambda create-event-source-mapping \
    --function-name my-function \
    --event-source-arn arn:aws:dynamodb:us-east-2:123456789012:table/my-table \
    --filter-criteria '{"Filters": [{"Pattern": "{ \"dynamodb\" : { \"NewImage\" : { \"AccountManager\" : { \"S\" : [ \"Pat Candella\", \"Shirley Rodriguez\" ] } } } }"}]}'
```

要将这些筛选条件添加到现有事件源映射中，请运行以下命令。

```
aws lambda update-event-source-mapping \
    --uuid "a1b2c3d4-5678-90ab-cdef-11111EXAMPLE" \
    --filter-criteria '{"Filters": [{"Pattern": "{ \"dynamodb\" : { \"NewImage\" : { \"AccountManager\" : { \"S\" : [ \"Pat Candella\", \"Shirley Rodriguez\" ] } } } }"}]}'
```

------
#### [ AWS SAM ]

要使用 AWS SAM 添加此筛选条件，请将以下代码段添加到事件源的 YAML 模板中。

```
FilterCriteria:
  Filters:
    - Pattern: '{ "dynamodb" : { "NewImage" : { "AccountManager" : { "S" : [ "Pat Candella", "Shirley Rodriguez" ] } } } }'
```

------

## 使用布尔表达式进行筛选
<a name="filtering-ddb-boolean"></a>

您也可以使用布尔 AND 表达式创建筛选器。这些表达式可能同时包含表的键和属性参数。假设您想要筛选 `AccountManager` 的 `NewImage` 值为“Pat Candella”且 `OldImage` 值为“Terry Whitlock”的记录。`FilterCriteria` 对象将如下所示。

```
{
    "Filters": [
        {
            "Pattern": "{ \"dynamodb\" : { \"NewImage\" : { \"AccountManager\" : { \"S\" : [ \"Pat Candella\" ] } } } , \"dynamodb\" : { \"OldImage\" : { \"AccountManager\" : { \"S\" : [ \"Terry Whitlock\" ] } } } }"
        }
    ]
}
```

为了更清楚起见，以下是在纯 JSON 中展开的筛选条件 `Pattern` 的值。

```
{ 
    "dynamodb" : { 
        "NewImage" : { 
            "AccountManager" : { 
                "S" : [ 
                    "Pat Candella" 
                ] 
            } 
        } 
    }, 
    "dynamodb": { 
        "OldImage": { 
            "AccountManager": { 
                "S": [ 
                    "Terry Whitlock" 
                ] 
            } 
        } 
    } 
}
```

您可以使用控制台、AWS CLI 或 AWS SAM 模板添加筛选条件。

------
#### [ Console ]

要使用控制台添加此筛选条件，请按照 [将筛选条件附加到事件源映射（控制台）](invocation-eventfiltering.md#filtering-console) 中的说明，为**筛选条件**输入以下字符串。

```
{ "dynamodb" : { "NewImage" : { "AccountManager" : { "S" : [ "Pat Candella" ] } } } , "dynamodb" : { "OldImage" : { "AccountManager" : { "S" : [ "Terry Whitlock" ] } } } }
```

------
#### [ AWS CLI ]

要使用 AWS Command Line Interface（AWS CLI）创建包含这些筛选条件的新事件源映射，请运行以下命令。

```
aws lambda create-event-source-mapping \
    --function-name my-function \
    --event-source-arn arn:aws:dynamodb:us-east-2:123456789012:table/my-table \
    --filter-criteria '{"Filters": [{"Pattern": "{ \"dynamodb\" : { \"NewImage\" : { \"AccountManager\" : { \"S\" : [ \"Pat Candella\" ] } } } , \"dynamodb\" : { \"OldImage\" : { \"AccountManager\" : { \"S\" : [ \"Terry Whitlock\" ] } } } } "}]}'
```

要将这些筛选条件添加到现有事件源映射中，请运行以下命令。

```
aws lambda update-event-source-mapping \
    --uuid "a1b2c3d4-5678-90ab-cdef-11111EXAMPLE" \
    --filter-criteria '{"Filters": [{"Pattern": "{ \"dynamodb\" : { \"NewImage\" : { \"AccountManager\" : { \"S\" : [ \"Pat Candella\" ] } } } , \"dynamodb\" : { \"OldImage\" : { \"AccountManager\" : { \"S\" : [ \"Terry Whitlock\" ] } } } } "}]}'
```

------
#### [ AWS SAM ]

要使用 AWS SAM 添加此筛选条件，请将以下代码段添加到事件源的 YAML 模板中。

```
FilterCriteria:
  Filters:
    - Pattern: '{ "dynamodb" : { "NewImage" : { "AccountManager" : { "S" : [ "Pat Candella" ] } } } , "dynamodb" : { "OldImage" : { "AccountManager" : { "S" : [ "Terry Whitlock" ] } } } }'
```

------

**注意**  
DynamoDB 事件筛选不支持使用数字运算符（数字相等和数字范围）。即使表中的项目存储为数字，这些参数也会转换为 JSON 记录对象中的字符串。

## 使用 Exists 运算符
<a name="filtering-ddb-exists"></a>

鉴于 DynamoDB 中 JSON 事件对象的结构方式，使用 Exists 运算符需要特别小心。Exists 运算符仅适用于事件 JSON 中的叶节点，若筛选条件模式使用 Exists 来测试中间节点，则不会起作用。请考虑以下 DynamoDB 表项目：

```
{
  "UserID": {"S": "12345"},
  "Name": {"S": "John Doe"},
  "Organizations": {"L": [
      {"S":"Sales"},
      {"S":"Marketing"},
      {"S":"Support"}
    ]
  }
}
```

您可能需要创建如下所示的筛选条件模式来测试包含 `"Organizations"` 的事件：

```
{ "dynamodb" : { "NewImage" : { "Organizations" : [ { "exists": true } ] } } }
```

不过，此筛选条件模式永远不会返回匹配项，因为 `"Organizations"` 不是叶节点。以下示例展示了如何正确使用 Exists 运算符来构造所需的筛选条件模式：

```
{ "dynamodb" : { "NewImage" : {"Organizations": {"L": {"S": [ {"exists": true } ] } } } } }
```

## 用于 DynamoDB 筛选的 JSON 格式
<a name="filtering-ddb-JSON-format"></a>

要正确筛选 DynamoDB 源中的事件，数据字段及其筛选条件 (`dynamodb`) 都必须为有效的 JSON 格式。如果任一字段不为有效的 JSON 格式，Lambda 将会丢弃消息或引发异常。下表汇总了具体行为：


| 传入数据格式 | 数据属性中的筛选条件模式格式 | 导致的操作 | 
| --- | --- | --- | 
|  有效 JSON  |  有效 JSON  |  Lambda 根据您的筛选条件进行筛选。  | 
|  有效 JSON  |  数据属性中没有筛选条件模式  |  Lambda 根据您的筛选条件进行筛选（仅限其他元数据属性）。  | 
|  有效 JSON  |  非 JSON  |  Lambda 在事件源映射创建或更新时引发异常。数据属性的筛选条件模式必须为有效的 JSON 格式。  | 
|  非 JSON  |  有效 JSON  |  Lambda 将丢弃记录。  | 
|  非 JSON  |  数据属性中没有筛选条件模式  |  Lambda 根据您的筛选条件进行筛选（仅限其他元数据属性）。  | 
|  非 JSON  |  非 JSON  |  Lambda 在事件源映射创建或更新时引发异常。数据属性的筛选条件模式必须为有效的 JSON 格式。  | 

# 教程：将 AWS Lambda 与 Amazon DynamoDB Streams 结合使用
<a name="with-ddb-example"></a>

 在本教程中，您将创建 Lambda 函数处理来自 Amazon DynamoDB Streams 的事件。

## 先决条件
<a name="with-ddb-prepare"></a>

### 安装 AWS Command Line Interface
<a name="install_aws_cli"></a>

如果您尚未安装 AWS Command Line Interface，请按照[安装或更新最新版本的 AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) 中的步骤进行安装。

本教程需要命令行终端或 Shell 来运行命令。在 Linux 和 macOS 中，可使用您首选的 Shell 和程序包管理器。

**注意**  
在 Windows 中，操作系统的内置终端不支持您经常与 Lambda 一起使用的某些 Bash CLI 命令（例如 `zip`）。[安装 Windows Subsystem for Linux](https://docs.microsoft.com/en-us/windows/wsl/install-win10)，获取 Ubuntu 和 Bash 与 Windows 集成的版本。

## 创建执行角色
<a name="with-ddb-create-execution-role"></a>

创建[执行角色](lambda-intro-execution-role.md)，向您的函数授予访问 AWS 资源的权限。

**创建执行角色**

1. 在 IAM 控制台中，打开 [Roles（角色）页面](https://console.aws.amazon.com/iam/home#/roles)。

1. 选择**创建角色**。

1. 创建具有以下属性的角色。
   + **Trusted entity**（可信任的实体）– Lambda。
   + **Permissions**（权限）– **AWSLambdaDynamoDBExecutionRole**。
   + **Role name**（角色名称）– **lambda-dynamodb-role**。

**AWSLambdaDynamoDBExecutionRole** 具有该函数所需的权限以从 DynamoDB 中读取项目并将日志写入 CloudWatch Logs。

## 创建函数
<a name="with-ddb-example-create-function"></a>

创建一个 Lambda 函数来处理 DynamoDB 事件。函数代码会将一些传入的事件数据写入 CloudWatch Logs。

------
#### [ .NET ]

**适用于 .NET 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-ddb-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
通过 .NET 将 DynamoDB 事件与 Lambda 结合使用。  

```
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
using System.Text.Json;
using System.Text;
using Amazon.Lambda.Core;
using Amazon.Lambda.DynamoDBEvents;

// Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]

namespace AWSLambda_DDB;

public class Function
{
    public void FunctionHandler(DynamoDBEvent dynamoEvent, ILambdaContext context)
    {
        context.Logger.LogInformation($"Beginning to process {dynamoEvent.Records.Count} records...");

        foreach (var record in dynamoEvent.Records)
        {
            context.Logger.LogInformation($"Event ID: {record.EventID}");
            context.Logger.LogInformation($"Event Name: {record.EventName}");

            context.Logger.LogInformation(JsonSerializer.Serialize(record));
        }

        context.Logger.LogInformation("Stream processing complete.");
    }
}
```

------
#### [ Go ]

**适用于 Go 的 SDK V2**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-ddb-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
使用 Go 将 DynamoDB 事件与 Lambda 结合使用。  

```
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package main

import (
	"context"
	"github.com/aws/aws-lambda-go/lambda"
	"github.com/aws/aws-lambda-go/events"
	"fmt"
)

func HandleRequest(ctx context.Context, event events.DynamoDBEvent) (*string, error) {
	if len(event.Records) == 0 {
		return nil, fmt.Errorf("received empty event")
	}

	for _, record := range event.Records {
	 	LogDynamoDBRecord(record)
	}

	message := fmt.Sprintf("Records processed: %d", len(event.Records))
	return &message, nil
}

func main() {
	lambda.Start(HandleRequest)
}

func LogDynamoDBRecord(record events.DynamoDBEventRecord){
	fmt.Println(record.EventID)
	fmt.Println(record.EventName)
	fmt.Printf("%+v\n", record.Change)
}
```

------
#### [ Java ]

**适用于 Java 的 SDK 2.x**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-ddb-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
使用 Java 将 DynamoDB 事件与 Lambda 结合使用。  

```
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.DynamodbEvent;
import com.amazonaws.services.lambda.runtime.events.DynamodbEvent.DynamodbStreamRecord;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

public class example implements RequestHandler<DynamodbEvent, Void> {

    private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();

    @Override
    public Void handleRequest(DynamodbEvent event, Context context) {
        System.out.println(GSON.toJson(event));
        event.getRecords().forEach(this::logDynamoDBRecord);
        return null;
    }

    private void logDynamoDBRecord(DynamodbStreamRecord record) {
        System.out.println(record.getEventID());
        System.out.println(record.getEventName());
        System.out.println("DynamoDB Record: " + GSON.toJson(record.getDynamodb()));
    }
}
```

------
#### [ JavaScript ]

**SDK for JavaScript（v3）**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-ddb-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
使用 JavaScript 将 DynamoDB 事件与 Lambda 结合使用。  

```
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
exports.handler = async (event, context) => {
    console.log(JSON.stringify(event, null, 2));
    event.Records.forEach(record => {
        logDynamoDBRecord(record);
    });
};

const logDynamoDBRecord = (record) => {
    console.log(record.eventID);
    console.log(record.eventName);
    console.log(`DynamoDB Record: ${JSON.stringify(record.dynamodb)}`);
};
```
使用 TypeScript 将 DynamoDB 事件与 Lambda 结合使用。  

```
export const handler = async (event, context) => {
    console.log(JSON.stringify(event, null, 2));
    event.Records.forEach(record => {
        logDynamoDBRecord(record);
    });
}
const logDynamoDBRecord = (record) => {
    console.log(record.eventID);
    console.log(record.eventName);
    console.log(`DynamoDB Record: ${JSON.stringify(record.dynamodb)}`);
};
```

------
#### [ PHP ]

**适用于 PHP 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-ddb-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
使用 PHP 将 DynamoDB 事件与 Lambda 结合使用。  

```
<?php

# using bref/bref and bref/logger for simplicity

use Bref\Context\Context;
use Bref\Event\DynamoDb\DynamoDbEvent;
use Bref\Event\DynamoDb\DynamoDbHandler;
use Bref\Logger\StderrLogger;

require __DIR__ . '/vendor/autoload.php';

class Handler extends DynamoDbHandler
{
    private StderrLogger $logger;

    public function __construct(StderrLogger $logger)
    {
        $this->logger = $logger;
    }

    /**
     * @throws JsonException
     * @throws \Bref\Event\InvalidLambdaEvent
     */
    public function handleDynamoDb(DynamoDbEvent $event, Context $context): void
    {
        $this->logger->info("Processing DynamoDb table items");
        $records = $event->getRecords();

        foreach ($records as $record) {
            $eventName = $record->getEventName();
            $keys = $record->getKeys();
            $old = $record->getOldImage();
            $new = $record->getNewImage();
            
            $this->logger->info("Event Name:".$eventName."\n");
            $this->logger->info("Keys:". json_encode($keys)."\n");
            $this->logger->info("Old Image:". json_encode($old)."\n");
            $this->logger->info("New Image:". json_encode($new));
            
            // TODO: Do interesting work based on the new data

            // Any exception thrown will be logged and the invocation will be marked as failed
        }

        $totalRecords = count($records);
        $this->logger->info("Successfully processed $totalRecords items");
    }
}

$logger = new StderrLogger();
return new Handler($logger);
```

------
#### [ Python ]

**适用于 Python 的 SDK（Boto3）**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-ddb-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
通过 Python 将 DynamoDB 事件与 Lambda 结合使用。  

```
import json

def lambda_handler(event, context):
    print(json.dumps(event, indent=2))

    for record in event['Records']:
        log_dynamodb_record(record)

def log_dynamodb_record(record):
    print(record['eventID'])
    print(record['eventName'])
    print(f"DynamoDB Record: {json.dumps(record['dynamodb'])}")
```

------
#### [ Ruby ]

**适用于 Ruby 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-ddb-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
通过 Ruby 将 DynamoDB 事件与 Lambda 结合使用。  

```
def lambda_handler(event:, context:)
    return 'received empty event' if event['Records'].empty?
  
    event['Records'].each do |record|
      log_dynamodb_record(record)
    end
  
    "Records processed: #{event['Records'].length}"
  end
  
  def log_dynamodb_record(record)
    puts record['eventID']
    puts record['eventName']
    puts "DynamoDB Record: #{JSON.generate(record['dynamodb'])}"
  end
```

------
#### [ Rust ]

**适用于 Rust 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-ddb-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
使用 Rust 将 DynamoDB 事件与 Lambda 结合使用。  

```
use lambda_runtime::{service_fn, tracing, Error, LambdaEvent};
use aws_lambda_events::{
    event::dynamodb::{Event, EventRecord},
   };


// Built with the following dependencies:
//lambda_runtime = "0.11.1"
//serde_json = "1.0"
//tokio = { version = "1", features = ["macros"] }
//tracing = { version = "0.1", features = ["log"] }
//tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt"] }
//aws_lambda_events = "0.15.0"

async fn function_handler(event: LambdaEvent<Event>) ->Result<(), Error> {
    
    let records = &event.payload.records;
    tracing::info!("event payload: {:?}",records);
    if records.is_empty() {
        tracing::info!("No records found. Exiting.");
        return Ok(());
    }

    for record in records{
        log_dynamo_dbrecord(record);
    }

    tracing::info!("Dynamo db records processed");

    // Prepare the response
    Ok(())

}

fn log_dynamo_dbrecord(record: &EventRecord)-> Result<(), Error>{
    tracing::info!("EventId: {}", record.event_id);
    tracing::info!("EventName: {}", record.event_name);
    tracing::info!("DynamoDB Record: {:?}", record.change );
    Ok(())

}

#[tokio::main]
async fn main() -> Result<(), Error> {
    tracing_subscriber::fmt()
    .with_max_level(tracing::Level::INFO)
    .with_target(false)
    .without_time()
    .init();

    let func = service_fn(function_handler);
    lambda_runtime::run(func).await?;
    Ok(())
    
}
```

------

**创建函数**

1. 将示例代码复制到名为 `example.js` 的文件中。

1. 创建部署程序包。

   ```
   zip function.zip example.js
   ```

1. 使用 `create-function` 命令创建 Lambda 函数。

   ```
   aws lambda create-function --function-name ProcessDynamoDBRecords \
       --zip-file fileb://function.zip --handler example.handler --runtime nodejs24.x \
       --role arn:aws:iam::111122223333:role/lambda-dynamodb-role
   ```

## 测试 Lambda 函数
<a name="with-dbb-invoke-manually"></a>

在本步骤中，您将使用 `invoke` AWS Lambda CLI 命令和以下示例 DynamoDB 事件手动调用 Lambda 函数。将以下内容复制到名为 `input.txt` 的文件中。

**Example input.txt**  

```
{
   "Records":[
      {
         "eventID":"1",
         "eventName":"INSERT",
         "eventVersion":"1.0",
         "eventSource":"aws:dynamodb",
         "awsRegion":"us-east-1",
         "dynamodb":{
            "Keys":{
               "Id":{
                  "N":"101"
               }
            },
            "NewImage":{
               "Message":{
                  "S":"New item!"
               },
               "Id":{
                  "N":"101"
               }
            },
            "SequenceNumber":"111",
            "SizeBytes":26,
            "StreamViewType":"NEW_AND_OLD_IMAGES"
         },
         "eventSourceARN":"stream-ARN"
      },
      {
         "eventID":"2",
         "eventName":"MODIFY",
         "eventVersion":"1.0",
         "eventSource":"aws:dynamodb",
         "awsRegion":"us-east-1",
         "dynamodb":{
            "Keys":{
               "Id":{
                  "N":"101"
               }
            },
            "NewImage":{
               "Message":{
                  "S":"This item has changed"
               },
               "Id":{
                  "N":"101"
               }
            },
            "OldImage":{
               "Message":{
                  "S":"New item!"
               },
               "Id":{
                  "N":"101"
               }
            },
            "SequenceNumber":"222",
            "SizeBytes":59,
            "StreamViewType":"NEW_AND_OLD_IMAGES"
         },
         "eventSourceARN":"stream-ARN"
      },
      {
         "eventID":"3",
         "eventName":"REMOVE",
         "eventVersion":"1.0",
         "eventSource":"aws:dynamodb",
         "awsRegion":"us-east-1",
         "dynamodb":{
            "Keys":{
               "Id":{
                  "N":"101"
               }
            },
            "OldImage":{
               "Message":{
                  "S":"This item has changed"
               },
               "Id":{
                  "N":"101"
               }
            },
            "SequenceNumber":"333",
            "SizeBytes":38,
            "StreamViewType":"NEW_AND_OLD_IMAGES"
         },
         "eventSourceARN":"stream-ARN"
      }
   ]
}
```

运行以下 `invoke` 命令：

```
aws lambda invoke --function-name ProcessDynamoDBRecords \
    --cli-binary-format raw-in-base64-out \
    --payload file://input.txt outputfile.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)。

函数在响应正文中返回字符串 `message`。

在 `outputfile.txt` 文件中验证输出。

## 创建启用流的 DynamoDB 表
<a name="with-ddb-create-buckets"></a>

创建启用流的 Amazon DynamoDB 表。

**创建 DynamoDB 表**

1. 打开 [DynamoDB 控制台](https://console.aws.amazon.com/dynamodb)。

1. 选择 **Create Table**。

1. 使用以下设置创建表。
   + **Table name**（表名称）- **lambda-dynamodb-stream**
   + **Primary key**（主键）– **id**（字符串）

1. 选择**创建**。

**启用流**

1. 打开 [DynamoDB 控制台](https://console.aws.amazon.com/dynamodb)。

1. 选择**表**。

1. 选择 **lambda-dynamodb-stream** 表。

1. 在 **Exports and streams**（导出和流）下，选择 **DynamoDB stream details**（DynamoDB 流详细信息）。

1. 选择**打开**。

1. 对于**视图类型**，选择**仅键属性**。

1. 选择**开启流**。

记下流 ARN。在下一步中将该流与 Lambda 函数关联时，您将需要此类信息。有关启用流的更多信息，请参阅[使用 DynamoDB Streams 捕获表活动](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Streams.html)。

## 在 AWS Lambda 中添加事件源
<a name="with-ddb-attach-notification-configuration"></a>

在 AWS Lambda 中创建事件源映射。此事件源映射将 DynamoDB Streams 与 Lambda 函数关联。创建此事件源映射后，AWS Lambda 即开始轮询该流。

运行以下 AWS CLI `create-event-source-mapping` 命令。命令运行后，记下 UUID。在任何命令中，如删除事件源映射时，您都需要该 UUID 来引用事件源映射。

```
aws lambda create-event-source-mapping --function-name ProcessDynamoDBRecords \
    --batch-size 100 --starting-position LATEST --event-source DynamoDB-stream-arn
```

 这会在指定的 DynamoDB Streams 和 Lambda 函数之间创建映射。您可将一个 DynamoDB Streams 关联到多个 Lambda 函数，也可将同一个 Lambda 函数关联到多个流。但是，Lambda 函数将共享其所共享的流的读取吞吐量。

您可以通过运行以下命令获取事件源映射的列表。

```
aws lambda list-event-source-mappings
```

该列表返回您创建的所有事件源映射，而对于每个映射，它都显示 `LastProcessingResult` 等信息。该字段用于在出现任何问题时提供信息性消息。`No records processed`（指示 AWS Lambda 未开始轮询或流中没有任何记录）和 `OK`（指示 AWS Lambda 已成功读取流中的记录并已调用 Lambda 函数）等值表示未出现任何问题。如果出现问题，您将收到一条错误消息。

如果您有大量事件源映射，请使用函数名称参数缩窄结果范围。

```
aws lambda list-event-source-mappings --function-name ProcessDynamoDBRecords
```

## 测试设置
<a name="with-ddb-final-integration-test-no-iam"></a>

测试端到端体验。当您更新表时，DynamoDB 会将事件记录写入流。当 AWS Lambda 轮询该流时，它将在流中检测新记录并通过向该函数传递事件来代表您调用 Lambda 函数。

1. 在 DynamoDB 控制台中，添加、更新、删除表中的项目。DynamoDB 会将这些操作记录写入流。

1. AWS Lambda 会轮询该流，当检测到流有更新时，它会通过传递在流中发现的事件数据来调用 Lambda 函数。

1. 函数运行并在 Amazon CloudWatch 中创建日志。您可以验证 Amazon CloudWatch 控制台中报告的日志。

## 后续步骤
<a name="with-ddb-next-steps"></a>

本教程介绍使用 Lambda 处理 DynamoDB 流事件的基础知识。对于生产工作负载，请考虑实施部分批处理响应逻辑，以更有效地处理单个记录故障。Powertools for AWS Lambda 中的[批处理器实用程序](https://docs.powertools.aws.dev/lambda/python/latest/utilities/batch/)适用于 Python、TypeScript、.NET 和 Java，并为此提供了强大的解决方案，可以自动处理部分批处理响应的复杂性并减少成功处理记录的重试次数。

## 清除资源
<a name="cleanup"></a>

除非您想要保留为本教程创建的资源，否则可立即将其删除。通过删除您不再使用的 AWS 资源，可防止您的 AWS 账户 产生不必要的费用。

**删除 Lambda 函数**

1. 打开 Lamba 控制台的 [Functions（函数）页面](https://console.aws.amazon.com/lambda/home#/functions)。

1. 选择您创建的函数。

1. 依次选择**操作**和**删除**。

1. 在文本输入字段中键入 **confirm**，然后选择**删除**。

**删除执行角色**

1. 打开 IAM 控制台的[角色页面](https://console.aws.amazon.com/iam/home#/roles)。

1. 选择您创建的执行角色。

1. 选择**删除**。

1. 在文本输入字段中输入角色名称，然后选择**删除**。

**删除 DynamoDB 表**

1. 打开 DynamoDB 控制台中 [Tables page](https://console.aws.amazon.com//dynamodb/home#tables:)（表页面）。

1. 选择您创建的表。

1. 选择 **Delete**。

1. 在文本框中输入 **delete**。

1. 选择 **删除表**。

# 使用 Lambda 函数处理 Amazon EC2 生命周期事件
<a name="services-ec2"></a>

您可以使用 AWS Lambda 处理来自 Amazon Elastic Compute Cloud 的生命周期事件并管理 Amazon EC2 资源。Amazon EC2 向 [Amazon EventBridge（CloudWatch Events）](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-what-is.html)发送[生命周期事件](https://docs.aws.amazon.com/autoscaling/ec2/userguide/ec2-auto-scaling-lifecycle.html)，例如，当实例更改状态时、当 Amazon Elastic Block Store 卷快照完成时或当计划终止竞价型实例时。您可以配置 EventBridge (CloudWatch Events) 以将这些事件转发到 Lambda 函数来进行处理。

EventBridge (CloudWatch Events) 通过来自 Amazon EC2 的事件文档异步调用 Lambda 函数。

**Example 实例生命周期事件**  

```
{
    "version": "0",
    "id": "b6ba298a-7732-2226-xmpl-976312c1a050",
    "detail-type": "EC2 Instance State-change Notification",
    "source": "aws.ec2",
    "account": "111122223333",
    "time": "2019-10-02T17:59:30Z",
    "region": "us-east-1",
    "resources": [
        "arn:aws:ec2:us-east-1:111122223333:instance/i-0c314xmplcd5b8173"
    ],
    "detail": {
        "instance-id": "i-0c314xmplcd5b8173",
        "state": "running"
    }
}
```

有关配置事件的详细信息，请参阅 [按计划调用 Lambda 函数](with-eventbridge-scheduler.md)。有关处理 Amazon EBS 快照通知的示例函数，请参阅 [EventBridge Scheduler for Amazon EBS](https://docs.aws.amazon.com/ebs/latest/userguide/ebs-cloud-watch-events.html)。

您还可以使用 AWS 开发工具包，通过 Amazon EC2 API 管理实例和其他资源。

## 向 EventBridge（CloudWatch Events）授予权限
<a name="services-ec2-permissions"></a>

要处理来自 Amazon EC2 的生命周期事件，EventBridge（CloudWatch Events）需要权限以调用函数。此权限来自函数的[基于资源的策略](access-control-resource-based.md)。如果您使用 EventBridge (CloudWatch Events) 控制台配置事件触发器，则该控制台将代表您更新基于资源的策略。否则，请添加如下所示的语句：

**Example 基于资源的策略语句，用于 Amazon EC2 生命周期通知**  

```
{
  "Sid": "ec2-events",
  "Effect": "Allow",
  "Principal": {
    "Service": "events.amazonaws.com"
  },
  "Action": "lambda:InvokeFunction",
  "Resource": "arn:aws:lambda:us-east-1:12456789012:function:my-function",
  "Condition": {
    "ArnLike": {
      "AWS:SourceArn": "arn:aws:events:us-east-1:12456789012:rule/*"
    }
  }
}
```

要添加语句，请使用 `add-permission` AWS CLI 命令。

```
aws lambda add-permission --action lambda:InvokeFunction --statement-id ec2-events \
--principal events.amazonaws.com --function-name my-function --source-arn 'arn:aws:events:us-east-1:12456789012:rule/*'
```

如果函数使用 AWS 开发工具包来管理 Amazon EC2 资源，请向函数的[执行角色](lambda-intro-execution-role.md)添加 Amazon EC2 权限。

# 使用 Lambda 处理应用程序负载均衡器请求
<a name="services-alb"></a>

您可以使用 Lambda 函数，处理来自 Application Load Balancer 的请求。Elastic Load Balancing 支持 Lambda 函数作为 Application Load Balancer 的目标。使用负载均衡器规则，基于路径或标头值将 HTTP 请求路由到一个函数。处理请求并从 Lambda 函数返回 HTTP 响应。

Elastic Load Balancing 将使用包含请求体和元数据的事件同步调用 Lambda 函数。

**Example Application Load Balancer 请求事件**  

```
{
    "requestContext": {
        "elb": {
            "targetGroupArn": "arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/lambda-279XGJDqGZ5rsrHC2Fjr/49e9d65c45c6791a"
        }
    },
    "httpMethod": "GET",
    "path": "/lambda",
    "queryStringParameters": {
        "query": "1234ABCD"
    },
    "headers": {
        "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
        "accept-encoding": "gzip",
        "accept-language": "en-US,en;q=0.9",
        "connection": "keep-alive",
        "host": "lambda-alb-123578498.us-east-1.elb.amazonaws.com",
        "upgrade-insecure-requests": "1",
        "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36",
        "x-amzn-trace-id": "Root=1-5c536348-3d683b8b04734faae651f476",
        "x-forwarded-for": "72.12.164.125",
        "x-forwarded-port": "80",
        "x-forwarded-proto": "http",
        "x-imforwards": "20"
    },
    "body": "",
    "isBase64Encoded": False
}
```

您的函数将处理事件并以 JSON 格式向负载均衡器返回响应文档。Elastic Load Balancing 将文档转换为 HTTP 成功或错误响应并将其返回给用户。

**Example 响应文档格式**  

```
{
    "statusCode": 200,
    "statusDescription": "200 OK",
    "isBase64Encoded": False,
    "headers": {
        "Content-Type": "text/html"
    },
    "body": "<h1>Hello from Lambda!</h1>"
}
```

要将 Application Load Balancer 配置为函数触发器，请为 Elastic Load Balancing 授予运行函数的权限、创建向函数发送请求的目标组、并将规则添加到向目标组发送请求的负载均衡器。

使用 `add-permission` 命令将权限语句添加到函数的基于资源的策略。

```
aws lambda add-permission --function-name alb-function \
--statement-id load-balancer --action "lambda:InvokeFunction" \
--principal elasticloadbalancing.amazonaws.com
```

您应看到以下输出：

```
{
    "Statement": "{\"Sid\":\"load-balancer\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"elasticloadbalancing.amazonaws.com\"},\"Action\":\"lambda:InvokeFunction\",\"Resource\":\"arn:aws:lambda:us-west-2:123456789012:function:alb-function\"}"
}
```

有关配置 Application Load Balancer 侦听器和目标组的说明，请参阅 *Application Load Balancer 用户指南*中的 [Lambda 函数目标](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/lambda-functions.html)。

## Powertools for AWS Lambda 中的事件处理程序
<a name="services-alb-powertools"></a>

在编写由应用程序负载均衡器调用的 Lambda 函数时，Powertools for AWS Lambda 工具包中的事件处理程序提供路由、中间件、CORS 配置、OpenAPI 规范生成、请求验证、错误处理和其他有用的功能。事件处理程序实用程序适用于 Python。有关更多信息，请参阅 *Powertools for AWS Lambda (Python) 文档*中的[事件处理程序 REST API](https://docs.powertools.aws.dev/lambda/python/latest/core/event_handler/api_gateway/)。

### Python
<a name="services-alb-powertools-python"></a>

```
import requests
from requests import Response

from aws_lambda_powertools import Logger, Tracer
from aws_lambda_powertools.event_handler import ALBResolver
from aws_lambda_powertools.logging import correlation_paths
from aws_lambda_powertools.utilities.typing import LambdaContext

tracer = Tracer()
logger = Logger()
app = ALBResolver()


@app.get("/todos")
@tracer.capture_method
def get_todos():
    todos: Response = requests.get("https://jsonplaceholder.typicode.com/todos")
    todos.raise_for_status()

    # for brevity, we'll limit to the first 10 only
    return {"todos": todos.json()[:10]}


# You can continue to use other utilities just as before
@logger.inject_lambda_context(correlation_id_path=correlation_paths.APPLICATION_LOAD_BALANCER)
@tracer.capture_lambda_handler
def lambda_handler(event: dict, context: LambdaContext) -> dict:
    return app.resolve(event, context)
```

# 按计划调用 Lambda 函数
<a name="with-eventbridge-scheduler"></a>

[Amazon EventBridge 调度器](https://docs.aws.amazon.com/scheduler/latest/UserGuide/what-is-scheduler.html)是一个无服务器调度器，使您能够从一个中央托管服务创建、运行和管理任务。借助 EventBridge 调度器，您可以使用 cron 和 rate 表达式为定期模式创建计划，也可以配置一次性调用。您可以设置灵活的交付时间窗口、定义重试限制，并为未处理的事件设置最长保留时间。

当您使用 Lambda 设置 EventBridge 调度器时，EventBridge 调度器会异步调用您的 Lambda 函数。本页介绍如何使用 EventBridge 调度器按计划调用 Lambda 函数。

## 设置执行角色
<a name="using-eventbridge-scheduler-execution-role"></a>

 创建新计划时，EventBridge 调度器必须有权代表您调用其目标 API 操作。您可以使用*执行角色*授予 EventBridge 调度器这些权限。您附加到计划执行角色的权限策略定义了所需权限。这些权限取决于您希望 EventBridge 调度器调用的目标 API。

 使用 EventBridge 调度器控制台创建计划时，EventBridge 调度器会根据选择的目标自动设置执行角色，如以下步骤所示。如果您想使用 EventBridge 调度器 SDK（AWS CLI 或 CloudFormation ）之一创建计划，您必须拥有现有的执行角色，以授予 EventBridge 调度器调用目标所需的权限。有关为计划手动设置执行角色的更多信息，请参阅 *EventBridge Scheduler User Guide* 中的 [Setting up an execution role](https://docs.aws.amazon.com/scheduler/latest/UserGuide/setting-up.html#setting-up-execution-role)。

## 创建计划
<a name="using-eventbridge-scheduler-create"></a>

**使用控制台创建计划**

1. 打开 Amazon EventBridge 调度器控制台，网址为：[https://console.aws.amazon.com/scheduler/home](https://console.aws.amazon.com/scheduler/home/)。

1.  在**计划**页面，选择**创建计划**。

1.  在**指定计划详细信息**页面，在**计划名称和描述**部分中，执行以下操作：

   1. 对于**计划名称**，输入计划的名称。例如，**MyTestSchedule**。

   1. （可选）对于**描述**，输入对计划的描述。例如，**My first schedule**。

   1. 对于**计划组**，从下拉列表中选择一个计划组。如果您没有计划组，选择**默认**。要创建计划组，选择**创建自己的计划**。

      您可以使用计划组将标签添加到计划组。

1. 

   1. 选择计划选项。    
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/with-eventbridge-scheduler.html)

1. （可选）如果您在上一步中选择**定期计划**，在**时间范围**部分，请执行以下操作：

   1. 对于**时区**，请选择时区。

   1. 对于**开始日期和时间**，请输入 `YYYY/MM/DD` 格式的有效日期，然后指定 24 小时 `hh:mm` 格式的时间戳。

   1. 对于**结束日期和时间**，请输入 `YYYY/MM/DD` 格式的有效日期，然后指定 24 小时 `hh:mm` 格式的时间戳。

1. 选择**下一步**。

1. 在**选择目标**页面，选择 EventBridge 调度器调用的 AWS API 操作：

   1. 选择 **AWS Lambda 调用**。

   1. 在**调用**部分，选择功能或选择**创建新 Lambda 函数**。

   1. （可选）输入 JSON 有效负载。如果您未输入有效负载，EventBridge 调度器将使用空事件来调用函数。

1. 选择**下一步**。

1. 在 **Settings (设置)** 页面上，执行以下操作：

   1. 要打开计划，在**计划状态**下，切换**启用计划**。

   1. 要为计划配置重试策略，在**重试策略和死信队列（DLQ）**下，请执行以下操作：
      + 切换**重试**。
      + 对于**事件的最长期限**，输入 EventBridge 调度器必须保留未处理事件的最长**小时**和**分钟**数。
      + 最长时间为 24 小时。
      + 对于**最大重试次数**，输入在目标返回错误的情况下，EventBridge  调度器重试计划的最大次数。

         最大值为 185 次重试。

      配置重试策略后，如果计划未能调用其目标，EventBridge 调度器将重新运行该计划。如果已配置，则必须为计划设置最长保留时间和最大重试次数。

   1. 选择 EventBridge 调度器存储未送达事件的位置。    
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/with-eventbridge-scheduler.html)

   1. 要使用客户托管密钥加密目标输入，在**加密**下，选择**自定义加密设置（高级）**。

      如果选择此选项，请输入现有的 KMS 密钥 ARN 或选择**创建一个 AWS KMS key** 以导航到 AWS KMS 控制台。有关 EventBridge 调度器如何加密静态数据的更多信息，请参阅 *Amazon EventBridge Scheduler User Guide* 中的 [Encryption at rest](https://docs.aws.amazon.com/scheduler/latest/UserGuide/encryption-rest.html)。

   1. 要让 EventBridge 调度器为您创建新的执行角色，请选择**为此计划创建新角色**。然后，在**角色名称**中输入名称。如果您选择此选项，EventBridge 调度器会将模板化目标所需的必要权限附加到该角色。

1. 选择**下一步**。

1.  在**查看并创建计划**页面上，查看计划的详细信息。在每个部分中，选择**编辑**返回到该步骤并编辑其详细信息。

1. 选择**创建计划**。

   您可以在**计划**页面上查看新的和现有的计划列表。在**状态**列下，验证新计划是否**已启用**。

要确认 EventBridge 调度器是否调用了该函数，[查看该函数的 Amazon CloudWatch Logs](monitoring-cloudwatchlogs-view.md#monitoring-cloudwatchlogs-console)。

## 相关资源
<a name="using-eventbridge-scheduler-related-resources"></a>

 有关 EventBridge 调度器的详细信息，请参阅以下内容：
+ [EventBridge Scheduler User Guide](https://docs.aws.amazon.com/scheduler/latest/UserGuide/what-is-scheduler.html)
+ [EventBridge Scheduler API Reference](https://docs.aws.amazon.com/scheduler/latest/APIReference/Welcome.html)
+ [EventBridge Scheduler Pricing](https://aws.amazon.com/eventbridge/pricing/#Scheduler)

# 配合使用 AWS Lambda 和 AWS IoT
<a name="services-iot"></a>

AWS IoT 提供连接 Internet 的设备（如传感器）与 AWS 云之间的安全通信。这让您能够从多个设备收集、存储和分析遥测数据。

您可以为设备创建 AWS IoT 规则，以便与 AWS 服务 进行交互。AWS IoT [规则引擎](https://docs.aws.amazon.com/iot/latest/developerguide/iot-rules.html)提供基于 SQL 的语言，用于从消息负载中选择数据，并将数据发送到其他服务，例如 Amazon S3、Amazon DynamoDB 和 AWS Lambda。当您想要调用其他AWS服务或第三方服务时，您可以定义规则以调用 Lambda 函数。

当传入的 IoT 消息触发规则时，AWS IoT 会[异步](invocation-async.md)调用 Lambda 函数并将数据从 IoT 消息传递到函数。

以下示例显示了温室传感器的湿度读数。**row** 和 **pos** 值标识传感器的位置。此示例事件基于 [AWS IoT 规则教程](https://docs.aws.amazon.com/iot/latest/developerguide/iot-rules-tutorial.html)中的温室类型。

**Example AWS IoT 消息事件**  

```
{
    "row" : "10",
    "pos" : "23",
    "moisture" : "75"
}
```

对于异步调用，Lambda 会对消息排队并在函数返回错误时[重试](invocation-retries.md)。通过 [destination](invocation-async-retain-records.md#invocation-async-destinations) 配置函数，保留函数无法处理的事件。

您需要向 AWS IoT 服务授予调用 Lambda 函数的权限。使用 `add-permission` 命令将权限语句添加到函数的基于资源的策略。

```
aws lambda add-permission --function-name my-function \
--statement-id iot-events --action "lambda:InvokeFunction" --principal iot.amazonaws.com
```

您应看到以下输出：

```
{
    "Statement": "{\"Sid\":\"iot-events\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"iot.amazonaws.com\"},\"Action\":\"lambda:InvokeFunction\",\"Resource\":\"arn:aws:lambda:us-east-1:123456789012:function:my-function\"}"
}
```

有关如何将 AWS IoT 与 Lambda 结合使用的更多信息，请参阅[创建 AWS Lambda 规则](https://docs.aws.amazon.com/iot/latest/developerguide/iot-lambda-rule.html)。

# 使用 Lambda 处理来自 Amazon Kinesis Data Streams 的记录
<a name="with-kinesis"></a>

您可以使用 Lambda 函数来处理 [Amazon Kinesis 数据流](https://docs.aws.amazon.com/streams/latest/dev/introduction.html)中的记录。您可以将 Lambda 函数映射到 Kinesis Data Streams 共享吞吐量使用者（标准迭代器）或具有[增强型扇出功能](https://docs.aws.amazon.com/kinesis/latest/dev/enhanced-consumers.html)的专用吞吐量使用者。对于标准迭代器，Lambda 使用 HTTP 协议轮询 Kinesis 流中的每个分片以查找记录。事件源映射与分片的其他使用者共享读取吞吐量。

 有关 Kinesis 数据流的详细信息，请参阅[读取 Amazon Kinesis Data Streams 中的数据](https://docs.aws.amazon.com/kinesis/latest/dev/building-consumers.html)。

**注意**  
Kinesis 按每个分区收费；对于增强型扇出功能，从流中读取数据。有关定价详细信息，请参阅 [Amazon Kinesis 定价](https://aws.amazon.com/kinesis/data-streams/pricing)。

## 轮询和批处理流
<a name="kinesis-polling-and-batching"></a>

Lambda 从数据流中读取记录并[同步](invocation-sync.md)调用您的函数，带有一个包含流记录的事件。Lambda 分批读取记录并调用您的函数来处理批处理中的记录。每个批处理包含来自单个分区/数据流的记录。

您的 Lambda 函数是数据流的用户应用程序。对于每个分片，它一次处理一批记录。您可以将 Lambda 函数映射到共享吞吐量使用者（标准迭代器）或具有增强扇出功能的专用吞吐量使用者。
+ **标准迭代器：**Lambda 将针对记录轮询 Kinesis 流中的每个分片（按照每秒一次的基本频率）。当有更多记录可用时，Lambda 会继续进行批处理，直到函数赶上流的速度。事件源映射与分片的其他使用者共享读取吞吐量。
+ **增强型扇出功能：**为了最大限度地减少延迟并最大限度地提高读取吞吐量，请创建具有[增强型扇出功能](https://docs.aws.amazon.com/streams/latest/dev/enhanced-consumers.html)的数据流使用者。增强扇出功能使用者将获得与每个分片的专用连接，这不会影响从流中读取信息的其他应用程序。流使用者使用 HTTP/2 通过长期连接将记录推送到 Lambda 并压缩请求头来减少延迟。您可以使用 Kinesis [RegisterStreamConsumer](https://docs.aws.amazon.com/kinesis/latest/APIReference/API_RegisterStreamConsumer.html) API 创建流使用者。

```
aws kinesis register-stream-consumer \
--consumer-name con1 \
--stream-arn arn:aws:kinesis:us-east-2:123456789012:stream/lambda-stream
```

您应看到以下输出：

```
{
    "Consumer": {
        "ConsumerName": "con1",
        "ConsumerARN": "arn:aws:kinesis:us-east-2:123456789012:stream/lambda-stream/consumer/con1:1540591608",
        "ConsumerStatus": "CREATING",
        "ConsumerCreationTimestamp": 1540591608.0
    }
}
```

要提高函数处理记录的速度，[请将分片添加到数据流中](https://repost.aws/knowledge-center/kinesis-data-streams-open-shards)。Lambda 按顺序处理各个分区中的记录。如果您的函数返回错误，它会停止处理分片中的其他记录。使用更多分片，可以同时处理更多批次，从而降低错误对并发性的影响。

如果您的函数无法扩展以处理并发批处理的总数，请为您的函数[请求提高配额](https://docs.aws.amazon.com/servicequotas/latest/userguide/request-quota-increase.html)或[预留并发](configuration-concurrency.md)。

默认情况下，Lambda 会在记录可用时尽快调用您的函数。如果 Lambda 从事件源中读取的批处理只有一条记录，则 Lambda 将会只向该函数发送一条记录。为避免在记录数量较少的情况下调用该函数，您可以配置 *batching window*（批处理时段），让事件源缓冲最多五分钟的记录。调用函数前，Lambda 会持续从事件源中读取记录，直到收集完整批处理、批处理时段到期或批处理达到 6MB 的有效负载时为止。有关更多信息，请参阅[批处理行为](invocation-eventsourcemapping.md#invocation-eventsourcemapping-batching)。

**警告**  
Lambda 事件源映射至少处理每个事件一次，有可能出现重复处理记录的情况。为避免与重复事件相关的潜在问题，我们强烈建议您将函数代码设为幂等性。要了解更多信息，请参阅 AWS 知识中心的[如何让我的 Lambda 函数保持幂等性](https://repost.aws/knowledge-center/lambda-function-idempotent)。

Lambda 在发送下次批处理之前不会等待任何配置的[扩展](lambda-extensions.md)完成。换句话说，扩展可能会在 Lambda 处理下一批记录时继续运行。如果您违反了账户的任何[并发](lambda-concurrency.md)设置或限制，可能会导致节流问题。要检测这是否是潜在问题，请监控函数并检查所显示的[并发指标](monitoring-concurrency.md#general-concurrency-metrics)是否高于事件源映射的预期。由于调用间隔时间较短，Lambda 可能会短暂报告高于分片数量的并发使用量。即使对于没有扩展名的 Lambda 函数也是如此。

配置 [ParallelizationFactor](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html#lambda-CreateEventSourceMapping-request-ParallelizationFactor) 设置以同时使用多个 Lambda 调用处理 Kinesis 数据流的一个分片。您可以指定 Lambda 通过从 1（默认值）到 10 的并行化因子从分区中轮询的并发批次数。例如，假设您将 `ParallelizationFactor` 设置为 2，则最多可以有 200 个并发 Lambda 调用来处理 100 个 Kinesis 数据分片（但您可能实际上会看到不同的 `ConcurrentExecutions` 指标值）。这有助于在数据量不稳定并且 `IteratorAge` 较高时纵向扩展处理吞吐量。增加每个分片的并发批次数后，Lambda 仍然可以确保分区密钥级别的顺序处理。

您还可以将 `ParallelizationFactor` 与 Kinesis 聚合一起使用。事件源映射的行为取决于您是否使用[增强型扇出功能](https://docs.aws.amazon.com/streams/latest/dev/enhanced-consumers.html)：
+ **如果没有增强型扇出功能**：聚合事件中的所有事件都必须具有相同的分区键。该分区键还必须与聚合事件的分区键相匹配。如果聚合事件中的事件具有不同的分区键，则 Lambda 无法保证按分区键依照顺序处理事件。
+ **借助增强型扇出功能**：首先，Lambda 将聚合的事件解码为其单个事件。聚合事件可以具有与其包含的事件不同的分区键。但是，与分区键不对应的事件会被[丢弃并丢失](https://github.com/awslabs/kinesis-aggregation/blob/master/potential_data_loss.md)。Lambda 不处理这些事件，也不会将它们发送到配置的失败目标。

## 示例事件
<a name="services-kinesis-event-example"></a>

**Example**  

```
{
    "Records": [
        {
            "kinesis": {
                "kinesisSchemaVersion": "1.0",
                "partitionKey": "1",
                "sequenceNumber": "49590338271490256608559692538361571095921575989136588898",
                "data": "SGVsbG8sIHRoaXMgaXMgYSB0ZXN0Lg==",
                "approximateArrivalTimestamp": 1545084650.987
            },
            "eventSource": "aws:kinesis",
            "eventVersion": "1.0",
            "eventID": "shardId-000000000006:49590338271490256608559692538361571095921575989136588898",
            "eventName": "aws:kinesis:record",
            "invokeIdentityArn": "arn:aws:iam::123456789012:role/lambda-role",
            "awsRegion": "us-east-2",
            "eventSourceARN": "arn:aws:kinesis:us-east-2:123456789012:stream/lambda-stream"
        },
        {
            "kinesis": {
                "kinesisSchemaVersion": "1.0",
                "partitionKey": "1",
                "sequenceNumber": "49590338271490256608559692540925702759324208523137515618",
                "data": "VGhpcyBpcyBvbmx5IGEgdGVzdC4=",
                "approximateArrivalTimestamp": 1545084711.166
            },
            "eventSource": "aws:kinesis",
            "eventVersion": "1.0",
            "eventID": "shardId-000000000006:49590338271490256608559692540925702759324208523137515618",
            "eventName": "aws:kinesis:record",
            "invokeIdentityArn": "arn:aws:iam::123456789012:role/lambda-role",
            "awsRegion": "us-east-2",
            "eventSourceARN": "arn:aws:kinesis:us-east-2:123456789012:stream/lambda-stream"
        }
    ]
}
```

# 使用 Lambda 处理 Amazon Kinesis Data Streams 记录
<a name="services-kinesis-create"></a>

要使用 Lambda 处理 Amazon Kinesis Data Streams 记录，可创建 Lambda 事件源映射。您可以将 Lambda 函数映射到标准迭代器或增强型扇出功能使用者。有关更多信息，请参阅 [轮询和批处理流](with-kinesis.md#kinesis-polling-and-batching)。

## 创建 Kinesis 事件源映射
<a name="services-kinesis-eventsourcemapping"></a>

要使用来自数据流的记录调用 Lambda 函数，请创建一个[事件源映射](invocation-eventsourcemapping.md)。您可以创建多个事件源映射，以使用多个 Lambda 函数处理相同的数据，或使用单个函数处理来自多个数据流的项目。处理来自多个流的项目时，每个批处理将只包含来自单个分片或流的记录。

您可以配置事件源映射来处理来自不同 AWS 账户中的流的记录。要了解更多信息，请参阅[创建跨账户事件源映射](#services-kinesis-eventsourcemapping-cross-account)。

在创建事件源映射之前，您需要向您的 Lambda 函数授予读取 Kinesis 数据流中数据的权限。Lambda 需要以下权限才能管理与您的 Kinesis 数据流相关的资源：
+ [kinesis:DescribeStream](https://docs.aws.amazon.com/lambda/latest/api/API_DescribeStream.html)
+ [kinesis:DescribeStreamSummary](https://docs.aws.amazon.com/lambda/latest/api/API_DescribeStreamSummary.html)
+ [kinesis:GetRecords](https://docs.aws.amazon.com/lambda/latest/api/API_GetRecords.html)
+ [kinesis:GetShardIterator](https://docs.aws.amazon.com/lambda/latest/api/API_GetShardIterator.html)
+ [kinesis:ListShards](https://docs.aws.amazon.com/lambda/latest/api/API_ListShards.html)
+ [kinesis:SubscribeToShard](https://docs.aws.amazon.com/lambda/latest/api/API_SubscribeToShard.html)

AWS 托管式策略 [AWSLambdaKinesisExecutionRole](https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AWSLambdaKinesisExecutionRole.html) 包含这些权限。按照以下过程所述将此托管式策略添加到您的函数。

**注意**  
您不需要 `kinesis:ListStreams` 权限来创建和管理 Kinesis 的事件源映射。但是，如果您在控制台中创建事件源映射但没有此权限，则无法从下拉列表中选择 Kinesis 流，并且控制台将显示错误。要创建事件源映射，您需要手动输入流的 Amazon 资源名称（ARN）。
Lambda 在重试失败的调用时会进行 `kinesis:GetRecords` 和 `kinesis:GetShardIterator` API 调用。

------
#### [ AWS 管理控制台 ]

**为您的函数添加 Kinesis 权限**

1. 打开 Lambda 控制台的[“函数”页面](https://console.aws.amazon.com/lambda/home#/functions)，然后选择函数。

1. 在**配置**选项卡中，选择**权限**。

1. 在**执行角色**窗格的**角色名称**下，选择指向函数的执行角色的链接。此链接将在 IAM 控制台中打开该角色的页面。

1. 在**权限策略**窗格中，选择**添加权限**，然后选择**附加策略**。

1. 在搜索字段中输入 **AWSLambdaKinesisExecutionRole**。

1. 选中该策略名称旁边的复选框，然后选择**添加权限**。

------
#### [ AWS CLI ]

**为您的函数添加 Kinesis 权限**
+ 运行以下 CLI 命令，以将 `AWSLambdaKinesisExecutionRole` 策略附加到函数的执行角色。

  ```
  aws iam attach-role-policy \
  --role-name MyFunctionRole \
  --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaKinesisExecutionRole
  ```

------
#### [ AWS SAM ]

**为您的函数添加 Kinesis 权限**
+ 在函数定义中添加 `Policies` 属性，如以下示例所示：

  ```
  Resources:
    MyFunction:
      Type: AWS::Serverless::Function
      Properties:
        CodeUri: ./my-function/
        Handler: index.handler
        Runtime: nodejs24.x
        Policies:
          - AWSLambdaKinesisExecutionRole
  ```

------

配置所需的权限后，创建事件源映射。

------
#### [ AWS 管理控制台 ]

**创建 Kinesis 事件源映射**

1. 打开 Lambda 控制台的[“函数”页面](https://console.aws.amazon.com/lambda/home#/functions)，然后选择函数。

1. 在**函数概述**窗格中，选择**添加触发器**。

1. 在**触发器配置**下，对于源，请选择 ** Kinesis **。

1. 选择要为其创建事件源映射的 Kinesis 流，也可以选择流的使用者。

1. （可选）编辑事件源映射的**批处理大小**、**起始位置**和**批处理窗口**。

1. 选择**添加**。

在控制台中创建事件源映射时，您的 IAM 角色必须拥有 [kinesis:ListStreams](https://docs.aws.amazon.com/lambda/latest/api/API_ListStreams.html) 和 [kinesis:ListStreamConsumers](https://docs.aws.amazon.com/lambda/latest/api/API_ListStreamConsumers.html) 权限。

------
#### [ AWS CLI ]

**创建 Kinesis 事件源映射**
+ 运行以下 CLI 命令以创建 Kinesis 事件源映射。根据您的应用场景选择自己的批量大小和起始位置。

  ```
  aws lambda create-event-source-mapping \
  --function-name MyFunction \
  --event-source-arn arn:aws:kinesis:us-east-2:123456789012:stream/lambda-stream \
  --starting-position LATEST \
  --batch-size 100
  ```

要指定批处理时间窗，请添加 `--maximum-batching-window-in-seconds` 选项。有关使用此参数和其他参数的更多信息，请参阅《AWS CLI Command Reference》中的 [create-event-source-mapping](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/create-event-source-mapping.html)**。

------
#### [ AWS SAM ]

**创建 Kinesis 事件源映射**
+ 在函数定义中添加 `KinesisEvent` 属性，如以下示例所示：

  ```
  Resources:
    MyFunction:
      Type: AWS::Serverless::Function
      Properties:
        CodeUri: ./my-function/
        Handler: index.handler
        Runtime: nodejs24.x
        Policies:
          - AWSLambdaKinesisExecutionRole
        Events:
          KinesisEvent:
            Type: Kinesis
            Properties:
              Stream: !GetAtt MyKinesisStream.Arn
              StartingPosition: LATEST
              BatchSize: 100
  
    MyKinesisStream:
      Type: AWS::Kinesis::Stream
      Properties:
        ShardCount: 1
  ```

要了解有关在 AWS SAM 中创建 Kinesis 数据流事件源映射的更多信息，请参阅《AWS Serverless Application Model Developer Guide》中的 [Kinesis](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-kinesis.html)**。

------

## 轮询和流的起始位置
<a name="services-kinesis-stream-start-pos"></a>

请注意，事件源映射创建和更新期间的流轮询最终是一致的。
+ 在事件源映射创建期间，可能需要几分钟才能开始轮询来自流的事件。
+ 在事件源映射更新期间，可能需要几分钟才能停止和重新开始轮询来自流的事件。

此行为意味着，如果你指定 `LATEST` 作为流的起始位置，事件源映射可能会在创建或更新期间错过事件。为确保不会错过任何事件，请将流的起始位置指定为 `TRIM_HORIZON` 或 `AT_TIMESTAMP`。

## 创建跨账户事件源映射
<a name="services-kinesis-eventsourcemapping-cross-account"></a>

Amazon Kinesis Data Streams 支持[基于资源的策略](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_identity-vs-resource.html)。因此，您可以在一个 AWS 账户中使用 Lambda 函数来处理另一个账户的流中摄入的数据。

要使用其他 AWS 账户中的 Kinesis 流为您的 Lambda 函数创建事件源映射，您必须使用基于资源的策略配置该流，以向您的 Lambda 函数授予读取相关项目的权限。要了解如何配置流以允许跨账户存取，请参阅《Amazon Kinesis Streams 开发人员指南》中的 [Sharing access with cross-account AWS Lambda functions](https://docs.aws.amazon.com/streams/latest/dev/resource-based-policy-examples.html#Resource-based-policy-examples-lambda)**。

使用基于资源的策略配置流以向您的 Lambda 函数授予所需的权限后，请使用上一节中描述的任何方法创建事件源映射。

如果您选择使用 Lambda 控制台创建事件源映射，请将流的 ARN 直接粘贴到输入字段中。如果您想指定流的使用者，粘贴使用者的 ARN 会自动填充流字段。

# 使用 Kinesis Data Streams 和 Lambda 配置部分批次响应
<a name="services-kinesis-batchfailurereporting"></a>

在使用和处理来自事件源的流式数据时，默认情况下，Lambda 仅在批处理完全成功时，才会在批次的最高序列号处设置检查点。Lambda 会将所有其他结果视为完全失败并重试批处理，直至达到重试次数上限。要允许在处理来自流的批次时部分成功，请开启 `ReportBatchItemFailures`。允许部分成功有助于减少对记录重试的次数，尽管这并不能完全阻止在成功记录中重试的可能性。

要开启 `ReportBatchItemFailures`，请在 [FunctionResponseTypes](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html#lambda-CreateEventSourceMapping-request-FunctionResponseTypes) 列表中包含枚举值 **ReportBatchItemFailures**。此列表指示为函数启用了哪些响应类型。您可以在[创建](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html)或[更新](https://docs.aws.amazon.com/lambda/latest/api/API_UpdateEventSourceMapping.html)事件源映射时配置此列表。

**注意**  
即使函数代码返回部分批处理失败响应，除非为事件源映射显式启用 `ReportBatchItemFailures` 功能，否则 Lambda 也不会处理这些响应。

## 报告语法
<a name="streams-batchfailurereporting-syntax"></a>

配置批处理项目失败的报告时，将返回 `StreamsEventResponse` 类，其中包含批处理项目失败列表。您可以使用 `StreamsEventResponse` 对象返回批处理中第一个失败记录的序列号。您还可以使用正确的响应语法来创建自己的自定义类。以下 JSON 结构显示了所需的响应语法：

```
{ 
  "batchItemFailures": [ 
        {
            "itemIdentifier": "<SequenceNumber>"
        }
    ]
}
```

**注意**  
如果 `batchItemFailures` 数组包含多个项目，Lambda 会使用序列号最小的记录作为检查点。然后，Lambda 会重试从该检查点开始的所有记录。

## 成功和失败的条件
<a name="streams-batchfailurereporting-conditions"></a>

如果返回以下任意一项，则 Lambda 会将批处理视为完全成功：
+ 空的 `batchItemFailure` 列表
+ Null `batchItemFailure` 列表
+ 空的 `EventResponse`
+ Null `EventResponse`

如果返回以下任何一项，则 Lambda 会将批处理视为完全失败：
+ 空字符串 `itemIdentifier`
+ Null `itemIdentifier`
+ 包含错误密钥名的 `itemIdentifier`

Lambda 会根据您的重试策略在失败时重试。

## 将批次一分为二
<a name="streams-batchfailurereporting-bisect"></a>

如果调用失败并且已开启 `BisectBatchOnFunctionError`，则无论您的 `ReportBatchItemFailures` 设置如何，批次都将一分为二。

当收到批处理部分成功响应且同时开启 `BisectBatchOnFunctionError` 和 `ReportBatchItemFailures` 时，批次将在返回的序列号处一分为二，并且 Lambda 将仅重试剩余记录。

为了简化部分批处理响应逻辑的实现，请考虑使用 Powertools for AWS Lambda 中的[批处理器实用程序](https://docs.powertools.aws.dev/lambda/python/latest/utilities/batch/)，它可以自动为您处理这些复杂问题。

以下函数代码示例将返回批处理中处理失败消息的 ID 列表：

------
#### [ .NET ]

**适用于 .NET 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-kinesis-to-lambda-with-batch-item-handling)存储库中查找完整示例，并了解如何进行设置和运行。
报告使用 .NET 进行 Lambda Kinesis 批处理项目失败。  

```
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
﻿using System.Text;
using System.Text.Json.Serialization;
using Amazon.Lambda.Core;
using Amazon.Lambda.KinesisEvents;
using AWS.Lambda.Powertools.Logging;

// Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]

namespace KinesisIntegration;

public class Function
{
    // Powertools Logger requires an environment variables against your function
    // POWERTOOLS_SERVICE_NAME
    [Logging(LogEvent = true)]
    public async Task<StreamsEventResponse> FunctionHandler(KinesisEvent evnt, ILambdaContext context)
    {
        if (evnt.Records.Count == 0)
        {
            Logger.LogInformation("Empty Kinesis Event received");
            return new StreamsEventResponse();
        }

        foreach (var record in evnt.Records)
        {
            try
            {
                Logger.LogInformation($"Processed Event with EventId: {record.EventId}");
                string data = await GetRecordDataAsync(record.Kinesis, context);
                Logger.LogInformation($"Data: {data}");
                // TODO: Do interesting work based on the new data
            }
            catch (Exception ex)
            {
                Logger.LogError($"An error occurred {ex.Message}");
                /* Since we are working with streams, we can return the failed item immediately.
                   Lambda will immediately begin to retry processing from this failed item onwards. */
                return new StreamsEventResponse
                {
                    BatchItemFailures = new List<StreamsEventResponse.BatchItemFailure>
                    {
                        new StreamsEventResponse.BatchItemFailure { ItemIdentifier = record.Kinesis.SequenceNumber }
                    }
                };
            }
        }
        Logger.LogInformation($"Successfully processed {evnt.Records.Count} records.");
        return new StreamsEventResponse();
    }

    private async Task<string> GetRecordDataAsync(KinesisEvent.Record record, ILambdaContext context)
    {
        byte[] bytes = record.Data.ToArray();
        string data = Encoding.UTF8.GetString(bytes);
        await Task.CompletedTask; //Placeholder for actual async work
        return data;
    }
}

public class StreamsEventResponse
{
    [JsonPropertyName("batchItemFailures")]
    public IList<BatchItemFailure> BatchItemFailures { get; set; }
    public class BatchItemFailure
    {
        [JsonPropertyName("itemIdentifier")]
        public string ItemIdentifier { get; set; }
    }
}
```

------
#### [ Go ]

**适用于 Go 的 SDK V2**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-kinesis-to-lambda-with-batch-item-handling)存储库中查找完整示例，并了解如何进行设置和运行。
报告通过 Go 进行 Lambda Kinesis 批处理项目失败。  

```
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package main

import (
	"context"
	"fmt"
	"github.com/aws/aws-lambda-go/events"
	"github.com/aws/aws-lambda-go/lambda"
)

func handler(ctx context.Context, kinesisEvent events.KinesisEvent) (map[string]interface{}, error) {
	batchItemFailures := []map[string]interface{}{}

	for _, record := range kinesisEvent.Records {
		curRecordSequenceNumber := ""

		// Process your record
		if /* Your record processing condition here */ {
			curRecordSequenceNumber = record.Kinesis.SequenceNumber
		}

		// Add a condition to check if the record processing failed
		if curRecordSequenceNumber != "" {
			batchItemFailures = append(batchItemFailures, map[string]interface{}{"itemIdentifier": curRecordSequenceNumber})
		}
	}

	kinesisBatchResponse := map[string]interface{}{
		"batchItemFailures": batchItemFailures,
	}
	return kinesisBatchResponse, nil
}

func main() {
	lambda.Start(handler)
}
```

------
#### [ Java ]

**适用于 Java 的 SDK 2.x**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-kinesis-to-lambda-with-batch-item-handling)存储库中查找完整示例，并了解如何进行设置和运行。
报告使用 Java 进行 Lambda Kinesis 批处理项目失败。  

```
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.KinesisEvent;
import com.amazonaws.services.lambda.runtime.events.StreamsEventResponse;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

public class ProcessKinesisRecords implements RequestHandler<KinesisEvent, StreamsEventResponse> {

    @Override
    public StreamsEventResponse handleRequest(KinesisEvent input, Context context) {

        List<StreamsEventResponse.BatchItemFailure> batchItemFailures = new ArrayList<>();
        String curRecordSequenceNumber = "";

        for (KinesisEvent.KinesisEventRecord kinesisEventRecord : input.getRecords()) {
            try {
                //Process your record
                KinesisEvent.Record kinesisRecord = kinesisEventRecord.getKinesis();
                curRecordSequenceNumber = kinesisRecord.getSequenceNumber();

            } catch (Exception e) {
                /* Since we are working with streams, we can return the failed item immediately.
                   Lambda will immediately begin to retry processing from this failed item onwards. */
                batchItemFailures.add(new StreamsEventResponse.BatchItemFailure(curRecordSequenceNumber));
                return new StreamsEventResponse(batchItemFailures);
            }
        }
       
       return new StreamsEventResponse(batchItemFailures);   
    }
}
```

------
#### [ JavaScript ]

**SDK for JavaScript（v3）**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/blob/main/integration-kinesis-to-lambda-with-batch-item-handling)存储库中查找完整示例，并了解如何进行设置和运行。
报告使用 Javascript 进行 Lambda Kinesis 批处理项目失败。  

```
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
exports.handler = async (event, context) => {
  for (const record of event.Records) {
    try {
      console.log(`Processed Kinesis Event - EventID: ${record.eventID}`);
      const recordData = await getRecordDataAsync(record.kinesis);
      console.log(`Record Data: ${recordData}`);
      // TODO: Do interesting work based on the new data
    } catch (err) {
      console.error(`An error occurred ${err}`);
      /* Since we are working with streams, we can return the failed item immediately.
            Lambda will immediately begin to retry processing from this failed item onwards. */
      return {
        batchItemFailures: [{ itemIdentifier: record.kinesis.sequenceNumber }],
      };
    }
  }
  console.log(`Successfully processed ${event.Records.length} records.`);
  return { batchItemFailures: [] };
};

async function getRecordDataAsync(payload) {
  var data = Buffer.from(payload.data, "base64").toString("utf-8");
  await Promise.resolve(1); //Placeholder for actual async work
  return data;
}
```
报告使用 TypeScript 进行 Lambda Kinesis 批处理项目失败。  

```
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import {
  KinesisStreamEvent,
  Context,
  KinesisStreamHandler,
  KinesisStreamRecordPayload,
  KinesisStreamBatchResponse,
} from "aws-lambda";
import { Buffer } from "buffer";
import { Logger } from "@aws-lambda-powertools/logger";

const logger = new Logger({
  logLevel: "INFO",
  serviceName: "kinesis-stream-handler-sample",
});

export const functionHandler: KinesisStreamHandler = async (
  event: KinesisStreamEvent,
  context: Context
): Promise<KinesisStreamBatchResponse> => {
  for (const record of event.Records) {
    try {
      logger.info(`Processed Kinesis Event - EventID: ${record.eventID}`);
      const recordData = await getRecordDataAsync(record.kinesis);
      logger.info(`Record Data: ${recordData}`);
      // TODO: Do interesting work based on the new data
    } catch (err) {
      logger.error(`An error occurred ${err}`);
      /* Since we are working with streams, we can return the failed item immediately.
            Lambda will immediately begin to retry processing from this failed item onwards. */
      return {
        batchItemFailures: [{ itemIdentifier: record.kinesis.sequenceNumber }],
      };
    }
  }
  logger.info(`Successfully processed ${event.Records.length} records.`);
  return { batchItemFailures: [] };
};

async function getRecordDataAsync(
  payload: KinesisStreamRecordPayload
): Promise<string> {
  var data = Buffer.from(payload.data, "base64").toString("utf-8");
  await Promise.resolve(1); //Placeholder for actual async work
  return data;
}
```

------
#### [ PHP ]

**适用于 PHP 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-kinesis-to-lambda-with-batch-item-handling)存储库中查找完整示例，并了解如何进行设置和运行。
报告通过 PHP 进行 Lambda Kinesis 批处理项目失败。  

```
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
<?php

# using bref/bref and bref/logger for simplicity

use Bref\Context\Context;
use Bref\Event\Kinesis\KinesisEvent;
use Bref\Event\Handler as StdHandler;
use Bref\Logger\StderrLogger;

require __DIR__ . '/vendor/autoload.php';

class Handler implements StdHandler
{
    private StderrLogger $logger;
    public function __construct(StderrLogger $logger)
    {
        $this->logger = $logger;
    }

    /**
     * @throws JsonException
     * @throws \Bref\Event\InvalidLambdaEvent
     */
    public function handle(mixed $event, Context $context): array
    {
        $kinesisEvent = new KinesisEvent($event);
        $this->logger->info("Processing records");
        $records = $kinesisEvent->getRecords();

        $failedRecords = [];
        foreach ($records as $record) {
            try {
                $data = $record->getData();
                $this->logger->info(json_encode($data));
                // TODO: Do interesting work based on the new data
            } catch (Exception $e) {
                $this->logger->error($e->getMessage());
                // failed processing the record
                $failedRecords[] = $record->getSequenceNumber();
            }
        }
        $totalRecords = count($records);
        $this->logger->info("Successfully processed $totalRecords records");

        // change format for the response
        $failures = array_map(
            fn(string $sequenceNumber) => ['itemIdentifier' => $sequenceNumber],
            $failedRecords
        );

        return [
            'batchItemFailures' => $failures
        ];
    }
}

$logger = new StderrLogger();
return new Handler($logger);
```

------
#### [ Python ]

**适用于 Python 的 SDK（Boto3）**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-kinesis-to-lambda-with-batch-item-handling)存储库中查找完整示例，并了解如何进行设置和运行。
报告使用 Python 进行 Lambda Kinesis 批处理项目失败。  

```
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
def handler(event, context):
    records = event.get("Records")
    curRecordSequenceNumber = ""
    
    for record in records:
        try:
            # Process your record
            curRecordSequenceNumber = record["kinesis"]["sequenceNumber"]
        except Exception as e:
            # Return failed record's sequence number
            return {"batchItemFailures":[{"itemIdentifier": curRecordSequenceNumber}]}

    return {"batchItemFailures":[]}
```

------
#### [ Ruby ]

**适用于 Ruby 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-kinesis-to-lambda-with-batch-item-handling)存储库中查找完整示例，并了解如何进行设置和运行。
报告通过 Ruby 进行 Lambda Kinesis 批处理项目失败。  

```
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
require 'aws-sdk'

def lambda_handler(event:, context:)
  batch_item_failures = []

  event['Records'].each do |record|
    begin
      puts "Processed Kinesis Event - EventID: #{record['eventID']}"
      record_data = get_record_data_async(record['kinesis'])
      puts "Record Data: #{record_data}"
      # TODO: Do interesting work based on the new data
    rescue StandardError => err
      puts "An error occurred #{err}"
      # Since we are working with streams, we can return the failed item immediately.
      # Lambda will immediately begin to retry processing from this failed item onwards.
      return { batchItemFailures: [{ itemIdentifier: record['kinesis']['sequenceNumber'] }] }
    end
  end

  puts "Successfully processed #{event['Records'].length} records."
  { batchItemFailures: batch_item_failures }
end

def get_record_data_async(payload)
  data = Base64.decode64(payload['data']).force_encoding('utf-8')
  # Placeholder for actual async work
  sleep(1)
  data
end
```

------
#### [ Rust ]

**适用于 Rust 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-kinesis-to-lambda-with-batch-item-handling)存储库中查找完整示例，并了解如何进行设置和运行。
报告通过 Rust 进行 Lambda Kinesis 批处理项目失败。  

```
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
use aws_lambda_events::{
    event::kinesis::KinesisEvent,
    kinesis::KinesisEventRecord,
    streams::{KinesisBatchItemFailure, KinesisEventResponse},
};
use lambda_runtime::{run, service_fn, Error, LambdaEvent};

async fn function_handler(event: LambdaEvent<KinesisEvent>) -> Result<KinesisEventResponse, Error> {
    let mut response = KinesisEventResponse {
        batch_item_failures: vec![],
    };

    if event.payload.records.is_empty() {
        tracing::info!("No records found. Exiting.");
        return Ok(response);
    }

    for record in &event.payload.records {
        tracing::info!(
            "EventId: {}",
            record.event_id.as_deref().unwrap_or_default()
        );

        let record_processing_result = process_record(record);

        if record_processing_result.is_err() {
            response.batch_item_failures.push(KinesisBatchItemFailure {
                item_identifier: record.kinesis.sequence_number.clone(),
            });
            /* Since we are working with streams, we can return the failed item immediately.
            Lambda will immediately begin to retry processing from this failed item onwards. */
            return Ok(response);
        }
    }

    tracing::info!(
        "Successfully processed {} records",
        event.payload.records.len()
    );

    Ok(response)
}

fn process_record(record: &KinesisEventRecord) -> Result<(), Error> {
    let record_data = std::str::from_utf8(record.kinesis.data.as_slice());

    if let Some(err) = record_data.err() {
        tracing::error!("Error: {}", err);
        return Err(Error::from(err));
    }

    let record_data = record_data.unwrap_or_default();

    // do something interesting with the data
    tracing::info!("Data: {}", record_data);

    Ok(())
}

#[tokio::main]
async fn main() -> Result<(), Error> {
    tracing_subscriber::fmt()
        .with_max_level(tracing::Level::INFO)
        // disable printing the name of the module in every log line.
        .with_target(false)
        // disabling time is handy because CloudWatch will add the ingestion time.
        .without_time()
        .init();

    run(service_fn(function_handler)).await
}
```

------

## 使用 Powertools for AWS Lambda 批处理器
<a name="services-kinesis-batchfailurereporting-powertools"></a>

Powertools for AWS Lambda 中的批处理器实用程序会自动处理部分批处理响应逻辑，从而降低实施批处理故障报告的复杂性。下面是使用批处理器的示例：

**Python**  
有关完整的示例和设置说明，请参阅[批处理器文档](https://docs.powertools.aws.dev/lambda/python/latest/utilities/batch/)。
使用 AWS Lambda 批处理器处理 Kinesis Data Streams 流记录。  

```
import json
from aws_lambda_powertools import Logger
from aws_lambda_powertools.utilities.batch import BatchProcessor, EventType, process_partial_response
from aws_lambda_powertools.utilities.data_classes import KinesisEvent
from aws_lambda_powertools.utilities.typing import LambdaContext

processor = BatchProcessor(event_type=EventType.KinesisDataStreams)
logger = Logger()

def record_handler(record):
    logger.info(record)
    # Your business logic here
    # Raise an exception to mark this record as failed
    
def lambda_handler(event, context: LambdaContext):
    return process_partial_response(
        event=event, 
        record_handler=record_handler, 
        processor=processor,
        context=context
    )
```

**TypeScript**  
有关完整的示例和设置说明，请参阅[批处理器文档](https://docs.aws.amazon.com/powertools/typescript/latest/features/batch/)。
使用 AWS Lambda 批处理器处理 Kinesis Data Streams 流记录。  

```
import { BatchProcessor, EventType, processPartialResponse } from '@aws-lambda-powertools/batch';
import { Logger } from '@aws-lambda-powertools/logger';
import type { KinesisEvent, Context } from 'aws-lambda';

const processor = new BatchProcessor(EventType.KinesisDataStreams);
const logger = new Logger();

const recordHandler = async (record: any): Promise<void> => {
    logger.info('Processing record', { record });
    // Your business logic here
    // Throw an error to mark this record as failed
};

export const handler = async (event: KinesisEvent, context: Context) => {
    return processPartialResponse(event, recordHandler, processor, {
        context,
    });
};
```

**Java**  
有关完整的示例和设置说明，请参阅[批处理器文档](https://docs.powertools.aws.dev/lambda/java/latest/utilities/batch/)。
使用 AWS Lambda 批处理器处理 Kinesis Data Streams 流记录。  

```
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.KinesisEvent;
import com.amazonaws.services.lambda.runtime.events.StreamsEventResponse;
import software.amazon.lambda.powertools.batch.BatchMessageHandlerBuilder;
import software.amazon.lambda.powertools.batch.handler.BatchMessageHandler;

public class KinesisStreamBatchHandler implements RequestHandler<KinesisEvent, StreamsEventResponse> {

    private final BatchMessageHandler<KinesisEvent, StreamsEventResponse> handler;

    public KinesisStreamBatchHandler() {
        handler = new BatchMessageHandlerBuilder()
                .withKinesisBatchHandler()
                .buildWithRawMessageHandler(this::processMessage);
    }

    @Override
    public StreamsEventResponse handleRequest(KinesisEvent kinesisEvent, Context context) {
        return handler.processBatch(kinesisEvent, context);
    }

    private void processMessage(KinesisEvent.KinesisEventRecord kinesisEventRecord, Context context) {
        // Process the stream record
    }
}
```

**.NET**  
有关完整的示例和设置说明，请参阅[批处理器文档](https://docs.aws.amazon.com/powertools/dotnet/utilities/batch-processing/)。
使用 AWS Lambda 批处理器处理 Kinesis Data Streams 流记录。  

```
using System;
using System.Threading;
using System.Threading.Tasks;
using Amazon.Lambda.Core;
using Amazon.Lambda.KinesisEvents;
using Amazon.Lambda.Serialization.SystemTextJson;
using AWS.Lambda.Powertools.BatchProcessing;

[assembly: LambdaSerializer(typeof(DefaultLambdaJsonSerializer))]

namespace HelloWorld;

public class OrderEvent
{
    public string? OrderId { get; set; }
    public string? CustomerId { get; set; }
    public decimal Amount { get; set; }
    public DateTime OrderDate { get; set; }
}

internal class TypedKinesisRecordHandler : ITypedRecordHandler<OrderEvent> 
{
    public async Task<RecordHandlerResult> HandleAsync(OrderEvent orderEvent, CancellationToken cancellationToken)
    {
        if (string.IsNullOrEmpty(orderEvent.OrderId)) 
        {
            throw new ArgumentException("Order ID is required");
        }

        return await Task.FromResult(RecordHandlerResult.None); 
    }
}

public class Function
{
    [BatchProcessor(TypedRecordHandler = typeof(TypedKinesisRecordHandler))]
    public BatchItemFailuresResponse HandlerUsingTypedAttribute(KinesisEvent _)
    {
        return TypedKinesisStreamBatchProcessor.Result.BatchItemFailuresResponse; 
    }
}
```

# 在 Lambda 中保留 Kinesis Data Streams 事件源的已丢弃批次记录
<a name="kinesis-on-failure-destination"></a>

Kinesis 事件源映射的错误处理取决于错误是在调用函数之前还是在函数调用期间发生的：
+ **调用前：**如果 Lambda 事件源映射由于节流或其他问题而无法调用该函数，则它会一直重试，直到记录过期或超过事件源映射上配置的最大期限（[MaximumRecordAgeInSeconds](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html#lambda-CreateEventSourceMapping-request-MaximumRecordAgeInSeconds)）。
+ **调用期间：**如果调用函数但返回错误，Lambda 会重试，直到记录过期、超过最大期限（[MaximumRecordAgeInSeconds](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html#lambda-CreateEventSourceMapping-request-MaximumRecordAgeInSeconds)）或达到配置的重试配额（[MaximumRetryAttempts](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html#lambda-CreateEventSourceMapping-request-MaximumRetryAttempts)）。对于函数错误，您还可以配置 [BisectBatchOnFunctionError](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html#lambda-CreateEventSourceMapping-response-BisectBatchOnFunctionError)，将失败的批次拆分为两个较小的批次，从而隔离错误记录并避免超时。拆分批次不会消耗重试配额。

如果错误处理措施失败，Lambda 将丢弃记录并继续处理数据流中的批次。使用默认设置时，这意味着错误的记录可能会阻止受影响的分区上的处理，时间最长为一周。为了避免这种情况，请配置函数的事件源映射，使用合理的重试次数和适合您的使用案例的最长记录期限。

## 配置失败调用的目标
<a name="kinesis-on-failure-destination-console"></a>

要保留失败的事件源映射调用的记录，请在函数的事件源映射中添加一个目标。发送到目标的每条记录都是一个 JSON 文档，其中包含有关失败调用的元数据。对于 Amazon S3 目标，Lambda 还会发送整个调用记录以及元数据。您可以将任何 Amazon SNS 主题、Amazon SQS 队列、Amazon S3 存储桶或 Kafka 配置为目标。

借助 Amazon S3 目标，您可以使用 [Amazon S3 事件通知](https://docs.aws.amazon.com/)功能在对象上传到目标 S3 存储桶时接收通知。您还可以将 S3 事件通知配置为调用另一个 Lambda 函数来对失败的批次执行自动处理。

您的执行角色必须具有目标的权限：
+ **对于 SQS 目标：**[sqs:SendMessage](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_SendMessage.html)
+ **对于 SNS 目标：**[sns:Publish](https://docs.aws.amazon.com/sns/latest/api/API_Publish.html)
+ **对于 S3 目标：**[ s3:PutObject](https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html) 和 [s3:ListBucket](https://docs.aws.amazon.com/AmazonS3/latest/API/ListObjectsV2.html)
+ **对于 Kafka 目标：**[kafka-cluster:WriteData](https://docs.aws.amazon.com/msk/latest/developerguide/kafka-actions.html)

您可以将 Kafka 主题配置为 Kafka 事件源映射失败时的目标。当 Lambda 在重试次数用尽后仍无法处理记录，或者当记录的保存时间超过最大期限时，Lambda 会将这些失败的记录发送至指定的 Kafka 主题，以便后续进行处理。请参考[使用 Kafka 主题作为失败时的目标](kafka-on-failure-destination.md)。

如果您已使用自己的 KMS 密钥为 S3 目标启用加密，则函数的执行角色还必须具有调用 [kms:GenerateDataKey](https://docs.aws.amazon.com/kms/latest/APIReference/API_GenerateDataKey.html) 的权限。如果 KMS 密钥和 S3 存储桶目标与您的 Lambda 函数和执行角色位于不同的账户中，请将 KMS 密钥配置为信任执行角色以允许 kms:GenerateDataKey。

要使用控制台配置失败时的目标，请执行以下步骤：

1. 打开 Lamba 控制台的[函数](https://console.aws.amazon.com/lambda/home#/functions)页面。

1. 选择函数。

1. 在 **Function overview (函数概览)** 下，选择 **Add destination (添加目标)**。

1. 对于**源**，请选择**事件源映射调用**。

1. 对于**事件源映射**，请选择为此函数配置的事件源。

1. 在**条件**中，选择**失败时**。对于事件源映射调用，这是唯一可接受的条件。

1. 对于**目标类型**，请选择 Lambda 要发送调用记录的目标类型。

1. 对于 **Destination (目标)**，请选择一个资源。

1. 选择**保存**。

您还可以使用 AWS Command Line Interface（AWS CLI）配置失败时的目标。例如，以 [create-event-source-mapping](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/create-event-source-mapping.html) 命令将带有 SQS 失败时目标的事件源映射添加到 `MyFunction`：

```
aws lambda create-event-source-mapping \
--function-name "MyFunction" \
--event-source-arn arn:aws:kinesis:us-east-2:123456789012:stream/lambda-stream \
--destination-config '{"OnFailure": {"Destination": "arn:aws:sqs:us-east-1:123456789012:dest-queue"}}'
```

以下 [update-event-source-mapping](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/update-event-source-mapping.html) 命令更新事件源映射，以在两次重试之后或记录超过一小时后将失败的调用记录发送到 SNS 目标。

```
aws lambda update-event-source-mapping \
--uuid f89f8514-cdd9-4602-9e1f-01a5b77d449b \
--maximum-retry-attempts 2 \
--maximum-record-age-in-seconds 3600 \
--destination-config '{"OnFailure": {"Destination": "arn:aws:sns:us-east-1:123456789012:dest-topic"}}'
```

更新的设置是异步应用的，并且直到该过程完成才反映在输出中。使用 [get-event-source-mapping](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/get-event-source-mapping.html) 命令查看当前状态。

要移除目标，请提供一个空字符串作为 `destination-config` 参数的实际参数：

```
aws lambda update-event-source-mapping \
--uuid f89f8514-cdd9-4602-9e1f-01a5b77d449b \
--destination-config '{"OnFailure": {"Destination": ""}}'
```

### Amazon S3 目标的安全最佳实践
<a name="kinesis-s3-destination-security"></a>

如果删除配置为目标的 S3 存储桶而不将目标从函数配置中删除，则可能会造成安全风险。如果其他用户知道您的目标存储桶的名称，则他们可以在其 AWS 账户中重新创建存储桶。调用失败的记录将发送到存储桶，这可能会暴露您函数中的数据。

**警告**  
为确保您的函数中的调用记录不会发送到另一个 AWS 账户中的 S3 存储桶，请向函数的执行角色添加条件，以将 `s3:PutObject` 权限限制为您账户中的存储桶。

以下示例显示了一个 IAM 策略，该策略将您函数的 `s3:PutObject` 权限限制为您账户中的存储桶。该策略还为 Lambda 提供了使用 S3 存储桶作为目标所需的 `s3:ListBucket` 权限。

```
{
    "Version": "2012-10-17",		 	 	 
    "Statement": [
        {
            "Sid": "S3BucketResourceAccountWrite",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:ListBucket"
            ],
            "Resource": [
                "arn:aws:s3:::*/*",
                "arn:aws:s3:::*"
            ],
            "Condition": {
                "StringEquals": {
                    "s3:ResourceAccount": "111122223333"
                }
            }
        }
    ]
}
```

要使用 AWS 管理控制台 或 AWS CLI 向函数的执行角色添加权限策略，请参阅以下程序中的说明：

------
#### [ Console ]

**向函数的执行角色添加权限策略（控制台）**

1. 打开 Lamba 控制台的[函数](https://console.aws.amazon.com/lambda/home#/functions)页面。

1. 选择想要修改其执行角色的 Lambda 函数。

1. 在**配置**选项卡中，选择**权限**。

1. 在**执行角色**选项卡中，选择您的函数的**角色名称**,以打开该角色的 IAM 控制台页面。

1. 通过执行以下操作，向角色添加权限策略：

   1. 在**权限策略**窗格中，选择**添加权限**，然后选择**创建内联策略**。

   1. 在**策略编辑器**中，选择 **JSON**。

   1. 将要添加的策略粘贴到编辑器中（替换现有 JSON），然后选择**下一步**。

   1. 在**策略详细信息**下，输入**策略名称**。

   1. 选择**创建策略**。

------
#### [ AWS CLI ]

**向函数的执行角色添加权限策略（CLI）**

1. 创建具有所需权限的 JSON 策略文档并将其保存在本地目录中。

1. 使用 IAM `put-role-policy` CLI 命令向您函数的执行角色添加权限。在您保存 JSON 策略文档的目录中运行以下命令，并用您自己的值替换角色名称、策略名称和策略文档。

   ```
   aws iam put-role-policy \
   --role-name my_lambda_role \
   --policy-name LambdaS3DestinationPolicy \
   --policy-document file://my_policy.json
   ```

------

### Amazon SNS 和 Amazon SQS 调用记录示例
<a name="kinesis-on-failure-destination-example-sns-sqs"></a>

以下示例显示了 Lambda 在 Kinesis 事件源调用失败时向 SQS 队列或 SNS 主题发送的内容。由于 Lambda 仅为这些目标类型发送元数据，因此请使用 `streamArn`、`shardId`、`startSequenceNumber` 和 `endSequenceNumber` 字段获取完整的原始记录。`KinesisBatchInfo` 属性中显示的所有字段始终存在。

```
{
    "requestContext": {
        "requestId": "c9b8fa9f-5a7f-xmpl-af9c-0c604cde93a5",
        "functionArn": "arn:aws:lambda:us-east-2:123456789012:function:myfunction",
        "condition": "RetryAttemptsExhausted",
        "approximateInvokeCount": 1
    },
    "responseContext": {
        "statusCode": 200,
        "executedVersion": "$LATEST",
        "functionError": "Unhandled"
    },
    "version": "1.0",
    "timestamp": "2019-11-14T00:38:06.021Z",
    "KinesisBatchInfo": {
        "shardId": "shardId-000000000001",
        "startSequenceNumber": "49601189658422359378836298521827638475320189012309704722",
        "endSequenceNumber": "49601189658422359378836298522902373528957594348623495186",
        "approximateArrivalOfFirstRecord": "2019-11-14T00:38:04.835Z",
        "approximateArrivalOfLastRecord": "2019-11-14T00:38:05.580Z",
        "batchSize": 500,
        "streamArn": "arn:aws:kinesis:us-east-2:123456789012:stream/mystream"
    }
}
```

您可以使用此信息从流中检索受影响的记录以进行故障排除。实际记录不包括在内，因此您必须处理此记录并在记录过期和丢失之前从流中检索它们。

### Amazon S3 调用记录示例
<a name="kinesis-on-failure-destination-example-sns-sqs-s3"></a>

以下示例显示了 Lambda 在 Kinesis 事件源调用失败时向 Amazon S3 存储桶发送的内容。除了针对 SQS 和 SNS 目标的上一示例中的所有字段外，`payload` 字段还包含作为转义 JSON 字符串的原始调用记录。

```
{
    "requestContext": {
        "requestId": "c9b8fa9f-5a7f-xmpl-af9c-0c604cde93a5",
        "functionArn": "arn:aws:lambda:us-east-2:123456789012:function:myfunction",
        "condition": "RetryAttemptsExhausted",
        "approximateInvokeCount": 1
    },
    "responseContext": {
        "statusCode": 200,
        "executedVersion": "$LATEST",
        "functionError": "Unhandled"
    },
    "version": "1.0",
    "timestamp": "2019-11-14T00:38:06.021Z",
    "KinesisBatchInfo": {
        "shardId": "shardId-000000000001",
        "startSequenceNumber": "49601189658422359378836298521827638475320189012309704722",
        "endSequenceNumber": "49601189658422359378836298522902373528957594348623495186",
        "approximateArrivalOfFirstRecord": "2019-11-14T00:38:04.835Z",
        "approximateArrivalOfLastRecord": "2019-11-14T00:38:05.580Z",
        "batchSize": 500,
        "streamArn": "arn:aws:kinesis:us-east-2:123456789012:stream/mystream"
    },
    "payload": "<Whole Event>" // Only available in S3
}
```

包含了调用记录的 S3 对象使用以下命名约定：

```
aws/lambda/<ESM-UUID>/<shardID>/YYYY/MM/DD/YYYY-MM-DDTHH.MM.SS-<Random UUID>
```

# 在 Lambda 中实现有状态的 Kinesis Data Streams 处理
<a name="services-kinesis-windows"></a>

Lambda 函数可以运行连续流处理应用程序。流表示通过您的应用程序持续流动的无边界数据。要分析这种不断更新的输入中的信息，可以使用按时间定义的窗口来限制包含的记录。

滚动窗口是定期打开和关闭的不同窗口。预设情况下，Lambda 调用是无状态的，在没有外部数据库的情况下，无法使用它们跨多次连续调用处理数据。但是，有了滚动窗口后，您可以在不同调用中保持状态。此状态包含之前为当前窗口处理的消息的汇总结果。您的状态最多可以是每个分片 1MB。如果超过该大小，Lambda 将提前终止窗口。

流中的每条记录都属于特定窗口。Lambda 将至少处理每条记录一次，但不保证每条记录只处理一次。在极少数情况下（例如错误处理），某些记录可能会被多次处理。第一次处理记录时始终按顺序处理。如果多次处理记录，则可能会不按顺序处理。

## 聚合和处理
<a name="streams-tumbling-processing"></a>

系统将调用您的用户托管函数以便聚合和处理该聚合的最终结果。Lambda 汇总在该窗口中接收的所有记录。您可以分多个批次接收这些记录，每个批次都作为单独的调用。每次调用都会收到一个状态。因此，当使用滚动窗口时，Lambda 函数响应必须包含 `state` 属性。如果响应不包含 `state` 属性，Lambda 会将其视作失败的调用。为了满足该条件，您的函数可以返回一个具有以下 JSON 形状的 `TimeWindowEventResponse` 对象：

**Example `TimeWindowEventResponse` 值**  

```
{
    "state": {
        "1": 282,
        "2": 715
    },
    "batchItemFailures": []
}
```

**注意**  
对于 Java 函数，我们建议使用 `Map<String, String>` 来表示状态。

在窗口末尾，标志 `isFinalInvokeForWindow` 被设置 `true`，以表示这是最终状态，并且已准备好进行处理。处理完成后，窗口完成，最终调用完成，然后状态将被删除。

在窗口结束时，Lambda 会对针对聚合结果的操作应用最终处理。您的最终处理将同步调用。成功调用后，函数会检查序列号并继续进行流处理。如果调用失败，则您的 Lambda 函数将暂停进一步处理，直到成功调用为止。

**Example KinesisTimeWindowEvent**  

```
{
    "Records": [
        {
            "kinesis": {
                "kinesisSchemaVersion": "1.0",
                "partitionKey": "1",
                "sequenceNumber": "49590338271490256608559692538361571095921575989136588898",
                "data": "SGVsbG8sIHRoaXMgaXMgYSB0ZXN0Lg==",
                "approximateArrivalTimestamp": 1607497475.000
            },
            "eventSource": "aws:kinesis",
            "eventVersion": "1.0",
            "eventID": "shardId-000000000006:49590338271490256608559692538361571095921575989136588898",
            "eventName": "aws:kinesis:record",
            "invokeIdentityArn": "arn:aws:iam::123456789012:role/lambda-kinesis-role",
            "awsRegion": "us-east-1",
            "eventSourceARN": "arn:aws:kinesis:us-east-1:123456789012:stream/lambda-stream"
        }
    ],
    "window": {
        "start": "2020-12-09T07:04:00Z",
        "end": "2020-12-09T07:06:00Z"
    },
    "state": {
        "1": 282,
        "2": 715
    },
    "shardId": "shardId-000000000006",
    "eventSourceARN": "arn:aws:kinesis:us-east-1:123456789012:stream/lambda-stream",
    "isFinalInvokeForWindow": false,
    "isWindowTerminatedEarly": false
}
```

## 配置
<a name="streams-tumbling-config"></a>

您可以在创建或更新事件源映射时配置滚动窗口。要配置翻转窗口，请以秒为单位进行指定（[TumblingWindowInSeconds](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html#lambda-CreateEventSourceMapping-request-TumblingWindowInSeconds)）。以下示例 AWS Command Line Interface (AWS CLI) 命令会创建一个滚动窗口为 120 秒的流式事件源映射。为聚合和处理定义的 Lambda 函数被命名为 `tumbling-window-example-function`。

```
aws lambda create-event-source-mapping \
--event-source-arn arn:aws:kinesis:us-east-1:123456789012:stream/lambda-stream \
--function-name tumbling-window-example-function \
--starting-position TRIM_HORIZON \
--tumbling-window-in-seconds 120
```

Lambda 根据记录插入到流的时间来确定滚动窗口的边界。所有记录都有一个大致的时间戳，供 Lambda 在确定边界时使用。

滚动窗口聚合不支持重新分片。当分片结束时，Lambda 认为当前窗口已关闭，并且任何子分片都将以全新状态启动自己的窗口。如果没有向当前窗口添加任何新记录，则 Lambda 会等待最多 2 分钟，然后假定该窗口已结束。这有助于确保函数读取当前窗口中的所有记录，即使这些记录是间歇性添加的。

滚动窗口完全支持现有的重试策略 `maxRetryAttempts` 和 `maxRecordAge`。

**Example Handler.py – 聚合和处理**  
以下 Python 函数演示了如何聚合然后处理您的最终状态：  

```
def lambda_handler(event, context):
    print('Incoming event: ', event)
    print('Incoming state: ', event['state'])

#Check if this is the end of the window to either aggregate or process.
    if event['isFinalInvokeForWindow']:
        # logic to handle final state of the window
        print('Destination invoke')
    else:
        print('Aggregate invoke')

#Check for early terminations
    if event['isWindowTerminatedEarly']:
        print('Window terminated early')

    #Aggregation logic
    state = event['state']
    for record in event['Records']:
        state[record['kinesis']['partitionKey']] = state.get(record['kinesis']['partitionKey'], 0) + 1

    print('Returning state: ', state)
    return {'state': state}
```

# Amazon Kinesis Data Streams 事件源映射的 Lambda 参数
<a name="services-kinesis-parameters"></a>

所有 Lambda 事件源映射共享相同的 [CreateEventSourceMapping](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html) 和 [UpdateEventSourceMapping](https://docs.aws.amazon.com/lambda/latest/api/API_UpdateEventSourceMapping.html) API 操作。但是，只有部分参数适用于 Kinesis。


| 参数 | 必需 | 默认值 | 备注 | 
| --- | --- | --- | --- | 
|  [BatchSize](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html#lambda-CreateEventSourceMapping-request-BatchSize)  |  否  |  100  |  最大值：10000  | 
|  [BisectBatchOnFunctionError](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html#lambda-CreateEventSourceMapping-request-BisectBatchOnFunctionError)  |  否  |  false  |  none | 
|  [DestinationConfig](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html#lambda-CreateEventSourceMapping-request-DestinationConfig)  |  否  | 不适用 |  丢弃的记录的 Amazon SQS 队列或 Amazon SNS 主题目标。有关更多信息，请参阅 [配置失败调用的目标](kinesis-on-failure-destination.md#kinesis-on-failure-destination-console)。  | 
|  [Enabled (已启用)](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html#lambda-CreateEventSourceMapping-request-Enabled)  |  否  |  真实  |  none | 
|  [EventSourceArn](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html#lambda-CreateEventSourceMapping-request-EventSourceArn)  |  Y  | 不适用 |  数据流或流使用者的 ARN  | 
|  [FunctionName](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html#lambda-CreateEventSourceMapping-request-FunctionName)  |  是  | 不适用 |  none | 
|  [FunctionResponseTypes](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html#lambda-CreateEventSourceMapping-request-FunctionResponseTypes)  |  否  |  不适用 |  要使您的函数报告某个批处理中的特定失败，请在 `FunctionResponseTypes` 中包含值 `ReportBatchItemFailures`。有关更多信息，请参阅 [使用 Kinesis Data Streams 和 Lambda 配置部分批次响应](services-kinesis-batchfailurereporting.md)。  | 
|  [MaximumBatchingWindowInSeconds](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html#lambda-CreateEventSourceMapping-request-MaximumBatchingWindowInSeconds)  |  否  |  0  |  none | 
|  [MaximumRecordAgeInSeconds](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html#lambda-CreateEventSourceMapping-request-MaximumRecordAgeInSeconds)  |  否  |  –1  |  -1 表示无限：Lambda 不会丢弃记录（[Kinesis Data Streams 的数据留存设置](https://docs.aws.amazon.com/streams/latest/dev/kinesis-extended-retention.html)仍然适用） 最小值：-1 最大值：604800  | 
|  [MaximumRetryAttempts](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html#lambda-CreateEventSourceMapping-request-MaximumRetryAttempts)  |  否  |  –1  |  -1 表示无限：会一直重试失败的记录，直到记录过期。 最小值：-1 最大值：10000  | 
|  [ParallelizationFactor](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html#lambda-CreateEventSourceMapping-request-ParallelizationFactor)  |  否  |  1  |  最大值：10  | 
|  [StartingPosition](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html#lambda-CreateEventSourceMapping-request-StartingPosition)  |  Y  |  不适用 |  AT\$1TIMESTAMP、TRIM\$1HORIZON 或 LATEST  | 
|  [StartingPositionTimestamp](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html#lambda-CreateEventSourceMapping-request-StartingPositionTimestamp)  |  否  |  不适用 |  仅当 StartingPosition 设置为 AT\$1TIMESTAMP 时才有效。开始读取的时间（以 Unix 时间秒为单位）  | 
|  [TumblingWindowInSeconds](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html#lambda-CreateEventSourceMapping-request-TumblingWindowInSeconds)  |  否  |  不适用 |  最小值：0 最大值：900  | 

# 对 Kinesis 事件源使用事件筛选
<a name="with-kinesis-filtering"></a>

您可以使用事件筛选，控制 Lambda 将流或队列中的哪些记录发送给函数。有关事件筛选工作原理的一般信息，请参阅 [控制 Lambda 向您的函数发送的事件](invocation-eventfiltering.md)。

本节重点介绍 Kinesis 事件源的事件筛选。

**注意**  
Kinesis 事件源映射仅支持对 `data` 键进行筛选。

**Topics**
+ [

## Kinesis 事件筛选基础知识
](#filtering-kinesis)
+ [

## 筛选 Kinesis 聚合记录
](#filtering-kinesis-efo)

## Kinesis 事件筛选基础知识
<a name="filtering-kinesis"></a>

假设创建器将 JSON 格式的数据放入 Kinesis 数据流。示例记录如下所示，`data` 字段中的 JSON 数据会转换为 Base64 编码字符串。

```
{
    "kinesis": {
        "kinesisSchemaVersion": "1.0",
        "partitionKey": "1",
        "sequenceNumber": "49590338271490256608559692538361571095921575989136588898",
        "data": "eyJSZWNvcmROdW1iZXIiOiAiMDAwMSIsICJUaW1lU3RhbXAiOiAieXl5eS1tbS1kZFRoaDptbTpzcyIsICJSZXF1ZXN0Q29kZSI6ICJBQUFBIn0=",
        "approximateArrivalTimestamp": 1545084650.987
        },
    "eventSource": "aws:kinesis",
    "eventVersion": "1.0",
    "eventID": "shardId-000000000006:49590338271490256608559692538361571095921575989136588898",
    "eventName": "aws:kinesis:record",
    "invokeIdentityArn": "arn:aws:iam::123456789012:role/lambda-role",
    "awsRegion": "us-east-2",
    "eventSourceARN": "arn:aws:kinesis:us-east-2:123456789012:stream/lambda-stream"
}
```

只要创建器放入流中的数据是有效的 JSON，您就可以使用 `data` 键，通过事件筛选来筛选记录。假设创建器将如下 JSON 格式的记录放入 Kinesis 流。

```
{
    "record": 12345,
    "order": {
        "type": "buy",
        "stock": "ANYCO",
        "quantity": 1000
        }
}
```

要仅筛选订单类型为“购买”的记录，`FilterCriteria` 对象将如下所示。

```
{
    "Filters": [
        {
            "Pattern": "{ \"data\" : { \"order\" : { \"type\" : [ \"buy\" ] } } }"
        }
    ]
}
```

为了更清楚起见，以下是在纯 JSON 中展开的筛选条件 `Pattern` 的值。

```
{
    "data": {
        "order": {
            "type": [ "buy" ]
            }
      }
}
```

您可以使用控制台、AWS CLI 或 AWS SAM 模板添加筛选条件。

------
#### [ Console ]

要使用控制台添加此筛选条件，请按照 [将筛选条件附加到事件源映射（控制台）](invocation-eventfiltering.md#filtering-console) 中的说明，为**筛选条件**输入以下字符串。

```
{ "data" : { "order" : { "type" : [ "buy" ] } } }
```

------
#### [ AWS CLI ]

要使用 AWS Command Line Interface（AWS CLI）创建包含这些筛选条件的新事件源映射，请运行以下命令。

```
aws lambda create-event-source-mapping \
    --function-name my-function \
    --event-source-arn arn:aws:kinesis:us-east-2:123456789012:stream/my-stream \
    --filter-criteria '{"Filters": [{"Pattern": "{ \"data\" : { \"order\" : { \"type\" : [ \"buy\" ] } } }"}]}'
```

要将这些筛选条件添加到现有事件源映射中，请运行以下命令。

```
aws lambda update-event-source-mapping \
    --uuid "a1b2c3d4-5678-90ab-cdef-11111EXAMPLE" \
    --filter-criteria '{"Filters": [{"Pattern": "{ \"data\" : { \"order\" : { \"type\" : [ \"buy\" ] } } }"}]}'
```

------
#### [ AWS SAM ]

要使用 AWS SAM 添加此筛选条件，请将以下代码段添加到事件源的 YAML 模板中。

```
FilterCriteria:
  Filters:
    - Pattern: '{ "data" : { "order" : { "type" : [ "buy" ] } } }'
```

------

要正确筛选 Kinesis 源中的事件，数据字段及其筛选条件都必须为有效的 JSON 格式。如果任一字段不为有效的 JSON 格式，Lambda 将会丢弃消息或引发异常。下表汇总了具体行为：


| 传入数据格式 | 数据属性中的筛选条件模式格式 | 导致的操作 | 
| --- | --- | --- | 
|  有效 JSON  |  有效 JSON  |  Lambda 根据您的筛选条件进行筛选。  | 
|  有效 JSON  |  数据属性中没有筛选条件模式  |  Lambda 根据您的筛选条件进行筛选（仅限其他元数据属性）。  | 
|  有效 JSON  |  非 JSON  |  Lambda 在事件源映射创建或更新时引发异常。数据属性的筛选条件模式必须为有效的 JSON 格式。  | 
|  非 JSON  |  有效 JSON  |  Lambda 将丢弃记录。  | 
|  非 JSON  |  数据属性中没有筛选条件模式  |  Lambda 根据您的筛选条件进行筛选（仅限其他元数据属性）。  | 
|  非 JSON  |  非 JSON  |  Lambda 在事件源映射创建或更新时引发异常。数据属性的筛选条件模式必须为有效的 JSON 格式。  | 

## 筛选 Kinesis 聚合记录
<a name="filtering-kinesis-efo"></a>

使用 Kinesis，您可以将多条记录聚合到单条 Kinesis Data Streams 记录中，以提高数据吞吐量。使用 Kinesis [增强扇出功能](https://docs.aws.amazon.com/streams/latest/dev/enhanced-consumers.html)时，Lambda 只能将筛选条件应用于聚合记录。不支持使用标准 Kinesis 筛选聚合记录。使用增强扇出功能时，您可以配置 Kinesis 专用吞吐量使用者作为 Lambda 函数的触发器。然后，Lambda 会筛选聚合记录，并仅传递符合筛选条件的记录。

要了解有关 Kinesis 记录聚合的更多信息，请参阅“Kinesis Producer Library（KPL）关键概念”页面上的[聚合](https://docs.aws.amazon.com/streams/latest/dev/kinesis-kpl-concepts.html#kinesis-kpl-concepts-aggretation)部分。要了解有关将 Lambda 与 Kinesis 增强扇出功能结合使用的更多信息，请参阅 AWS 计算博客上的[使用 Amazon Kinesis Data Streams 增强扇出功能和 AWS Lambda 提高实时流处理性能](https://aws.amazon.com/blogs/compute/increasing-real-time-stream-processing-performance-with-amazon-kinesis-data-streams-enhanced-fan-out-and-aws-lambda/)。

# 教程：将 Lambda 与 Kinesis Data Streams 结合使用
<a name="with-kinesis-example"></a>

在本教程中，您将创建 Lambda 函数来处理 Amazon Kinesis 数据流中的事件。

1. 自定义应用程序将记录写入流。

1. AWS Lambda 轮询流并在检测到流中的新记录时调用 Lambda 函数。

1. AWS Lambda 通过代入您在创建 Lambda 函数时指定的执行角色来运行 Lambda 函数。

## 先决条件
<a name="with-kinesis-prepare"></a>

### 安装 AWS Command Line Interface
<a name="install_aws_cli"></a>

如果您尚未安装 AWS Command Line Interface，请按照[安装或更新最新版本的 AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) 中的步骤进行安装。

本教程需要命令行终端或 Shell 来运行命令。在 Linux 和 macOS 中，可使用您首选的 Shell 和程序包管理器。

**注意**  
在 Windows 中，操作系统的内置终端不支持您经常与 Lambda 一起使用的某些 Bash CLI 命令（例如 `zip`）。[安装 Windows Subsystem for Linux](https://docs.microsoft.com/en-us/windows/wsl/install-win10)，获取 Ubuntu 和 Bash 与 Windows 集成的版本。

## 创建执行角色
<a name="with-kinesis-example-create-iam-role"></a>

创建[执行角色](lambda-intro-execution-role.md)，向您的函数授予访问 AWS 资源的权限。

**创建执行角色**

1. 在 IAM 控制台中，打开 [Roles（角色）页面](https://console.aws.amazon.com/iam/home#/roles)。

1. 选择**创建角色**。

1. 创建具有以下属性的角色。
   + **Trusted entity**（可信任的实体）– **AWS Lambda**。
   + **Permissions**（权限）– **AWSLambdaKinesisExecutionRole**。
   + **Role name**（角色名称）– **lambda-kinesis-role**。

**AWSLambdaKinesisExecutionRole** 策略具有该函数从 Kinesis 中读取项目并将日志写入 CloudWatch Logs 所需的权限。

## 创建函数
<a name="with-kinesis-example-create-function"></a>

创建一个处理您的 Kinesis 消息的 Lambda 函数。函数代码会将 Kinesis 记录的事件 ID 和事件数据记录到 CloudWatch Logs 中。

本教程使用 Node.js 24 运行时系统，但我们还提供了其他运行时系统语言的示例代码。您可以选择以下框中的选项卡，查看适用于您感兴趣的运行时系统的代码。您将在此步骤中使用的 JavaScript 代码是 **JavaScript** 选项卡中显示的第一个示例。

------
#### [ .NET ]

**适用于 .NET 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-kinesis-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
通过 .NET 将 Kinesis 事件与 Lambda 结合使用。  

```
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
﻿using System.Text;
using Amazon.Lambda.Core;
using Amazon.Lambda.KinesisEvents;
using AWS.Lambda.Powertools.Logging;

// Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]

namespace KinesisIntegrationSampleCode;

public class Function
{
    // Powertools Logger requires an environment variables against your function
    // POWERTOOLS_SERVICE_NAME
    [Logging(LogEvent = true)]
    public async Task FunctionHandler(KinesisEvent evnt, ILambdaContext context)
    {
        if (evnt.Records.Count == 0)
        {
            Logger.LogInformation("Empty Kinesis Event received");
            return;
        }

        foreach (var record in evnt.Records)
        {
            try
            {
                Logger.LogInformation($"Processed Event with EventId: {record.EventId}");
                string data = await GetRecordDataAsync(record.Kinesis, context);
                Logger.LogInformation($"Data: {data}");
                // TODO: Do interesting work based on the new data
            }
            catch (Exception ex)
            {
                Logger.LogError($"An error occurred {ex.Message}");
                throw;
            }
        }
        Logger.LogInformation($"Successfully processed {evnt.Records.Count} records.");
    }

    private async Task<string> GetRecordDataAsync(KinesisEvent.Record record, ILambdaContext context)
    {
        byte[] bytes = record.Data.ToArray();
        string data = Encoding.UTF8.GetString(bytes);
        await Task.CompletedTask; //Placeholder for actual async work
        return data;
    }
}
```

------
#### [ Go ]

**适用于 Go 的 SDK V2**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-kinesis-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
使用 Go 将 Kinesis 事件与 Lambda 结合使用。  

```
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package main

import (
	"context"
	"log"

	"github.com/aws/aws-lambda-go/events"
	"github.com/aws/aws-lambda-go/lambda"
)

func handler(ctx context.Context, kinesisEvent events.KinesisEvent) error {
	if len(kinesisEvent.Records) == 0 {
		log.Printf("empty Kinesis event received")
		return nil
	}

	for _, record := range kinesisEvent.Records {
		log.Printf("processed Kinesis event with EventId: %v", record.EventID)
		recordDataBytes := record.Kinesis.Data
		recordDataText := string(recordDataBytes)
		log.Printf("record data: %v", recordDataText)
		// TODO: Do interesting work based on the new data
	}
	log.Printf("successfully processed %v records", len(kinesisEvent.Records))
	return nil
}

func main() {
	lambda.Start(handler)
}
```

------
#### [ Java ]

**适用于 Java 的 SDK 2.x**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-kinesis-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
使用 Java 将 Kinesis 事件与 Lambda 结合使用。  

```
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package example;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.LambdaLogger;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.KinesisEvent;

public class Handler implements RequestHandler<KinesisEvent, Void> {
    @Override
    public Void handleRequest(final KinesisEvent event, final Context context) {
        LambdaLogger logger = context.getLogger();
        if (event.getRecords().isEmpty()) {
            logger.log("Empty Kinesis Event received");
            return null;
        }
        for (KinesisEvent.KinesisEventRecord record : event.getRecords()) {
            try {
                logger.log("Processed Event with EventId: "+record.getEventID());
                String data = new String(record.getKinesis().getData().array());
                logger.log("Data:"+ data);
                // TODO: Do interesting work based on the new data
            }
            catch (Exception ex) {
                logger.log("An error occurred:"+ex.getMessage());
                throw ex;
            }
        }
        logger.log("Successfully processed:"+event.getRecords().size()+" records");
        return null;
    }

}
```

------
#### [ JavaScript ]

**SDK for JavaScript（v3）**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/blob/main/integration-kinesis-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
通过 JavaScript 将 Kinesis 事件与 Lambda 结合使用。  

```
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
exports.handler = async (event, context) => {
  for (const record of event.Records) {
    try {
      console.log(`Processed Kinesis Event - EventID: ${record.eventID}`);
      const recordData = await getRecordDataAsync(record.kinesis);
      console.log(`Record Data: ${recordData}`);
      // TODO: Do interesting work based on the new data
    } catch (err) {
      console.error(`An error occurred ${err}`);
      throw err;
    }
  }
  console.log(`Successfully processed ${event.Records.length} records.`);
};

async function getRecordDataAsync(payload) {
  var data = Buffer.from(payload.data, "base64").toString("utf-8");
  await Promise.resolve(1); //Placeholder for actual async work
  return data;
}
```
通过 TypeScript 将 Kinesis 事件与 Lambda 结合使用。  

```
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import {
  KinesisStreamEvent,
  Context,
  KinesisStreamHandler,
  KinesisStreamRecordPayload,
} from "aws-lambda";
import { Buffer } from "buffer";
import { Logger } from "@aws-lambda-powertools/logger";

const logger = new Logger({
  logLevel: "INFO",
  serviceName: "kinesis-stream-handler-sample",
});

export const functionHandler: KinesisStreamHandler = async (
  event: KinesisStreamEvent,
  context: Context
): Promise<void> => {
  for (const record of event.Records) {
    try {
      logger.info(`Processed Kinesis Event - EventID: ${record.eventID}`);
      const recordData = await getRecordDataAsync(record.kinesis);
      logger.info(`Record Data: ${recordData}`);
      // TODO: Do interesting work based on the new data
    } catch (err) {
      logger.error(`An error occurred ${err}`);
      throw err;
    }
    logger.info(`Successfully processed ${event.Records.length} records.`);
  }
};

async function getRecordDataAsync(
  payload: KinesisStreamRecordPayload
): Promise<string> {
  var data = Buffer.from(payload.data, "base64").toString("utf-8");
  await Promise.resolve(1); //Placeholder for actual async work
  return data;
}
```

------
#### [ PHP ]

**适用于 PHP 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-kinesis-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
通过 PHP 将 Kinesis 事件与 Lambda 结合使用。  

```
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
<?php

# using bref/bref and bref/logger for simplicity

use Bref\Context\Context;
use Bref\Event\Kinesis\KinesisEvent;
use Bref\Event\Kinesis\KinesisHandler;
use Bref\Logger\StderrLogger;

require __DIR__ . '/vendor/autoload.php';

class Handler extends KinesisHandler
{
    private StderrLogger $logger;
    public function __construct(StderrLogger $logger)
    {
        $this->logger = $logger;
    }

    /**
     * @throws JsonException
     * @throws \Bref\Event\InvalidLambdaEvent
     */
    public function handleKinesis(KinesisEvent $event, Context $context): void
    {
        $this->logger->info("Processing records");
        $records = $event->getRecords();
        foreach ($records as $record) {
            $data = $record->getData();
            $this->logger->info(json_encode($data));
            // TODO: Do interesting work based on the new data

            // Any exception thrown will be logged and the invocation will be marked as failed
        }
        $totalRecords = count($records);
        $this->logger->info("Successfully processed $totalRecords records");
    }
}

$logger = new StderrLogger();
return new Handler($logger);
```

------
#### [ Python ]

**适用于 Python 的 SDK（Boto3）**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-kinesis-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
使用 Python 将 Kinesis 事件与 Lambda 结合使用。  

```
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
import base64
def lambda_handler(event, context):

    for record in event['Records']:
        try:
            print(f"Processed Kinesis Event - EventID: {record['eventID']}")
            record_data = base64.b64decode(record['kinesis']['data']).decode('utf-8')
            print(f"Record Data: {record_data}")
            # TODO: Do interesting work based on the new data
        except Exception as e:
            print(f"An error occurred {e}")
            raise e
    print(f"Successfully processed {len(event['Records'])} records.")
```

------
#### [ Ruby ]

**适用于 Ruby 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-kinesis-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
通过 Ruby 将 Kinesis 事件与 Lambda 结合使用。  

```
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
require 'aws-sdk'

def lambda_handler(event:, context:)
  event['Records'].each do |record|
    begin
      puts "Processed Kinesis Event - EventID: #{record['eventID']}"
      record_data = get_record_data_async(record['kinesis'])
      puts "Record Data: #{record_data}"
      # TODO: Do interesting work based on the new data
    rescue => err
      $stderr.puts "An error occurred #{err}"
      raise err
    end
  end
  puts "Successfully processed #{event['Records'].length} records."
end

def get_record_data_async(payload)
  data = Base64.decode64(payload['data']).force_encoding('UTF-8')
  # Placeholder for actual async work
  # You can use Ruby's asynchronous programming tools like async/await or fibers here.
  return data
end
```

------
#### [ Rust ]

**适用于 Rust 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-kinesis-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
通过 Rust 将 Kinesis 事件与 Lambda 结合使用。  

```
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
use aws_lambda_events::event::kinesis::KinesisEvent;
use lambda_runtime::{run, service_fn, Error, LambdaEvent};

async fn function_handler(event: LambdaEvent<KinesisEvent>) -> Result<(), Error> {
    if event.payload.records.is_empty() {
        tracing::info!("No records found. Exiting.");
        return Ok(());
    }

    event.payload.records.iter().for_each(|record| {
        tracing::info!("EventId: {}",record.event_id.as_deref().unwrap_or_default());

        let record_data = std::str::from_utf8(&record.kinesis.data);

        match record_data {
            Ok(data) => {
                // log the record data
                tracing::info!("Data: {}", data);
            }
            Err(e) => {
                tracing::error!("Error: {}", e);
            }
        }
    });

    tracing::info!(
        "Successfully processed {} records",
        event.payload.records.len()
    );

    Ok(())
}

#[tokio::main]
async fn main() -> Result<(), Error> {
    tracing_subscriber::fmt()
        .with_max_level(tracing::Level::INFO)
        // disable printing the name of the module in every log line.
        .with_target(false)
        // disabling time is handy because CloudWatch will add the ingestion time.
        .without_time()
        .init();

    run(service_fn(function_handler)).await
}
```

------

**创建函数**

1. 为项目创建一个目录，然后切换到该目录。

   ```
   mkdir kinesis-tutorial
   cd kinesis-tutorial
   ```

1. 将示例 JavaScript 代码复制到名为 `index.js` 的新文件中。

1. 创建部署程序包。

   ```
   zip function.zip index.js
   ```

1. 使用 `create-function` 命令创建 Lambda 函数。

   ```
   aws lambda create-function --function-name ProcessKinesisRecords \
   --zip-file fileb://function.zip --handler index.handler --runtime nodejs24.x \
   --role arn:aws:iam::111122223333:role/lambda-kinesis-role
   ```

## 测试 Lambda 函数
<a name="walkthrough-kinesis-events-adminuser-create-test-function-upload-zip-test-manual-invoke"></a>

使用 `invoke` AWS Lambda CLI 命令和示例 Kinesis 事件手动调用 Lambda 函数。

**测试 Lambda 函数**

1. 将以下 JSON 复制到文件中并将其保存为 `input.txt`。

   ```
   {
       "Records": [
           {
               "kinesis": {
                   "kinesisSchemaVersion": "1.0",
                   "partitionKey": "1",
                   "sequenceNumber": "49590338271490256608559692538361571095921575989136588898",
                   "data": "SGVsbG8sIHRoaXMgaXMgYSB0ZXN0Lg==",
                   "approximateArrivalTimestamp": 1545084650.987
               },
               "eventSource": "aws:kinesis",
               "eventVersion": "1.0",
               "eventID": "shardId-000000000006:49590338271490256608559692538361571095921575989136588898",
               "eventName": "aws:kinesis:record",
               "invokeIdentityArn": "arn:aws:iam::111122223333:role/lambda-kinesis-role",
               "awsRegion": "us-east-2",
               "eventSourceARN": "arn:aws:kinesis:us-east-2:111122223333:stream/lambda-stream"
           }
       ]
   }
   ```

1. 使用 `invoke` 命令将事件发送到该函数。

   ```
   aws lambda invoke --function-name ProcessKinesisRecords \
   --cli-binary-format raw-in-base64-out \
   --payload file://input.txt outputfile.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)。

   响应将保存到 `out.txt` 中。

## 创建 Kinesis 流
<a name="with-kinesis-example-configure-event-source-create"></a>

使用 `create-stream ` 命令创建流。

```
aws kinesis create-stream --stream-name lambda-stream --shard-count 1
```

运行下面的 `describe-stream` 命令以获取流 ARN。

```
aws kinesis describe-stream --stream-name lambda-stream
```

您应看到以下输出：

```
{
    "StreamDescription": {
        "Shards": [
            {
                "ShardId": "shardId-000000000000",
                "HashKeyRange": {
                    "StartingHashKey": "0",
                    "EndingHashKey": "340282366920746074317682119384634633455"
                },
                "SequenceNumberRange": {
                    "StartingSequenceNumber": "49591073947768692513481539594623130411957558361251844610"
                }
            }
        ],
        "StreamARN": "arn:aws:kinesis:us-east-1:111122223333:stream/lambda-stream",
        "StreamName": "lambda-stream",
        "StreamStatus": "ACTIVE",
        "RetentionPeriodHours": 24,
        "EnhancedMonitoring": [
            {
                "ShardLevelMetrics": []
            }
        ],
        "EncryptionType": "NONE",
        "KeyId": null,
        "StreamCreationTimestamp": 1544828156.0
    }
}
```

您将使用下一步中的流 ARN 来将该流关联到您的 Lambda 函数。

## 在 AWS Lambda 中添加事件源
<a name="with-kinesis-example-configure-event-source-add-event-source"></a>

运行以下 AWS CLI `add-event-source` 命令。

```
aws lambda create-event-source-mapping --function-name ProcessKinesisRecords \
--event-source  arn:aws:kinesis:us-east-1:111122223333:stream/lambda-stream \
--batch-size 100 --starting-position LATEST
```

记下映射 ID 以供将来使用。您可以通过运行 `list-event-source-mappings` 命令获取事件源映射的列表。

```
aws lambda list-event-source-mappings --function-name ProcessKinesisRecords \
--event-source arn:aws:kinesis:us-east-1:111122223333:stream/lambda-stream
```

在该响应中，您可以验证状态值是否为 `enabled`。可以禁用事件源映射，以临时暂停轮询而不丢失任何记录。

## 测试设置
<a name="with-kinesis-example-configure-event-source-test-end-to-end"></a>

要测试事件源映射，请将事件记录添加到 Kinesis 流中。`--data` 值是一个字符串，CLI 先将其编码为 base64 字符串，然后才发送到 Kinesis。您可以多次运行同一命令来向流中添加多条记录。

```
aws kinesis put-record --stream-name lambda-stream --partition-key 1 \
--data "Hello, this is a test."
```

Lambda 使用执行角色来读取来自流的记录。然后它将调用 Lambda 函数，批量传递记录。该函数解码每条记录中的数据并记录它，将输出发送到 CloudWatch Logs 中。在 [CloudWatch 控制台](https://console.aws.amazon.com/cloudwatch)中查看这些日志。

## 清除资源
<a name="cleanup"></a>

除非您想要保留为本教程创建的资源，否则可立即将其删除。通过删除您不再使用的 AWS 资源，可防止您的 AWS 账户 产生不必要的费用。

**删除执行角色**

1. 打开 IAM 控制台的[角色页面](https://console.aws.amazon.com/iam/home#/roles)。

1. 选择您创建的执行角色。

1. 选择**删除**。

1. 在文本输入字段中输入角色名称，然后选择**删除**。

**删除 Lambda 函数**

1. 打开 Lamba 控制台的 [Functions（函数）页面](https://console.aws.amazon.com/lambda/home#/functions)。

1. 选择您创建的函数。

1. 依次选择**操作**和**删除**。

1. 在文本输入字段中键入 **confirm**，然后选择 **Delete**（删除）。

**删除 Kinesis 流**

1. 登录到 AWS 管理控制台，然后通过以下网址打开 Kinesis 控制台：[https://console.aws.amazon.com/kinesisvideo/home](https://console.aws.amazon.com/kinesis)。

1. 选择您创建的流。

1. 依次选择 **Actions**（操作）和 **Delete**（删除）。

1. 在文本输入字段中输入 **delete**。

1. 选择**删除**。

# 将 Lambda 与 Kubernetes 结合使用
<a name="with-kubernetes"></a>

您可以使用 [AWS Controllers for Kubernetes（ACK）](https://aws-controllers-k8s.github.io/community/docs/community/overview/) 或 [Crossplane](https://docs.crossplane.io/latest/packages/providers/) 通过 Kubernetes API 部署和管理 Lambda 函数。

## AWS Controllers for Kubernetes（ACK）
<a name="kubernetes-ack"></a>

您可以使用 ACK 部署和管理来自 Kubernetes API 的 AWS 资源。通过 ACK，AWS 为 AWS 服务 [例如 Lambda、Amazon Elastic Container Registry（Amazon ECR）、Amazon Simple Storage Service（Amazon S3）和 Amazon SageMaker AI] 提供开源自定义控制器。每个支持的 AWS 服务有自己的自定义控制器。在您的 Kubernetes 集群中，为每个您要使用的 AWS 服务安装控制器。然后，创建[自定义资源定义（CRD）](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/)来定义 AWS 资源。

我们建议您使用 [Helm 3.8 或更高版本](https://helm.sh/docs/intro/install/)安装 ACK 控制器。每个 ACK 控制器都有自己的 Helm 图表，用于安装控制器、CRD 和 Kubernetes RBAC 规则。有关更多信息，请参阅 ACK  文档中的 [Install an ACK Controller](https://aws-controllers-k8s.github.io/community/docs/user-docs/install/)。

创建 ACK 自定义资源后，您可以像使用任何其他内置 Kubernetes 对象一样使用此资源。例如，您可以使用首选的 Kubernetes 工具链（包括 [kubectl](https://kubernetes.io/docs/reference/kubectl/)）部署和管理 Lambda 函数。

以下是通过 ACK 预置 Lambda 函数的示例用例：
+ 您的组织使用[基于角色的访问控制（RBAC）](https://kubernetes.io/docs/reference/access-authn-authz/rbac/)和[服务账户的 IAM 角色](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html)来创建权限边界。借助 ACK，您可以为 Lambda 重用此安全模型，而无需创建新用户和策略。
+ 您的组织有一个 DevOps 流程，可以使用 Kubernetes 清单将资源部署到 Amazon Elastic Kubernetes Service（Amazon EKS）集群中。借助 ACK，您可以使用清单来配置 Lambda 函数，而无需创建单独的基础架构作为代码模板。

有关使用 ACK 的更多信息，请参阅 [ACK 文档中的 Lambda 教程](https://aws-controllers-k8s.github.io/community/docs/tutorials/lambda-oci-example/)。

## Crossplane
<a name="kubernetes-crossplane"></a>

[Crossplane](https://docs.crossplane.io/latest/packages/providers/) 是一个开源云原生计算基金会（CNCF）项目，其使用 Kubernetes 来管理云基础设施资源。借助 Crossplane，开发人员可以请求基础设施，而无需了解其复杂性。平台团队保留对基础设施的预置和管理方式的控制。

借助 Crossplane，您可以使用首选的 Kubernetes 工具链（例如 [kubectl](https://kubernetes.io/docs/reference/kubectl/)）部署和管理 Lambda 函数，以及任何可以将清单部署到 Kubernetes 的 CI/CD 管道。以下是通过 Crossplane 预置 Lambda 函数的示例用例：
+ 您的组织想要通过确保 Lambda 函数具有正确的[标签](configuration-tags.md)来强制实施合规性。平台团队可以使用 [Crossplane Compositions](https://docs.crossplane.io/latest/get-started/get-started-with-composition/) 通过 API 抽象来定义此策略。然后，开发人员可以使用这些抽象来部署带标签的 Lambda 函数。
+ 您的项目使用将 GitOps 和 Kubernetes 结合使用。在此模型中，Kubernetes 不断将 git 存储库（所需状态）与集群内运行的资源（当前状态）进行协调。如果存在差异，GitOps 流程会自动对集群进行更改。您可以将 GitOps 与 Kubernetes 结合使用，以便借助 [CRD](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/) 和[控制器](https://kubernetes.io/docs/concepts/architecture/controller/)等熟悉的 Kubernetes 工具和概念，通过 Crossplane 部署和管理 Lambda 函数。

要了解将 Crossplane 与 Lambda 结合使用的更多信息，请参阅以下内容：
+ [AWS Blueprints for Crossplane](https://github.com/awslabs/crossplane-on-eks/blob/main/examples/upbound-aws-provider/README.md#deploy-the-examples)：此存储库包含如何使用 Crossplane 部署 AWS 资源（包括 Lambda 函数）的示例。
**注意**  
AWS Blueprints for Crossplane 正在积极开发中，不应用于生产。
+ [使用 Amazon EKS 和 Crossplane 部署 Lambda](https://www.youtube.com/watch?v=m-9KLq29K4k)：此视频演示了使用 Crossplane 部署 AWS 无服务器架构的高级示例，从开发人员和平台两方的角度探索设计。

# 结合 Amazon MQ 使用 Lambda
<a name="with-mq"></a>

**注意**  
如果想要将数据发送到 Lambda 函数以外的目标，或要在发送数据之前丰富数据，请参阅 [Amazon EventBridge Pipes](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-pipes.html)（Amazon EventBridge 管道）。

Amazon MQ 是一项托管消息代理服务，用于 [Apache ActiveMQ](https://activemq.apache.org/) 和 [RabbitMQ](https://www.rabbitmq.com)。*消息代理*允许软件应用程序和组件使用各种编程语言、操作系统和正式消息收发协议，通过主题或队列事件目标进行通信。

Amazon MQ 还可以通过安装 ActiveMQ 代理或 RabbitMQ 代理以及提供不同的网络拓扑和其他基础设施需求来代表您管理 Amazon Elastic Compute Cloud (Amazon EC2) 实例。

您可使用 Lambda 函数处理来自 Amazon MQ 消息代理的记录。Lambda 通过[事件源映射](invocation-eventsourcemapping.md)调用您的函数，事件源映射是从您的代理读取消息并[同步](invocation-sync.md)调用函数的一种 Lambda 资源。

**警告**  
Lambda 事件源映射至少处理每个事件一次，有可能出现重复处理记录的情况。为避免与重复事件相关的潜在问题，我们强烈建议您将函数代码设为幂等性。要了解更多信息，请参阅 AWS 知识中心的[如何让我的 Lambda 函数保持幂等性](https://repost.aws/knowledge-center/lambda-function-idempotent)。

Amazon MQ 事件源映射有以下配置限制：
+ 并发 – 使用 Amazon MQ 事件源映射的 Lambda 函数具有默认的最大[并发](lambda-concurrency.md)设置。对于 ActiveMQ，Lambda 服务将每个 Amazon MQ 事件源映射的并发执行环境数量限制为 5 个。对于 RabbitMQ，每个 Amazon MQ 事件源映射的并发执行环境数量限制为 1 个。即使您更改了函数的预留或预调配并发设置，Lambda 服务也不会提供更多的执行环境。要请求增加单个 Amazon MQ 事件源映射的默认最大并发数，请联系 支持 并提供事件源映射 UUID 和区域。因为是在特定的事件源映射级别而不是账户或区域级别增加，所以需要为每个事件源映射手动请求按比例增加。
+ 跨账户 – Lambda 不支持跨账户处理。您不能使用 Lambda 处理来自不同 AWS 账户 账户中的 Amazon MQ 消息代理的记录。
+ 身份验证 – 对于 ActiveMQ，仅支持 ActiveMQ [SimpleAuthenticationPlugin](https://activemq.apache.org/security#simple-authentication-plugin)。对于 RabbitMQ，仅支持 [PLAIN](https://www.rabbitmq.com/access-control.html#mechanisms) 身份验证机制。用户必须使用 AWS Secrets Manager 来管理凭据。有关 ActiveMQ 身份验证的更多信息，请参阅 *Amazon MQ 开发人员指南*中的[使用 LDAP 集成 ActiveMQ 代理](https://docs.aws.amazon.com/amazon-mq/latest/developer-guide/security-authentication-authorization.html)。
+ 连接配额 – 代理具有每个有线级协议允许的最大连接数。此配额基于代理实例类型。有关更多信息，请参阅 *Amazon MQ 开发人员指南*中的 **Amazon MQ 中的配额**的[代理](https://docs.aws.amazon.com/amazon-mq/latest/developer-guide/amazon-mq-limits.html#broker-limits)部分。
+ 连接 – 您可以在公有或私有虚拟私有云（VPC）中创建代理。对于私有 VPC，您的 Lambda 函数需要具备对 VPC 的访问权限才能接收消息。有关更多信息，请参阅此部分后面的[配置网络安全](process-mq-messages-with-lambda.md#process-mq-messages-with-lambda-networkconfiguration)。
+ 事件目标 – 仅支持队列目标。但是，您可以使用虚拟主题，虚拟主题在内部与主题行为一致，在与 Lambda 交互时与队列行为一致。有关更多信息，请参阅 Apache ActiveMQ 网站上的[虚拟目标](https://activemq.apache.org/virtual-destinations)和 RabbitMQ 网站上的[虚拟主机](https://www.rabbitmq.com/vhosts.html)。
+ 网络拓扑 – 对于 ActiveMQ，每个事件源映射仅支持一个单实例或备用代理。对于 RabbitMQ，每个事件源映射只支持一个单实例代理或集群部署。单实例代理需要一个失效转移端点。有关这些代理部署模式的更多信息，请参阅 *Amazon MQ 开发人员指南*中的 [Active MQ 代理架构](https://docs.aws.amazon.com/amazon-mq/latest/developer-guide/amazon-mq-broker-architecture.html)和[RabbitMQ 代理架构](https://docs.aws.amazon.com/amazon-mq/latest/developer-guide/rabbitmq-broker-architecture.html)。
+ 协议 – 支持的协议取决于 Amazon MQ 集成的类型。
  + 对于 ActiveMQ 集成，Lambda 使用 OpenWire/Java Message Service (JMS) 协议来使用消息。消息的使用不支持任何其他协议。在 JMS 协议中，仅支持 [https://activemq.apache.org/components/cms/api_docs/activemqcpp-3.6.0/html/classactivemq_1_1commands_1_1_active_m_q_text_message.html](https://activemq.apache.org/components/cms/api_docs/activemqcpp-3.6.0/html/classactivemq_1_1commands_1_1_active_m_q_text_message.html) 和 [https://activemq.apache.org/components/cms/api_docs/activemqcpp-3.9.0/html/classactivemq_1_1commands_1_1_active_m_q_bytes_message.html](https://activemq.apache.org/components/cms/api_docs/activemqcpp-3.9.0/html/classactivemq_1_1commands_1_1_active_m_q_bytes_message.html)。Lambda 还支持 JMS 自定义属性。有关 OpenWire 协议的更多信息，请参阅 Apache ActiveMQ 网站上的 [OpenWire](https://activemq.apache.org/openwire.html)。
  + 对于 RabbitMQ 集成，Lambda 使用 AMQP 0-9-1 协议来使用消息。消息的使用不支持任何其他协议。有关 RabbitMQ 的 AMQP 0-9-1 协议实施的详细信息，请参阅 RabbitMQ 网站上的 [AMQP 0-9-1 完整参考指南](https://www.rabbitmq.com/amqp-0-9-1-reference.html)。

Lambda 自动支持 Amazon MQ 支持的最新版本的 ActiveMQ 和 RabbitMQ。有关受支持的最新版本，请参阅 *Amazon MQ 开发人员指南*中的 [Amazon MQ 发布说明](https://docs.aws.amazon.com/amazon-mq/latest/developer-guide/amazon-mq-release-notes.html)。

**注意**  
默认情况下，Amazon MQ 代理有一个每周维护时段。代理在该时段内无法使用。对于没有备用代理的代理，Lambda 将无法在该时段处理任何消息。

**Topics**
+ [

## 了解 Amazon MQ 的 Lambda 使用者组
](#services-mq-configure)
+ [

# 为 Lambda 配置 Amazon MQ 事件源
](process-mq-messages-with-lambda.md)
+ [

# 事件源映射 API
](services-mq-params.md)
+ [

# 筛选来自 Amazon MQ 事件源的事件
](with-mq-filtering.md)
+ [

# Amazon MQ 事件源映射错误排查
](services-mq-errors.md)

## 了解 Amazon MQ 的 Lambda 使用者组
<a name="services-mq-configure"></a>

为了与 Amazon MQ 进行交互，Lambda 会创建一个可以从 Amazon MQ 代理中读取的使用者组。使用与事件源映射 UUID 相同的 ID 创建使用者组。

对于 Amazon MQ 事件源，Lambda 会将记录合并为批处理，然后通过单个有效负载中将其发送到您的函数。要控制行为，您可以配置批处理时段和批处理大小。Lambda 会持续提取消息，直到达到 6 MB 的最大有效负载大小、批处理时段过期或记录数达到完整批处理大小时为止。有关更多信息，请参阅 [批处理行为](invocation-eventsourcemapping.md#invocation-eventsourcemapping-batching)。

使用者组将消息作为字节 BLOB 进行检索，然后以 base64 格式编码为单个 JSON 有效负载，接下来调用您的函数。如果函数为批处理中的任何消息返回错误，Lambda 将重试整批消息，直到处理成功或消息过期为止。

**注意**  
尽管 Lambda 函数的最大超时限制通常为 15 分钟，但 Amazon MSK、自行管理的 Apache Kafka、Amazon DocumentDB、Amazon MQ for ActiveMQ 和 RabbitMQ 的事件源映射，仅支持最大超时限制为 14 分钟的函数。此约束可确保事件源映射可以正确处理函数错误和重试。

您可以使用 Amazon CloudWatch 中的 `ConcurrentExecutions` 指标监控给定函数的并发使用情况。有关并发的更多信息，请参阅 [为函数配置预留并发](configuration-concurrency.md)。

**Example Amazon MQ 记录事件**  

```
{
   "eventSource": "aws:mq",
   "eventSourceArn": "arn:aws:mq:us-east-2:111122223333:broker:test:b-9bcfa592-423a-4942-879d-eb284b418fc8",
   "messages": [
      { 
        "messageID": "ID:b-9bcfa592-423a-4942-879d-eb284b418fc8-1---mq---us-east-2.amazonaws.com.rproxy.goskope.com-37557-1234520418293-4:1:1:1:1", 
        "messageType": "jms/text-message",
        "deliveryMode": 1,
        "replyTo": null,
        "type": null,
        "expiration": "60000",
        "priority": 1,
        "correlationId": "myJMSCoID",
        "redelivered": false,
        "destination": { 
          "physicalName": "testQueue" 
        },
        "data":"QUJDOkFBQUE=",
        "timestamp": 1598827811958,
        "brokerInTime": 1598827811958, 
        "brokerOutTime": 1598827811959, 
        "properties": {
          "index": "1",
          "doAlarm": "false",
          "myCustomProperty": "value"
        }
      },
      { 
        "messageID": "ID:b-9bcfa592-423a-4942-879d-eb284b418fc8-1---mq---us-east-2.amazonaws.com.rproxy.goskope.com-37557-1234520418293-4:1:1:1:1",
        "messageType": "jms/bytes-message",
        "deliveryMode": 1,
        "replyTo": null,
        "type": null,
        "expiration": "60000",
        "priority": 2,
        "correlationId": "myJMSCoID1",
        "redelivered": false,
        "destination": { 
          "physicalName": "testQueue" 
        },
        "data":"LQaGQ82S48k=",
        "timestamp": 1598827811958,
        "brokerInTime": 1598827811958, 
        "brokerOutTime": 1598827811959, 
        "properties": {
          "index": "1",
          "doAlarm": "false",
          "myCustomProperty": "value"
        }
      }
   ]
}
```

```
{
  "eventSource": "aws:rmq",
  "eventSourceArn": "arn:aws:mq:us-east-2:111122223333:broker:pizzaBroker:b-9bcfa592-423a-4942-879d-eb284b418fc8",
  "rmqMessagesByQueue": {
    "pizzaQueue::/": [
      {
        "basicProperties": {
          "contentType": "text/plain",
          "contentEncoding": null,
          "headers": {
            "header1": {
              "bytes": [
                118,
                97,
                108,
                117,
                101,
                49
              ]
            },
            "header2": {
              "bytes": [
                118,
                97,
                108,
                117,
                101,
                50
              ]
            },
            "numberInHeader": 10
          },
          "deliveryMode": 1,
          "priority": 34,
          "correlationId": null,
          "replyTo": null,
          "expiration": "60000",
          "messageId": null,
          "timestamp": "Jan 1, 1970, 12:33:41 AM",
          "type": null,
          "userId": "AIDACKCEVSQ6C2EXAMPLE",
          "appId": null,
          "clusterId": null,
          "bodySize": 80
        },
        "redelivered": false,
        "data": "eyJ0aW1lb3V0IjowLCJkYXRhIjoiQ1pybWYwR3c4T3Y0YnFMUXhENEUifQ=="
      }
    ]
  }
}
```
在 RabbitMQ 示例中，`pizzaQueue` 是 RabbitMQ 队列的名称，`/` 是虚拟主机的名称。接收消息时，事件源会将消息列在 `pizzaQueue::/` 下。

# 为 Lambda 配置 Amazon MQ 事件源
<a name="process-mq-messages-with-lambda"></a>

**Topics**
+ [

## 配置网络安全
](#process-mq-messages-with-lambda-networkconfiguration)
+ [

## 创建事件源映射
](#services-mq-eventsourcemapping)

## 配置网络安全
<a name="process-mq-messages-with-lambda-networkconfiguration"></a>

要通过事件源映射向 Lambda 提供对 Amazon MQ 的完全访问权限，代理必须使用公有端点（公有 IP 地址），或者您必须提供对您在其中创建了代理的 Amazon VPC 的访问权限。

将 Amazon MQ 与 Lambda 配合使用时，创建 [AWS PrivateLink VPC 端点](https://docs.aws.amazon.com/vpc/latest/privatelink/create-interface-endpoint.html)，以便您的函数可以访问 Amazon VPC 中的资源。

**注意**  
对于使用事件轮询器的默认（按需）模式的事件源映射函数，需要 AWS PrivateLink VPC 端点。如果事件源映射使用[预调配模式](invocation-eventsourcemapping.md#invocation-eventsourcemapping-provisioned-mode)，则无需配置 AWS PrivateLink VPC 端点。

创建端点以提供对以下资源的访问权限：
+  Lambda：为 Lambda 服务主体创建端点。
+  AWS STS：为 AWS STS 创建端点，以便服务主体代您代入角色。
+  Secrets Manager：如果代理使用 Secrets Manager 来存储凭证，请为 Secrets Manager 创建端点。

也可以在 Amazon VPC 中的每个公有子网上配置 NAT 网关。有关更多信息，请参阅 [为连接到 VPC 的 Lambda 函数启用互联网访问权限](configuration-vpc-internet.md)。

为 Amazon MQ 创建事件源映射时，Lambda 会检查为 Amazon VPC 配置的子网和安全组是否已经存在弹性网络接口（ENI）。如果 Lambda 发现现有 ENI，则会尝试重用这些 ENI。否则，Lambda 会创建新的 ENI 来连接到事件源并调用函数。

**注意**  
Lambda 函数始终在 Lambda 服务拥有的 Amazon VPC 中运行。函数的 VPC 配置不会影响事件源映射。只有事件源的网络配置才能决定 Lambda 连接到事件源的方式。

为包含代理的 Amazon VPC 配置安全组。默认情况下，Amazon MQ 使用以下端口：`61617`（Amazon MQ for ActiveMQ）和 `5671`（Amazon MQ for RabbitMQ）。
+ 入站规则：允许与事件源关联安全组的默认代理端口的所有流量。或者，您可以使用自引用安全组规则允许来自同一安全组内的实例进行访问。
+ 出站规则 – 如果您的函数需要与 AWS 服务进行通信，则允许端口 `443` 上的所有流量向外部目标传输。或者，如果您不需要与其他 AWS 服务通信，也可以使用自引用的安全组规则来限制对代理的访问权限。
+ Amazon VPC 端点入站规则：如果您正在使用 Amazon VPC 端点，则与 Amazon VPC 端点关联的安全组，必须允许来自代理安全组的端口 `443` 上的入站流量。

如果代理使用身份验证，您还可以限制 Secrets Manager 端点的端点策略。要调用 Secrets Manager API，Lambda 会使用函数角色而非 Lambda 服务主体。

**Example VPC 端点策略：Secrets Manager 端点**  

```
{
      "Statement": [
          {
              "Action": "secretsmanager:GetSecretValue",
              "Effect": "Allow",
              "Principal": {
                  "AWS": [
                      "arn:aws::iam::123456789012:role/my-role"
                  ]
              },
              "Resource": "arn:aws::secretsmanager:us-west-2:123456789012:secret:my-secret"
          }
      ]
  }
```

当您使用 Amazon VPC 端点时，AWS 会使用端点的弹性网络接口（ENI）路由 API 调用来调用函数。Lambda 服务主体需要针对使用这些 ENI 的任何角色和函数调用 `lambda:InvokeFunction`。

默认情况下，Amazon VPC 端点具有开放的 IAM 策略，允许对资源进行广泛访问。最佳实践是，将这些策略限制为使用该端点执行所需的操作。为确保事件源映射能够调用 Lambda 函数，VPC 端点策略必须允许 Lambda 服务主体调用 `sts:AssumeRole` 和 `lambda:InvokeFunction`。将 VPC 端点策略限制为仅允许来自组织内部的 API 调用，会导致事件源映射无法正常运行，因此这些策略中需要 `"Resource": "*"`。

以下 VPC 端点策略示例展示了如何向 AWS STS 的 Lambda 服务主体和 Lambda 端点授予所需的访问权限。

**Example VPC 端点策略 – AWS STS 端点**  

```
{
      "Statement": [
          {
              "Action": "sts:AssumeRole",
              "Effect": "Allow",
              "Principal": {
                  "Service": [
                      "lambda.amazonaws.com"
                  ]
              },
              "Resource": "*"
          }
      ]
    }
```

**Example VPC 端点策略 – Lambda 端点**  

```
{
      "Statement": [
          {
              "Action": "lambda:InvokeFunction",
              "Effect": "Allow",
              "Principal": {
                  "Service": [
                      "lambda.amazonaws.com"
                  ]
              },
              "Resource": "*"
          }
      ]
  }
```

## 创建事件源映射
<a name="services-mq-eventsourcemapping"></a>

创建[事件源映射](invocation-eventsourcemapping.md)以指示 Lambda 将 Amazon MQ 代理中的记录发送到 Lambda 函数。您可以创建多个事件源映射，以使用多个函数处理相同的数据，或使用单个函数处理来自多个源的项目。

要将您的函数配置为从 Amazon MQ 中读取，请添加所需权限并在 Lambda 控制台中创建 **MQ** 触发器。

要从 Amazon MQ 代理读取记录，Lambda 函数需要以下权限：通过向函数[执行角色](lambda-intro-execution-role.md)添加权限语句，可以授予 Lambda 与 Amazon MQ 代理及其底层资源交互的权限：
+ [mq:DescribeBroker](https://docs.aws.amazon.com/amazon-mq/latest/api-reference/brokers-broker-id.html#brokers-broker-id-http-methods)
+ [secretsmanager:GetSecretValue](https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_GetSecretValue.html)
+ [ec2:CreateNetworkInterface](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_CreateNetworkInterface.html)
+ [ec2:DeleteNetworkInterface](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DeleteNetworkInterface.html)
+ [ec2:DescribeNetworkInterfaces](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeNetworkInterfaces.html)
+ [ec2:DescribeSecurityGroups](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeSecurityGroups.html)
+ [ec2:DescribeSubnets](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeSubnets.html)
+ [ec2:DescribeVpcs](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeVpcs.html)
+ [logs:CreateLogGroup](https://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_CreateLogGroup.html)
+ [logs:CreateLogStream](https://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_CreateLogStream.html)
+ [logs:PutLogEvents](https://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_PutLogEvents.html)

**注意**  
使用加密的客户托管密钥时，也可以添加 `[kms:Decrypt](https://docs.aws.amazon.com/msk/1.0/apireference/clusters-clusterarn-bootstrap-brokers.html#clusters-clusterarn-bootstrap-brokersget)` 权限。

**要添加权限并创建触发器**

1. 打开 Lamba 控制台的[函数](https://console.aws.amazon.com/lambda/home#/functions)页面。

1. 选择一个函数的名称。

1. 选择 **Configuration**（配置）选项卡，然后选择 **Permissions**（权限）。

1. 在**角色名称**下，选择至执行角色的链接。此角色将在 IAM 控制台中打开角色。  
![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/execution-role.png)

1. 选择**添加权限**，然后选择**创建内联策略**。  
![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/inline-policy.png)

1. 在**策略编辑器**中，选择 **JSON**。输入以下策略。您的函数需要这些权限才能从 Amazon MQ 代理读取数据。

------
#### [ JSON ]

****  

   ```
   {
       "Version":"2012-10-17",		 	 	 
       "Statement": [
         {
           "Effect": "Allow",
           "Action": [
             "mq:DescribeBroker",
             "secretsmanager:GetSecretValue",
             "ec2:CreateNetworkInterface",
             "ec2:DeleteNetworkInterface",
             "ec2:DescribeNetworkInterfaces", 
             "ec2:DescribeSecurityGroups",
             "ec2:DescribeSubnets",
             "ec2:DescribeVpcs",
             "logs:CreateLogGroup",
             "logs:CreateLogStream", 
             "logs:PutLogEvents"		
           ],
           "Resource": "*"
         }
       ]
     }
   ```

------
**注意**  
使用加密的客户托管密钥时，还必须添加 `kms:Decrypt` 权限。

1. 选择**下一步**。输入策略名称，然后选择**创建策略**。

1. 在 Lambda 控制台中返回您的函数。在 **Function overview**（函数概览）下，选择 **Add trigger**（添加触发器）。  
![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/add-trigger.png)

1. 选择 **MQ** 触发器类型。

1. 配置必填选项，然后选择 **Add**（添加）。

Lambda 支持对 Amazon MQ 事件源使用以下选项：
+ **MQ broker**（MQ 代理）– 选择 Amazon MQ 代理。
+ **Batch size**（批处理大小）– 设置要在单个批次中检索的最大消息数。
+ **Queue name**（队列名称）– 输入要使用的 Amazon MQ 队列。
+ **Source access configuration**（源访问配置）– 输入虚拟主机信息和 Secret Secrets Manager 密钥，用于存储您的代理凭证。
+ **Enable trigger**（启用触发器）– 禁用触发器以停止处理记录。

要启用或禁用触发器（或删除触发器），请在设计器中选择 **MQ** 触发器。要重新配置触发器，请使用事件源映射 API 操作。

# 事件源映射 API
<a name="services-mq-params"></a>

所有 Lambda 事件源类型共享相同的 [CreateEventSourceMapping](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html) 和 [UpdateEventSourceMapping](https://docs.aws.amazon.com/lambda/latest/api/API_UpdateEventSourceMapping.html) API 操作。但是，只有部分参数适用于 Amazon MQ 和 RabbitMQ。


| 参数 | 必需 | 默认值 | 备注 | 
| --- | --- | --- | --- | 
|  BatchSize  |  否  |  100  |  最大值：10000  | 
|  已启用  |  否  |  真实  | none | 
|  FunctionName  |  是  | 不适用  | none | 
|  FilterCriteria  |  否  |  不适用   |  [控制 Lambda 向您的函数发送的事件](invocation-eventfiltering.md)  | 
|  MaximumBatchingWindowInSeconds  |  否  |  500 毫秒  |  [批处理行为](invocation-eventsourcemapping.md#invocation-eventsourcemapping-batching)  | 
|  队列  |  否  | 不适用 |  要使用的 Amazon MQ 代理目标队列的名称。  | 
|  SourceAccessConfigurations  |  否  | 不适用  |  对于 ActiveMQ 为 BASIC\$1AUTH 凭证。对于 RabbitMQ，可以同时包含 BASIC\$1AUTH 凭证和 VIRTUAL\$1HOST 信息。  | 

# 筛选来自 Amazon MQ 事件源的事件
<a name="with-mq-filtering"></a>

您可以使用事件筛选，控制 Lambda 将流或队列中的哪些记录发送给函数。有关事件筛选工作原理的一般信息，请参阅 [控制 Lambda 向您的函数发送的事件](invocation-eventfiltering.md)。

本节重点介绍 Amazon MQ 事件源的事件筛选。

**注意**  
Amazon MQ 事件源映射仅支持对 `data` 键进行筛选。

**Topics**
+ [

## Amazon MQ 事件筛选基础知识
](#filtering-AMQ)

## Amazon MQ 事件筛选基础知识
<a name="filtering-AMQ"></a>

假设 Amazon MQ 消息队列包含有效 JSON 格式或纯字符串的消息。示例记录如下所示，`data` 字段中的数据会转换为 Base64 编码字符串。

------
#### [ ActiveMQ ]

```
{ 
    "messageID": "ID:b-9bcfa592-423a-4942-879d-eb284b418fc8-1---mq---us-east-2.amazonaws.com.rproxy.goskope.com-37557-1234520418293-4:1:1:1:1", 
    "messageType": "jms/text-message",
    "deliveryMode": 1,
    "replyTo": null,
    "type": null,
    "expiration": "60000",
    "priority": 1,
    "correlationId": "myJMSCoID",
    "redelivered": false,
    "destination": { 
      "physicalName": "testQueue" 
    },
    "data":"QUJDOkFBQUE=",
    "timestamp": 1598827811958,
    "brokerInTime": 1598827811958, 
    "brokerOutTime": 1598827811959, 
    "properties": {
      "index": "1",
      "doAlarm": "false",
      "myCustomProperty": "value"
    }
}
```

------
#### [ RabbitMQ ]

```
{
    "basicProperties": {
        "contentType": "text/plain",
        "contentEncoding": null,
        "headers": {
            "header1": {
                "bytes": [
                  118,
                  97,
                  108,
                  117,
                  101,
                  49
                ]
            },
            "header2": {
                "bytes": [
                  118,
                  97,
                  108,
                  117,
                  101,
                  50
                ]
            },
            "numberInHeader": 10
        },
        "deliveryMode": 1,
        "priority": 34,
        "correlationId": null,
        "replyTo": null,
        "expiration": "60000",
        "messageId": null,
        "timestamp": "Jan 1, 1970, 12:33:41 AM",
        "type": null,
        "userId": "AIDACKCEVSQ6C2EXAMPLE",
        "appId": null,
        "clusterId": null,
        "bodySize": 80
        },
    "redelivered": false,
    "data": "eyJ0aW1lb3V0IjowLCJkYXRhIjoiQ1pybWYwR3c4T3Y0YnFMUXhENEUifQ=="
}
```

------

对于 Active MQ 和 Rabbit MQ 代理，您可以使用 `data` 键，通过事件筛选来筛选记录。假设 Amazon MQ 队列包含以下 JSON 格式的消息。

```
{
    "timeout": 0,
    "IPAddress": "203.0.113.254"
}
```

要仅筛选 `timeout` 字段大于 0 的记录，`FilterCriteria` 对象将如下所示。

```
{
    "Filters": [
        {
            "Pattern": "{ \"data\" : { \"timeout\" : [ { \"numeric\": [ \">\", 0] } } ] } }"
        }
    ]
}
```

为了更清楚起见，以下是在纯 JSON 中展开的筛选条件 `Pattern` 的值。

```
{
    "data": {
        "timeout": [ { "numeric": [ ">", 0 ] } ]
        }
}
```

您可以使用控制台、AWS CLI 或 AWS SAM 模板添加筛选条件。

------
#### [ Console ]

要使用控制台添加此筛选条件，请按照 [将筛选条件附加到事件源映射（控制台）](invocation-eventfiltering.md#filtering-console) 中的说明，为**筛选条件**输入以下字符串。

```
{ "data" : { "timeout" : [ { "numeric": [ ">", 0 ] } ] } }
```

------
#### [ AWS CLI ]

要使用 AWS Command Line Interface（AWS CLI）创建包含这些筛选条件的新事件源映射，请运行以下命令。

```
aws lambda create-event-source-mapping \
    --function-name my-function \
    --event-source-arn arn:aws:mq:us-east-2:123456789012:broker:my-broker:b-8ac7cc01-5898-482d-be2f-a6b596050ea8 \
    --filter-criteria '{"Filters": [{"Pattern": "{ \"data\" : { \"timeout\" : [ { \"numeric\": [ \">\", 0 ] } ] } }"}]}'
```

要将这些筛选条件添加到现有事件源映射中，请运行以下命令。

```
aws lambda update-event-source-mapping \
    --uuid "a1b2c3d4-5678-90ab-cdef-11111EXAMPLE" \
    --filter-criteria '{"Filters": [{"Pattern": "{ \"data\" : { \"timeout\" : [ { \"numeric\": [ \">\", 0 ] } ] } }"}]}'
```

要将这些筛选条件添加到现有事件源映射中，请运行以下命令。

```
aws lambda update-event-source-mapping \
    --uuid "a1b2c3d4-5678-90ab-cdef-11111EXAMPLE" \
    --filter-criteria '{"Filters": [{"Pattern": "{ \"data\" : { \"timeout\" : [ { \"numeric\": [ \">\", 0 ] } ] } }"}]}'
```

------
#### [ AWS SAM ]

要使用 AWS SAM 添加此筛选条件，请将以下代码段添加到事件源的 YAML 模板中。

```
FilterCriteria:
  Filters:
    - Pattern: '{ "data" : { "timeout" : [ { "numeric": [ ">", 0 ] } ] } }'
```

------

使用 Amazon MQ，您还可以筛选消息为纯字符串的记录。假设您只想处理消息以“结果：”开头的记录。`FilterCriteria` 对象将如下所示。

```
{
    "Filters": [
        {
            "Pattern": "{ \"data\" : [ { \"prefix\": \"Result: \" } ] }"
        }
    ]
}
```

为了更清楚起见，以下是在纯 JSON 中展开的筛选条件 `Pattern` 的值。

```
{
    "data": [
        {
        "prefix": "Result: "
        }
    ]
}
```

您可以使用控制台、AWS CLI 或 AWS SAM 模板添加筛选条件。

------
#### [ Console ]

要使用控制台添加此筛选条件，请按照 [将筛选条件附加到事件源映射（控制台）](invocation-eventfiltering.md#filtering-console) 中的说明，为**筛选条件**输入以下字符串。

```
{ "data" : [ { "prefix": "Result: " } ] }
```

------
#### [ AWS CLI ]

要使用 AWS Command Line Interface（AWS CLI）创建包含这些筛选条件的新事件源映射，请运行以下命令。

```
aws lambda create-event-source-mapping \
    --function-name my-function \
    --event-source-arn arn:aws:mq:us-east-2:123456789012:broker:my-broker:b-8ac7cc01-5898-482d-be2f-a6b596050ea8 \
    --filter-criteria '{"Filters": [{"Pattern": "{ \"data\" : [ { \"prefix\": \"Result: \" } ] }"}]}'
```

要将这些筛选条件添加到现有事件源映射中，请运行以下命令。

```
aws lambda update-event-source-mapping \
    --uuid "a1b2c3d4-5678-90ab-cdef-11111EXAMPLE" \
    --filter-criteria '{"Filters": [{"Pattern": "{ \"data\" : [ { \"prefix\": \"Result: \" } ] }"}]}'
```

------
#### [ AWS SAM ]

要使用 AWS SAM 添加此筛选条件，请将以下代码段添加到事件源的 YAML 模板中。

```
FilterCriteria:
  Filters:
    - Pattern: '{ "data" : [ { "prefix": "Result " } ] }'
```

------

Amazon MQ 消息必须是 UTF-8 编码的字符串，可以是纯字符串或 JSON 格式。这是因为 Lambda 在应用筛选条件之前将 Amazon MQ 字节数组解码为 UTF-8。如果您的消息使用另一种编码，例如 UTF-16 或 ASCII，或者消息格式与 `FilterCriteria` 格式不匹配，则 Lambda 仅处理元数据筛选条件。下表汇总了具体行为：


| 传入消息格式 | 消息属性的筛选条件模式格式 | 导致的操作 | 
| --- | --- | --- | 
|  纯字符串  |  纯字符串  |  Lambda 根据您的筛选条件进行筛选。  | 
|  纯字符串  |  数据属性中没有筛选条件模式  |  Lambda 根据您的筛选条件进行筛选（仅限其他元数据属性）。  | 
|  纯字符串  |  有效 JSON  |  Lambda 根据您的筛选条件进行筛选（仅限其他元数据属性）。  | 
|  有效 JSON  |  纯字符串  |  Lambda 根据您的筛选条件进行筛选（仅限其他元数据属性）。  | 
|  有效 JSON  |  数据属性中没有筛选条件模式  |  Lambda 根据您的筛选条件进行筛选（仅限其他元数据属性）。  | 
|  有效 JSON  |  有效 JSON  |  Lambda 根据您的筛选条件进行筛选。  | 
|  非 UTF-8 编码字符串  |  JSON、纯字符串或无模式  |  Lambda 根据您的筛选条件进行筛选（仅限其他元数据属性）。  | 

# Amazon MQ 事件源映射错误排查
<a name="services-mq-errors"></a>

当 Lambda 函数遇到不可恢复的错误时，您的 Amazon MQ 使用者将停止处理记录。任何其他使用者如果没有遇到相同的错误，都可以继续处理。要确定使用者停止的潜在原因，请检查 `StateTransitionReason` 返回的详细信息中的 `EventSourceMapping` 字段中是否有以下代码：

**`ESM_CONFIG_NOT_VALID`**  
事件源映射配置无效。

**`EVENT_SOURCE_AUTHN_ERROR`**  
Lambda 验证事件源失败。

**`EVENT_SOURCE_AUTHZ_ERROR`**  
Lambda 没有访问事件源所需的权限。

**`FUNCTION_CONFIG_NOT_VALID`**  
函数的配置无效。

如果记录由于其大小而被 Lambda 丢弃，也将处于未处理状态。Lambda 记录的大小限制为 6 MB。要在函数出错时重新传递消息，您可以使用死信队列 (DLQ)。有关更多信息，请参阅 Apache ActiveMQ 网站上的[消息重新传递和 DLQ 处理](https://activemq.apache.org/message-redelivery-and-dlq-handling)和 RabbitMQ 网站上的[可靠性指南](https://www.rabbitmq.com/reliability.html)。

**注意**  
Lambda 不支持自定义重新传递策略。相反，Lambda 使用一个策略，其默认值来自 Apache ActiveMQ 网站上的[重新传递策略](https://activemq.apache.org/redelivery-policy)页面。其中，`maximumRedeliveries` 设置为 6。

# 将 AWS Lambda 与 Amazon RDS 结合使用
<a name="services-rds"></a>

您可以将 Lambda 函数直接连接到 Amazon Relational Database Service（Amazon RDS），也可以通过 Amazon RDS 代理连接。直接连接适用于简单的场景，而在生产中则推荐使用代理。数据库代理管理共享数据库连接池，从而让函数能够在不耗尽数据库连接的情况下达到高并发级别。

对于频繁建立短数据库连接或打开和关闭大量数据库连接的 Lambda 函数，我们建议使用 Amazon RDS 代理。有关更多信息，请参阅《Amazon Relational Database Service 开发人员指南》中的[自动连接 Lambda 函数和数据库实例](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/lambda-rds-connect.html)。

**提示**  
要将 Lambda 函数快速连接到 Amazon RDS 数据库，您可以使用控制台内的引导式向导。要打开向导，执行以下操作：  
打开 Lamba 控制台的[函数](https://console.aws.amazon.com/lambda/home#/functions)页面。
选择要将数据库连接到其中的函数。
在**配置**选项卡上，选择 **RDS 数据库**。
选择**连接到 RDS 数据库**。
将函数连接到数据库后，您可以通过选择**添加代理**来创建代理。

## 配置函数以使用 RDS 资源
<a name="rds-configuration"></a>

在 Lambda 控制台中，您可以预置和配置 Amazon RDS 数据库实例和代理资源。您可以导航到**配置**选项卡下的 **RDS 数据库**完成此操作。或者，您也可以在 Amazon RDS 控制台中创建和配置到 Lambda 函数的连接。配置 RDS 数据库实例以便与 Lambda 配合使用时，请注意以下条件：
+ 要连接到数据库，您的函数必须位于数据库在其中运行的 Amazon VPC。
+ 您可以将 Amazon RDS 数据库与 MySQL、MariaDB、PostgreSQL 或 Microsoft SQL Server 引擎一起使用。
+ 您也可以将 Aurora 数据库集群与 MySQL 或 PostgreSQL 引擎一起使用。
+ 您需要提供用于数据库身份验证的 Secrets Manager 密钥。
+ IAM 角色必须提供使用密钥的权限和必须允许 Amazon RDS 代入角色的信任策略。
+  使用控制台配置 Amazon RDS 资源并将其连接到函数的 IAM 主体必须具有以下权限：

### 权限策略示例
<a name="rds-lambda-permissions"></a>

**注意**  
 只有在配置 Amazon RDS 代理来管理数据库连接池时，才需要 Amazon RDS 代理权限。

------
#### [ JSON ]

****  

```
{
  "Version":"2012-10-17",		 	 	 
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ec2:CreateSecurityGroup",
        "ec2:DescribeSecurityGroups",
        "ec2:DescribeSubnets",
        "ec2:DescribeVpcs",
        "ec2:AuthorizeSecurityGroupIngress",
        "ec2:AuthorizeSecurityGroupEgress",
        "ec2:RevokeSecurityGroupEgress",
        "ec2:CreateNetworkInterface",
        "ec2:DeleteNetworkInterface",
        "ec2:DescribeNetworkInterfaces"
      ],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "rds-db:connect",
        "rds:CreateDBProxy",
        "rds:CreateDBInstance",
        "rds:CreateDBSubnetGroup",
        "rds:DescribeDBClusters",
        "rds:DescribeDBInstances",
        "rds:DescribeDBSubnetGroups",
        "rds:DescribeDBProxies",
        "rds:DescribeDBProxyTargets",
        "rds:DescribeDBProxyTargetGroups",
        "rds:RegisterDBProxyTargets",
        "rds:ModifyDBInstance",
        "rds:ModifyDBProxy"
      ],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "lambda:CreateFunction",
        "lambda:ListFunctions",
        "lambda:UpdateFunctionConfiguration"
      ],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "iam:AttachRolePolicy",
        "iam:CreateRole",
        "iam:CreatePolicy"
      ],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "secretsmanager:GetResourcePolicy",
        "secretsmanager:GetSecretValue",
        "secretsmanager:DescribeSecret",
        "secretsmanager:ListSecretVersionIds",
        "secretsmanager:CreateSecret"
      ],
      "Resource": "*"
    }
  ]
}
```

------

Amazon RDS 根据数据库实例大小按小时收取代理费率，详情请参阅 [RDS 代理定价](https://aws.amazon.com/rds/proxy/pricing/)。有关通用代理连接的更多信息，请参阅《Amazon RDS 用户指南》中的[使用 Amazon RDS 代理](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-proxy.html)。

### Amazon RDS 连接的 SSL/TLS 要求
<a name="rds-lambda-certificates"></a>

要与 Amazon RDS 数据库实例建立安全的 SSL/TLS 连接，您的 Lambda 函数必须使用可信证书来验证数据库服务器的身份。Lambda 会根据您的部署包类型以不同的方式处理这些证书：
+ [.zip 文件存档](configuration-function-zip.md)：证书处理因运行时而异：
  + **Node.js 18 及更早版本**：Lambda 会自动包含 CA 证书和 RDS 证书。
  + **Node.js 20 及更高版本**：Lambda 不再默认加载其他 CA 证书。将 `NODE_EXTRA_CA_CERTS` 环境变量设置为 `/var/runtime/ca-cert.pem`。

  将新 AWS 区域的 Amazon RDS 证书添加到 Lambda 托管运行时可能需要长达 4 周的时间。
+ [容器映像](images-create.md)：AWS 基础映像仅包含 CA 证书。如果函数连接到 Amazon RDS 数据库实例，则必须在容器映像中包含相应的证书。在您的 Dockerfile 中，下载[与托管数据库的 AWS 区域相对应的证书捆绑包](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.SSL.html#UsingWithRDS.SSL.CertificatesDownload)。示例：

  ```
  RUN curl https://truststore.pki.rds.amazonaws.com/us-east-1/us-east-1-bundle.pem -o /us-east-1-bundle.pem
  ```

此命令可下载 Amazon RDS 证书捆绑包并将其保存在容器根目录的绝对路径 `/us-east-1-bundle.pem` 下。在函数代码中配置数据库连接时，必须引用此确切路径。示例：

------
#### [ Node.js ]

`readFileSync` 函数是必需的，因为 Node.js 数据库客户端需要内存中的实际证书内容，而不仅仅是证书文件的路径。没有 `readFileSync`，客户端会将路径字符串解释为证书内容，从而导致“证书链中的自签名证书”错误。

**Example OCI 函数的 Node.js 连接配置**  

```
import { readFileSync } from 'fs';

// ...

let connectionConfig = {
    host: process.env.ProxyHostName,
    user: process.env.DBUserName,
    password: token,
    database: process.env.DBName,
    ssl: {
        ca: readFileSync('/us-east-1-bundle.pem') // Load RDS certificate content from file into memory
    }
};
```

------
#### [ Python ]

**Example OCI 函数的 Python 连接配置**  

```
connection = pymysql.connect(
    host=proxy_host_name,
    user=db_username,
    password=token,
    db=db_name,
    port=port,
    ssl={'ca': '/us-east-1-bundle.pem'}  #Path to the certificate in container
)
```

------
#### [ Java ]

对于使用 JDBC 连接的 Java 函数，连接字符串必须包含：
+ `useSSL=true`
+ `requireSSL=true`
+ 指向容器映像中 Amazon RDS 证书位置的 `sslCA` 参数

**Example OCI 函数的 Java 连接字符串**  

```
// Define connection string
String connectionString = String.format("jdbc:mysql://%s:%s/%s?useSSL=true&requireSSL=true&sslCA=/us-east-1-bundle.pem", // Path to the certificate in container
        System.getenv("ProxyHostName"),
        System.getenv("Port"),
        System.getenv("DBName"));
```

------
#### [ .NET ]

**Example OCI 函数中用于 MySQL 连接的 .NET 连接字符串**  

```
/// Build the Connection String with the Token 
string connectionString = $"Server={Environment.GetEnvironmentVariable("RDS_ENDPOINT")};" +
                         $"Port={Environment.GetEnvironmentVariable("RDS_PORT")};" +
                         $"Uid={Environment.GetEnvironmentVariable("RDS_USERNAME")};" +
                         $"Pwd={authToken};" +
                         "SslMode=Required;" +
                         "SslCa=/us-east-1-bundle.pem";  // Path to the certificate in container
```

------
#### [ Go ]

对于使用 MySQL 连接的 Go 函数，请将 Amazon RDS 证书加载到证书池中，并将其注册到 MySQL 驱动程序。然后，连接字符串必须使用 `tls` 参数来引用此配置。

**Example OCI 函数中用于 MySQL 连接的 Go 代码**  

```
import (
    "crypto/tls"
    "crypto/x509"
    "os"
    "github.com/go-sql-driver/mysql"
)

...

// Create certificate pool and register TLS config
rootCertPool := x509.NewCertPool()
pem, err := os.ReadFile("/us-east-1-bundle.pem")  // Path to the certificate in container
if err != nil {
    panic("failed to read certificate file: " + err.Error())
}
if ok := rootCertPool.AppendCertsFromPEM(pem); !ok {
    panic("failed to append PEM")
}

mysql.RegisterTLSConfig("custom", &tls.Config{
    RootCAs: rootCertPool,
})

dsn := fmt.Sprintf("%s:%s@tcp(%s)/%s?allowCleartextPasswords=true&tls=custom",
    dbUser, authenticationToken, dbEndpoint, dbName,
)
```

------
#### [ Ruby ]

**Example OCI 函数的 Ruby 连接配置**  

```
conn = Mysql2::Client.new(
    host: endpoint,
    username: user,
    password: token,
    port: port,
    database: db_name,
    sslca: '/us-east-1-bundle.pem',  # Path to the certificate in container
    sslverify: true
)
```

------

## 使用 Lambda 函数连接到 Amazon RDS 数据库
<a name="rds-connection"></a>

以下代码示例显示了如何实现连接到 Amazon RDS 数据库的 Lambda 函数。该函数发出一个简单的数据库请求并返回结果。

**注意**  
这些代码示例仅适用于 [.zip 部署包](configuration-function-zip.md)。如果您使用[容器映像](images-create.md)来部署函数，则必须在函数代码中指定 Amazon RDS 证书文件，如[上一节](#oci-certificate)所述。

------
#### [ .NET ]

**适用于 .NET 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/lambda-function-connect-rds-iam)存储库中查找完整示例，并了解如何进行设置和运行。
在 Lambda 函数中使用 .NET 连接到 Amazon RDS 数据库。  

```
using System.Data;
using System.Text.Json;
using Amazon.Lambda.APIGatewayEvents;
using Amazon.Lambda.Core;
using MySql.Data.MySqlClient;

// Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]

namespace aws_rds;

public class InputModel
{
    public string key1 { get; set; }
    public string key2 { get; set; }
}

public class Function
{
    /// <summary>
    // Handles the Lambda function execution for connecting to RDS using IAM authentication.
    /// </summary>
    /// <param name="input">The input event data passed to the Lambda function</param>
    /// <param name="context">The Lambda execution context that provides runtime information</param>
    /// <returns>A response object containing the execution result</returns>

    public async Task<APIGatewayProxyResponse> FunctionHandler(APIGatewayProxyRequest request, ILambdaContext context)
    {
        // Sample Input: {"body": "{\"key1\":\"20\", \"key2\":\"25\"}"}
        var input = JsonSerializer.Deserialize<InputModel>(request.Body);

        /// Obtain authentication token
        var authToken = RDSAuthTokenGenerator.GenerateAuthToken(
            Environment.GetEnvironmentVariable("RDS_ENDPOINT"),
            Convert.ToInt32(Environment.GetEnvironmentVariable("RDS_PORT")),
            Environment.GetEnvironmentVariable("RDS_USERNAME")
        );

        /// Build the Connection String with the Token 
        string connectionString = $"Server={Environment.GetEnvironmentVariable("RDS_ENDPOINT")};" +
                                  $"Port={Environment.GetEnvironmentVariable("RDS_PORT")};" +
                                  $"Uid={Environment.GetEnvironmentVariable("RDS_USERNAME")};" +
                                  $"Pwd={authToken};";


        try
        {
            await using var connection = new MySqlConnection(connectionString);
            await connection.OpenAsync();

            const string sql = "SELECT @param1 + @param2 AS Sum";

            await using var command = new MySqlCommand(sql, connection);
            command.Parameters.AddWithValue("@param1", int.Parse(input.key1 ?? "0"));
            command.Parameters.AddWithValue("@param2", int.Parse(input.key2 ?? "0"));

            await using var reader = await command.ExecuteReaderAsync();
            if (await reader.ReadAsync())
            {
                int result = reader.GetInt32("Sum");

                //Sample Response: {"statusCode":200,"body":"{\"message\":\"The sum is: 45\"}","isBase64Encoded":false}
                return new APIGatewayProxyResponse
                {
                    StatusCode = 200,
                    Body = JsonSerializer.Serialize(new { message = $"The sum is: {result}" })
                };
            }

        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error: {ex.Message}");
        }

        return new APIGatewayProxyResponse
        {
            StatusCode = 500,
            Body = JsonSerializer.Serialize(new { error = "Internal server error" })
        };
    }
}
```

------
#### [ Go ]

**适用于 Go 的 SDK V2**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/lambda-function-connect-rds-iam)存储库中查找完整示例，并了解如何进行设置和运行。
在 Lambda 函数中使用 Go 连接到 Amazon RDS 数据库。  

```
/*
Golang v2 code here.
*/

package main

import (
	"context"
	"database/sql"
	"encoding/json"
	"fmt"
	"os"

	"github.com/aws/aws-lambda-go/lambda"
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/aws/aws-sdk-go-v2/feature/rds/auth"
	_ "github.com/go-sql-driver/mysql"
)

type MyEvent struct {
	Name string `json:"name"`
}

func HandleRequest(event *MyEvent) (map[string]interface{}, error) {

	var dbName string = os.Getenv("DatabaseName")
	var dbUser string = os.Getenv("DatabaseUser")
	var dbHost string = os.Getenv("DBHost") // Add hostname without https
	var dbPort int = os.Getenv("Port")      // Add port number
	var dbEndpoint string = fmt.Sprintf("%s:%d", dbHost, dbPort)
	var region string = os.Getenv("AWS_REGION")

	cfg, err := config.LoadDefaultConfig(context.TODO())
	if err != nil {
		panic("configuration error: " + err.Error())
	}

	authenticationToken, err := auth.BuildAuthToken(
		context.TODO(), dbEndpoint, region, dbUser, cfg.Credentials)
	if err != nil {
		panic("failed to create authentication token: " + err.Error())
	}

	dsn := fmt.Sprintf("%s:%s@tcp(%s)/%s?tls=true&allowCleartextPasswords=true",
		dbUser, authenticationToken, dbEndpoint, dbName,
	)

	db, err := sql.Open("mysql", dsn)
	if err != nil {
		panic(err)
	}

	defer db.Close()

	var sum int
	err = db.QueryRow("SELECT ?+? AS sum", 3, 2).Scan(&sum)
	if err != nil {
		panic(err)
	}
	s := fmt.Sprint(sum)
	message := fmt.Sprintf("The selected sum is: %s", s)

	messageBytes, err := json.Marshal(message)
	if err != nil {
		return nil, err
	}

	messageString := string(messageBytes)
	return map[string]interface{}{
		"statusCode": 200,
		"headers":    map[string]string{"Content-Type": "application/json"},
		"body":       messageString,
	}, nil
}

func main() {
	lambda.Start(HandleRequest)
}
```

------
#### [ Java ]

**适用于 Java 的 SDK 2.x**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/lambda-function-connect-rds-iam)存储库中查找完整示例，并了解如何进行设置和运行。
在 Lambda 函数中使用 Java 连接到 Amazon RDS 数据库。  

```
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.rdsdata.RdsDataClient;
import software.amazon.awssdk.services.rdsdata.model.ExecuteStatementRequest;
import software.amazon.awssdk.services.rdsdata.model.ExecuteStatementResponse;
import software.amazon.awssdk.services.rdsdata.model.Field;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

public class RdsLambdaHandler implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {

    @Override
    public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent event, Context context) {
        APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent();

        try {
            // Obtain auth token
            String token = createAuthToken();

            // Define connection configuration
            String connectionString = String.format("jdbc:mysql://%s:%s/%s?useSSL=true&requireSSL=true",
                    System.getenv("ProxyHostName"),
                    System.getenv("Port"),
                    System.getenv("DBName"));

            // Establish a connection to the database
            try (Connection connection = DriverManager.getConnection(connectionString, System.getenv("DBUserName"), token);
                 PreparedStatement statement = connection.prepareStatement("SELECT ? + ? AS sum")) {

                statement.setInt(1, 3);
                statement.setInt(2, 2);

                try (ResultSet resultSet = statement.executeQuery()) {
                    if (resultSet.next()) {
                        int sum = resultSet.getInt("sum");
                        response.setStatusCode(200);
                        response.setBody("The selected sum is: " + sum);
                    }
                }
            }

        } catch (Exception e) {
            response.setStatusCode(500);
            response.setBody("Error: " + e.getMessage());
        }

        return response;
    }

    private String createAuthToken() {
        // Create RDS Data Service client
        RdsDataClient rdsDataClient = RdsDataClient.builder()
                .region(Region.of(System.getenv("AWS_REGION")))
                .credentialsProvider(DefaultCredentialsProvider.create())
                .build();

        // Define authentication request
        ExecuteStatementRequest request = ExecuteStatementRequest.builder()
                .resourceArn(System.getenv("ProxyHostName"))
                .secretArn(System.getenv("DBUserName"))
                .database(System.getenv("DBName"))
                .sql("SELECT 'RDS IAM Authentication'")
                .build();

        // Execute request and obtain authentication token
        ExecuteStatementResponse response = rdsDataClient.executeStatement(request);
        Field tokenField = response.records().get(0).get(0);

        return tokenField.stringValue();
    }
}
```

------
#### [ JavaScript ]

**SDK for JavaScript（v3）**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/lambda-function-connect-rds-iam)存储库中查找完整示例，并了解如何进行设置和运行。
在 Lambda 函数中使用 JavaScript 连接到 Amazon RDS 数据库。  

```
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
/* 
Node.js code here.
*/
// ES6+ example
import { Signer } from "@aws-sdk/rds-signer";
import mysql from 'mysql2/promise';

async function createAuthToken() {
  // Define connection authentication parameters
  const dbinfo = {

    hostname: process.env.ProxyHostName,
    port: process.env.Port,
    username: process.env.DBUserName,
    region: process.env.AWS_REGION,

  }

  // Create RDS Signer object
  const signer = new Signer(dbinfo);

  // Request authorization token from RDS, specifying the username
  const token = await signer.getAuthToken();
  return token;
}

async function dbOps() {

  // Obtain auth token
  const token = await createAuthToken();
  // Define connection configuration
  let connectionConfig = {
    host: process.env.ProxyHostName,
    user: process.env.DBUserName,
    password: token,
    database: process.env.DBName,
    ssl: 'Amazon RDS'
  }
  // Create the connection to the DB
  const conn = await mysql.createConnection(connectionConfig);
  // Obtain the result of the query
  const [res,] = await conn.execute('select ?+? as sum', [3, 2]);
  return res;

}

export const handler = async (event) => {
  // Execute database flow
  const result = await dbOps();
  // Return result
  return {
    statusCode: 200,
    body: JSON.stringify("The selected sum is: " + result[0].sum)
  }
};
```
在 Lambda 函数中使用 TypeScript 连接到 Amazon RDS 数据库。  

```
import { Signer } from "@aws-sdk/rds-signer";
import mysql from 'mysql2/promise';

// RDS settings
// Using '!' (non-null assertion operator) to tell the TypeScript compiler that the DB settings are not null or undefined,
const proxy_host_name = process.env.PROXY_HOST_NAME!
const port = parseInt(process.env.PORT!)
const db_name = process.env.DB_NAME!
const db_user_name = process.env.DB_USER_NAME!
const aws_region = process.env.AWS_REGION!


async function createAuthToken(): Promise<string> {

    // Create RDS Signer object
    const signer = new Signer({
        hostname: proxy_host_name,
        port: port,
        region: aws_region,
        username: db_user_name
    });

    // Request authorization token from RDS, specifying the username
    const token = await signer.getAuthToken();
    return token;
}

async function dbOps(): Promise<mysql.QueryResult | undefined> {
    try {
        // Obtain auth token
        const token = await createAuthToken();
        const conn = await mysql.createConnection({
            host: proxy_host_name,
            user: db_user_name,
            password: token,
            database: db_name,
            ssl: 'Amazon RDS' // Ensure you have the CA bundle for SSL connection
        });
        const [rows, fields] = await conn.execute('SELECT ? + ? AS sum', [3, 2]);
        console.log('result:', rows);
        return rows;
    }
    catch (err) {
        console.log(err);
    }
}

export const lambdaHandler = async (event: any): Promise<{ statusCode: number; body: string }> => {
    // Execute database flow
    const result = await dbOps();

    // Return error is result is undefined
    if (result == undefined)
        return {
            statusCode: 500,
            body: JSON.stringify(`Error with connection to DB host`)
        }

    // Return result
    return {
        statusCode: 200,
        body: JSON.stringify(`The selected sum is: ${result[0].sum}`)
    };
};
```

------
#### [ PHP ]

**适用于 PHP 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/lambda-function-connect-rds-iam)存储库中查找完整示例，并了解如何进行设置和运行。
在 Lambda 函数中使用 PHP 连接到 Amazon RDS 数据库。  

```
<?php
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0

# using bref/bref and bref/logger for simplicity

use Bref\Context\Context;
use Bref\Event\Handler as StdHandler;
use Bref\Logger\StderrLogger;
use Aws\Rds\AuthTokenGenerator;
use Aws\Credentials\CredentialProvider;

require __DIR__ . '/vendor/autoload.php';

class Handler implements StdHandler
{
    private StderrLogger $logger;
    public function __construct(StderrLogger $logger)
    {
        $this->logger = $logger;
    }


    private function getAuthToken(): string {
        // Define connection authentication parameters
        $dbConnection = [
            'hostname' => getenv('DB_HOSTNAME'),
            'port' => getenv('DB_PORT'),
            'username' => getenv('DB_USERNAME'),
            'region' => getenv('AWS_REGION'),
        ];

        // Create RDS AuthTokenGenerator object
        $generator = new AuthTokenGenerator(CredentialProvider::defaultProvider());

        // Request authorization token from RDS, specifying the username
        return $generator->createToken(
            $dbConnection['hostname'] . ':' . $dbConnection['port'],
            $dbConnection['region'],
            $dbConnection['username']
        );
    }

    private function getQueryResults() {
        // Obtain auth token
        $token = $this->getAuthToken();

        // Define connection configuration
        $connectionConfig = [
            'host' => getenv('DB_HOSTNAME'),
            'user' => getenv('DB_USERNAME'),
            'password' => $token,
            'database' => getenv('DB_NAME'),
        ];

        // Create the connection to the DB
        $conn = new PDO(
            "mysql:host={$connectionConfig['host']};dbname={$connectionConfig['database']}",
            $connectionConfig['user'],
            $connectionConfig['password'],
            [
                PDO::MYSQL_ATTR_SSL_CA => '/path/to/rds-ca-2019-root.pem',
                PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => true,
            ]
        );

        // Obtain the result of the query
        $stmt = $conn->prepare('SELECT ?+? AS sum');
        $stmt->execute([3, 2]);

        return $stmt->fetch(PDO::FETCH_ASSOC);
    }

    /**
     * @param mixed $event
     * @param Context $context
     * @return array
     */
    public function handle(mixed $event, Context $context): array
    {
        $this->logger->info("Processing query");

        // Execute database flow
        $result = $this->getQueryResults();

        return [
            'sum' => $result['sum']
        ];
    }
}

$logger = new StderrLogger();
return new Handler($logger);
```

------
#### [ Python ]

**适用于 Python 的 SDK（Boto3）**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/lambda-function-connect-rds-iam)存储库中查找完整示例，并了解如何进行设置和运行。
在 Lambda 函数中使用 Python 连接到 Amazon RDS 数据库。  

```
import json
import os
import boto3
import pymysql

# RDS settings
proxy_host_name = os.environ['PROXY_HOST_NAME']
port = int(os.environ['PORT'])
db_name = os.environ['DB_NAME']
db_user_name = os.environ['DB_USER_NAME']
aws_region = os.environ['AWS_REGION']


# Fetch RDS Auth Token
def get_auth_token():
    client = boto3.client('rds')
    token = client.generate_db_auth_token(
        DBHostname=proxy_host_name,
        Port=port
        DBUsername=db_user_name
        Region=aws_region
    )
    return token

def lambda_handler(event, context):
    token = get_auth_token()
    try:
        connection = pymysql.connect(
            host=proxy_host_name,
            user=db_user_name,
            password=token,
            db=db_name,
            port=port,
            ssl={'ca': 'Amazon RDS'}  # Ensure you have the CA bundle for SSL connection
        )
        
        with connection.cursor() as cursor:
            cursor.execute('SELECT %s + %s AS sum', (3, 2))
            result = cursor.fetchone()

        return result
        
    except Exception as e:
        return (f"Error: {str(e)}")  # Return an error message if an exception occurs
```

------
#### [ Ruby ]

**适用于 Ruby 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/lambda-function-connect-rds-iam)存储库中查找完整示例，并了解如何进行设置和运行。
在 Lambda 函数中使用 Ruby 连接到 Amazon RDS 数据库。  

```
# Ruby code here.

require 'aws-sdk-rds'
require 'json'
require 'mysql2'

def lambda_handler(event:, context:)
  endpoint = ENV['DBEndpoint'] # Add the endpoint without https"
  port = ENV['Port']           # 3306
  user = ENV['DBUser']
  region = ENV['DBRegion']     # 'us-east-1'
  db_name = ENV['DBName']

  credentials = Aws::Credentials.new(
    ENV['AWS_ACCESS_KEY_ID'],
    ENV['AWS_SECRET_ACCESS_KEY'],
    ENV['AWS_SESSION_TOKEN']
  )
  rds_client = Aws::RDS::AuthTokenGenerator.new(
    region: region, 
    credentials: credentials
  )

  token = rds_client.auth_token(
    endpoint: endpoint+ ':' + port,
    user_name: user,
    region: region
  )

  begin
    conn = Mysql2::Client.new(
      host: endpoint,
      username: user,
      password: token,
      port: port,
      database: db_name,
      sslca: '/var/task/global-bundle.pem', 
      sslverify: true,
      enable_cleartext_plugin: true
    )
    a = 3
    b = 2
    result = conn.query("SELECT #{a} + #{b} AS sum").first['sum']
    puts result
    conn.close
    {
      statusCode: 200,
      body: result.to_json
    }
  rescue => e
    puts "Database connection failed due to #{e}"
  end
end
```

------
#### [ Rust ]

**适用于 Rust 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/lambda-function-connect-rds-iam)存储库中查找完整示例，并了解如何进行设置和运行。
在 Lambda 函数中使用 Rust 连接到 Amazon RDS 数据库。  

```
use aws_config::BehaviorVersion;
use aws_credential_types::provider::ProvideCredentials;
use aws_sigv4::{
    http_request::{sign, SignableBody, SignableRequest, SigningSettings},
    sign::v4,
};
use lambda_runtime::{run, service_fn, Error, LambdaEvent};
use serde_json::{json, Value};
use sqlx::postgres::PgConnectOptions;
use std::env;
use std::time::{Duration, SystemTime};

const RDS_CERTS: &[u8] = include_bytes!("global-bundle.pem");

async fn generate_rds_iam_token(
    db_hostname: &str,
    port: u16,
    db_username: &str,
) -> Result<String, Error> {
    let config = aws_config::load_defaults(BehaviorVersion::v2024_03_28()).await;

    let credentials = config
        .credentials_provider()
        .expect("no credentials provider found")
        .provide_credentials()
        .await
        .expect("unable to load credentials");
    let identity = credentials.into();
    let region = config.region().unwrap().to_string();

    let mut signing_settings = SigningSettings::default();
    signing_settings.expires_in = Some(Duration::from_secs(900));
    signing_settings.signature_location = aws_sigv4::http_request::SignatureLocation::QueryParams;

    let signing_params = v4::SigningParams::builder()
        .identity(&identity)
        .region(&region)
        .name("rds-db")
        .time(SystemTime::now())
        .settings(signing_settings)
        .build()?;

    let url = format!(
        "https://{db_hostname}:{port}/?Action=connect&DBUser={db_user}",
        db_hostname = db_hostname,
        port = port,
        db_user = db_username
    );

    let signable_request =
        SignableRequest::new("GET", &url, std::iter::empty(), SignableBody::Bytes(&[]))
            .expect("signable request");

    let (signing_instructions, _signature) =
        sign(signable_request, &signing_params.into())?.into_parts();

    let mut url = url::Url::parse(&url).unwrap();
    for (name, value) in signing_instructions.params() {
        url.query_pairs_mut().append_pair(name, &value);
    }

    let response = url.to_string().split_off("https://".len());

    Ok(response)
}

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

async fn handler(_event: LambdaEvent<Value>) -> Result<Value, Error> {
    let db_host = env::var("DB_HOSTNAME").expect("DB_HOSTNAME must be set");
    let db_port = env::var("DB_PORT")
        .expect("DB_PORT must be set")
        .parse::<u16>()
        .expect("PORT must be a valid number");
    let db_name = env::var("DB_NAME").expect("DB_NAME must be set");
    let db_user_name = env::var("DB_USERNAME").expect("DB_USERNAME must be set");

    let token = generate_rds_iam_token(&db_host, db_port, &db_user_name).await?;

    let opts = PgConnectOptions::new()
        .host(&db_host)
        .port(db_port)
        .username(&db_user_name)
        .password(&token)
        .database(&db_name)
        .ssl_root_cert_from_pem(RDS_CERTS.to_vec())
        .ssl_mode(sqlx::postgres::PgSslMode::Require);

    let pool = sqlx::postgres::PgPoolOptions::new()
        .connect_with(opts)
        .await?;

    let result: i32 = sqlx::query_scalar("SELECT $1 + $2")
        .bind(3)
        .bind(2)
        .fetch_one(&pool)
        .await?;

    println!("Result: {:?}", result);

    Ok(json!({
        "statusCode": 200,
        "content-type": "text/plain",
        "body": format!("The selected sum is: {result}")
    }))
}
```

------

## 处理来自 Amazon RDS 的事件通知
<a name="rds-events"></a>

您可以使用 Lambda 处理 Amazon RDS 数据库中的事件通知。Amazon RDS 将通知发送到 Amazon Simple Notification Service (Amazon SNS) 主题，您可以将其配置为调用 Lambda 函数。Amazon SNS 将来自 Amazon RDS 的消息封装在其自己的事件文档中，并将其发送到您的函数。

有关配置 Amazon RDS 数据库以发送通知的详细信息，请参阅[使用 Amazon RDS 事件通知](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_Events.html)。

**Example Amazon SNS 事件中的 Amazon RDS 消息**  

```
{
        "Records": [
          {
            "EventVersion": "1.0",
            "EventSubscriptionArn": "arn:aws:sns:us-east-2:123456789012:rds-lambda:21be56ed-a058-49f5-8c98-aedd2564c486",
            "EventSource": "aws:sns",
            "Sns": {
              "SignatureVersion": "1",
              "Timestamp": "2023-01-02T12:45:07.000Z",
              "Signature": "tcc6faL2yUC6dgZdmrwh1Y4cGa/ebXEkAi6RibDsvpi+tE/1+82j...65r==",
              "SigningCertUrl": "https://sns.us-east-2.amazonaws.com/SimpleNotificationService-ac565b8b1a6c5d002d285f9598aa1d9b.pem",
              "MessageId": "95df01b4-ee98-5cb9-9903-4c221d41eb5e",
              "Message": "{\"Event Source\":\"db-instance\",\"Event Time\":\"2023-01-02 12:45:06.000\",\"Identifier Link\":\"https://console.aws.amazon.com/rds/home?region=eu-west-1#dbinstance:id=dbinstanceid\",\"Source ID\":\"dbinstanceid\",\"Event ID\":\"http://docs.amazonwebservices.com/AmazonRDS/latest/UserGuide/USER_Events.html#RDS-EVENT-0002\",\"Event Message\":\"Finished DB Instance backup\"}",
              "MessageAttributes": {},
              "Type": "Notification",
              "UnsubscribeUrl": "https://sns.us-east-2.amazonaws.com/?Action=Unsubscribe&amp;SubscriptionArn=arn:aws:sns:us-east-2:123456789012:test-lambda:21be56ed-a058-49f5-8c98-aedd2564c486",
              "TopicArn":"arn:aws:sns:us-east-2:123456789012:sns-lambda",
              "Subject": "RDS Notification Message"
            }
          }
        ]
      }
```

## Lambda 和 Amazon RDS 完整教程
<a name="rds-database-samples"></a>
+ [使用 Lambda 函数访问 Amazon RDS 数据库](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-lambda-tutorial.html)：在《Amazon RDS 用户指南》中，了解如何使用 Lambda 函数通过 Amazon RDS 代理将数据写入 Amazon RDS 数据库。Lambda 函数将从 Amazon SQS 队列中读取记录，每当添加消息时，都将新的项目写入数据库的表中。

# 为基于 Lambda 的应用程序选择数据库服务
<a name="ddb-rds-database-decision"></a>

许多无服务器应用程序需要存储和检索数据。AWS 提供了多个与 Lambda 函数一起使用的数据库选项。最受欢迎的两个选项是 Amazon DynamoDB（NoSQL 数据库服务）和 Amazon RDS（传统关系数据库解决方案）。以下几节将说明这些服务在与 Lambda 一起使用时的主要区别，并帮助您为无服务器应用程序选择合适的数据库服务。

要详细了解由 AWS 提供的其他数据库服务，并更广泛地了解其用例和权衡，请参阅 [Choosing an AWS database service](https://docs.aws.amazon.com/decision-guides/latest/databases-on-aws-how-to-choose/databases-on-aws-how-to-choose.html)。所有 AWS 数据库服务都与 Lambda 兼容，但并非所有服务都适合您的特定用例。

## 选择使用 Lambda 的数据库服务时，您有哪些选择？
<a name="w2aad101d101c19b9"></a>

AWS 提供多种数据库服务。对于无服务器应用程序，最受欢迎的两个选择是 DynamoDB 和 Amazon RDS。
+ **DynamoDB** 是一项完全托管的 NoSQL 数据库服务，其针对无服务器应用程序进行了优化。该服务可在任何规模下提供无缝的扩展和一致的个位数毫秒级性能。
+ **Amazon RDS** 是一项托管关系数据库服务，其支持多个数据库引擎，包括 MySQL 和 PostgreSQL。该服务通过托管基础架构提供熟悉的 SQL 功能。

## 在已经知道自己要求的情况下的建议
<a name="w2aad101d101c19c11"></a>

如果您已经明确自己的要求，则以下是我们的基本建议：

对于需要一致的低延迟性能、自动扩展且不需要复杂联接或事务的无服务器应用程序，建议使用 [DynamoDB](with-ddb.md)。由于其无服务器性质，该服务特别适合基于 Lambda 的应用程序。

当您需要复杂的 SQL 查询、联接或拥有使用关系数据库的现有应用程序时，[Amazon RDS](services-rds.md) 是更好的选择。但请注意，将 Lambda 函数连接到 Amazon RDS 需要额外的配置，并且可能会影响冷启动时间。

## 选择数据库服务时需要考虑的因素
<a name="w2aad101d101c19c13"></a>

在为您的 Lambda 应用程序选择 DynamoDB 或 Amazon RDS 时，请考虑以下因素：
+ 连接管理和冷启动
+ 数据访问模式
+ 查询的复杂性
+ 数据一致性要求
+ 扩展特征
+ 成本模型

通过了解这些因素，您可以选择能满足自己特定用例需求的最佳选项。

### 连接管理和冷启动
<a name="w2aad101d101c19c13b9b1"></a>
+ DynamoDB 对所有操作都使用 HTTP API。Lambda 函数可以在不维护连接的情况下立即发出请求，从而提高冷启动性能。每个请求都使用 AWS 凭证进行身份验证，而不会产生连接开销。
+ Amazon RDS 需要管理连接池，因为其使用传统数据库连接。这可能会影响冷启动，因为新的 Lambda 实例需要建立连接。您需要实施连接池策略，并有可能使用 [Amazon RDS 代理](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-proxy.html)来有效地管理连接。请注意，使用 Amazon RDS 代理会产生额外的费用。

### 数据访问模式
<a name="w2aad101d101c19c13b9b3"></a>
+ DynamoDB 最适合已知的访问模式和单表设计。其非常适合需要基于主键或二级索引对数据进行一致低延迟访问的 Lambda 应用程序。
+ Amazon RDS 为复杂的查询和不断变化的访问模式提供了灵活性。当您的 Lambda 函数需要在多个表之间执行独特、量身定制的查询或复杂联接时，该服务更适合。

### 查询的复杂性
<a name="w2aad101d101c19c13b9b5"></a>
+ DynamoDB 擅长简单、基于键的操作和预定义的访问模式。复杂的查询必须围绕索引结构进行设计，且联接必须在应用程序代码中进行处理。
+ Amazon RDS 支持带有联接、子查询和聚合的复杂 SQL 查询。当需要复杂的数据操作时，这可以简化您的 Lambda 函数代码。

### 数据一致性要求
<a name="w2aad101d101c19c13b9b7"></a>
+ DynamoDB 提供最终一致性和强一致性选项，单项读取可使用强一致性。其支持事务，但有一些限制。
+ Amazon RDS 提供完整的原子性、一致性、隔离性与持久性（ACID）合规以及复杂的事务支持。如果您的 Lambda 函数需要复杂的事务或多条记录之间的强一致性，则 Amazon RDS 可能更合适。

### 扩展特征
<a name="w2aad101d101c19c13b9b9"></a>
+ DynamoDB 会根据您的工作负载自动扩展。其无需预置即可处理来自 Lambda 函数的流量突然猛增。您可以使用按需容量模式，以仅为使用的容量付费，该模式与 Lambda 的扩展模型完美匹配。
+ 根据您选择的实例大小，Amazon RDS 具有固定容量。如果多个 Lambda 函数尝试同时连接，则可能会超出连接配额。您需要仔细管理连接池，并有可能实施重试逻辑。

### 成本模型
<a name="w2aad101d101c19c13b9c11"></a>
+ DynamoDB 的定价与无服务器应用程序非常吻合。使用按需容量，您只需为 Lambda 函数执行的实际读取和写入付费。空闲时间不收取任何费用。
+ 无论使用情况如何，Amazon RDS 都会对正在运行的实例收费。对于无服务器应用程序中典型的零星工作负载，此服务的成本效益可能较低。但对于持续使用的高吞吐量工作负载来说，此服务可能更经济。

## 开始使用您所选的数据库服务
<a name="w2aad101d101c19c15"></a>

现在，您已经了解了在 DynamoDB 与 Amazon RDS 之间进行选择的标准以及两者之间的主要区别，您可以选择最符合自己需求的选项，并通过以下资源开始使用。

------
#### [ DynamoDB ]

**通过以下资源开始使用 DynamoDB**
+ 有关 DynamoDB 服务的简介，请阅读《Amazon DynamoDB 开发人员指南》**中的[什么是 DynamoDB？](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Introduction.html)。
+ 按照教程：[利用 API Gateway 使用 Lambda](services-apigateway-tutorial.md) 进行操作，查看使用 Lambda 函数响应 API 请求对 DynamoDB 表执行 CRUD 操作的示例。
+ 阅读《Amazon DynamoDB 开发人员指南》**中的[使用 DynamoDB 和 AWS SDK 编程](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Programming.html)，了解如何使用其中一个 AWS SDK 从 Lambda 函数内访问 DynamoDB 的更多信息。

------
#### [ Amazon RDS ]

**通过以下资源开始使用 Amazon RDS**
+ 有关 Amazon RDS 服务的简介，请阅读《Amazon Relational Database Service 用户指南》**中的[什么是 Amazon Relational Database Service (Amazon RDS)？](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Welcome.html)。
+ 按照《Amazon Relational Database Service 用户指南》**中的教程：[使用 Lambda 函数访问 Amazon RDS 数据库](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-lambda-tutorial.html)进行操作。
+ 通过阅读[将 AWS Lambda 与 Amazon RDS 结合使用](services-rds.md)，了解有关将 Lambda 与 Amazon RDS 搭配使用的更多信息。

------

# 使用 Lambda 处理 Amazon S3 事件通知
<a name="with-s3"></a>

您可以使用 Lambda 来处理来自 Amazon Simple Storage Service 的[事件通知](https://docs.aws.amazon.com/AmazonS3/latest/userguide/NotificationHowTo.html)。Amazon S3 可以在创建或删除对象时向 Lambda 函数发送事件。您在存储桶上配置通知设置，并向 Amazon S3 授予权限来根据函数的基于资源的权限策略调用函数。

**警告**  
如果您的 Lambda 函数使用触发它的同一存储桶，则会导致在一个循环中运行该函数。例如，如果每当上传一个对象，存储桶就触发某个函数，而该函数又上传一个对象给存储桶，则该函数间接触发了自身。为避免这种情况，请使用两个存储桶，或将触发器配置为仅适用于传入对象所用的前缀。

Amazon S3 使用包含有关对象的详细信息的事件[异步](invocation-async.md)调用您的函数。以下示例显示了在将部署包上载到 Amazon S3 时 Amazon S3 发送的事件。

**Example Amazon S3 通知事件**  

```
{
  "Records": [
    {
      "eventVersion": "2.1",
      "eventSource": "aws:s3",
      "awsRegion": "us-east-2",
      "eventTime": "2019-09-03T19:37:27.192Z",
      "eventName": "ObjectCreated:Put",
      "userIdentity": {
        "principalId": "AWS:AIDAINPONIXQXHT3IKHL2"
      },
      "requestParameters": {
        "sourceIPAddress": "205.255.255.255"
      },
      "responseElements": {
        "x-amz-request-id": "D82B88E5F771F645",
        "x-amz-id-2": "vlR7PnpV2Ce81l0PRw6jlUpck7Jo5ZsQjryTjKlc5aLWGVHPZLj5NeC6qMa0emYBDXOo6QBU0Wo="
      },
      "s3": {
        "s3SchemaVersion": "1.0",
        "configurationId": "828aa6fc-f7b5-4305-8584-487c791949c1",
        "bucket": {
          "name": "amzn-s3-demo-bucket",
          "ownerIdentity": {
            "principalId": "A3I5XTEXAMAI3E"
          },
          "arn": "arn:aws:s3:::lambda-artifacts-deafc19498e3f2df"
        },
        "object": {
          "key": "b21b84d653bb07b05b1e6b33684dc11b",
          "size": 1305107,
          "eTag": "b21b84d653bb07b05b1e6b33684dc11b",
          "sequencer": "0C0F6F405D6ED209E1"
        }
      }
    }
  ]
}
```

要调用您的函数，Amazon S3 需要来自该函数的[基于资源的策略](access-control-resource-based.md)的权限。当您在 Lambda 控制台中配置 Amazon S3 触发器时，该控制台将修改基于资源的策略以允许 Amazon S3 在存储桶名称和账户 ID 匹配时调用函数。如果您在 Amazon S3 中配置通知，请使用 Lambda API 更新策略。您还可以使用 Lambda API 向另一个账户授予权限，或将权限限制到指定的别名。

如果您的函数使用 AWS 开发工具包来管理 Amazon S3 资源，则其[执行角色](lambda-intro-execution-role.md)也需要 Amazon S3 权限。

**Topics**
+ [

# 教程：使用 Amazon S3 触发器调用 Lambda 函数
](with-s3-example.md)
+ [

# 教程：使用 Amazon S3 触发器创建缩略图
](with-s3-tutorial.md)

# 教程：使用 Amazon S3 触发器调用 Lambda 函数
<a name="with-s3-example"></a>

在本教程中，您将使用控制台创建 Lambda 函数，然后为 Amazon Simple Storage Service（Amazon S3）存储桶配置触发器。每次向 Amazon S3 存储桶添加对象时，函数都会运行并将该对象类型输出到 Amazon CloudWatch Logs 中。

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-s3-example/s3_tut_config.png)


本教程演示如何：

1. 创建 Amazon S3 存储桶。

1. 创建一个 Lambda 函数，该函数会在 Amazon S3 存储桶中返回对象的类型。

1. 配置一个 Lambda 触发器，该触发器将在对象上传到存储桶时调用函数。

1. 先后使用虚拟事件和触发器测试函数。

完成这些步骤后，您将了解如何配置 Lambda 函数，使其在向 Amazon S3 存储桶添加或删除对象时运行。您仅可以使用 AWS 管理控制台 完成此教程。

## 创建 Amazon S3 存储桶
<a name="with-s3-example-create-bucket"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-s3-example/s3trigger_tut_steps1.png)


**创建 Amazon S3 存储桶**

1. 打开 [Amazon S3 控制台](https://console.aws.amazon.com/s3)并选择**通用存储桶**页面。

1. 选择最接近您地理位置的 AWS 区域。您可以使用屏幕顶部的下拉列表更改区域。在本教程的后面部分，您必须在同个区域中创建 Lambda 函数。  
![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/console_region_select.png)

1. 选择 **Create bucket**（创建存储桶）。

1. 在 **General configuration**（常规配置）下，执行以下操作：

   1. 对于**存储桶类型**，确保选中**通用型**。

   1. 对于**存储桶名称**，输入符合 Amazon S3 存储桶[命名规则](https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html)的全局唯一名称。存储桶名称只能由小写字母、数字、句点（.）和连字符（-）组成。

1. 将所有其他选项设置为默认值并选择**创建存储桶**。

## 将测试对象上传到存储桶
<a name="with-s3-example-upload-test-object"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-s3-example/s3trigger_tut_steps2.png)


**要上传测试对象**

1. 打开 Amazon S3 控制台的[存储桶](https://console.aws.amazon.com/s3/buckets)页面，选择您在上一步中创建的存储桶。

1. 选择**上传**。

1. 选择**添加文件**，然后选择要上传的对象。您可以选择任何文件（例如 `HappyFace.jpg`）。

1. 选择**打开**，然后选择**上传**。

在本教程的后面部分，您要使用此对象测试 Lambda 函数。

## 创建权限策略
<a name="with-s3-example-create-policy"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-s3-example/s3trigger_tut_steps3.png)


创建权限策略，允许 Lambda 从 Amazon S3 存储桶获取对象并写入 Amazon CloudWatch Logs。

**创建策略**

1. 打开 IAM 控制台的 [Policies（策略）页面](https://console.aws.amazon.com/iam/home#/policies)。

1. 选择**创建策略**。

1. 选择 **JSON** 选项卡，然后将以下自定义策略粘贴到 JSON 编辑器中。

------
#### [ JSON ]

****  

   ```
   {
       "Version":"2012-10-17",		 	 	 
       "Statement": [
           {
               "Effect": "Allow",
               "Action": [
                   "logs:PutLogEvents",
                   "logs:CreateLogGroup",
                   "logs:CreateLogStream"
               ],
               "Resource": "arn:aws:logs:*:*:*"
           },
           {
               "Effect": "Allow",
               "Action": [
                   "s3:GetObject"
               ],
               "Resource": "arn:aws:s3:::*/*"
           }
       ]
   }
   ```

------

1. 选择**下一步：标签**。

1. 选择**下一步：审核**。

1. 在 **Review policy (查看策略)** 下，为策略 **Name (名称)** 输入 **s3-trigger-tutorial**。

1. 选择**创建策略**。

## 创建执行角色
<a name="with-s3-example-create-role"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-s3-example/s3trigger_tut_steps4.png)


[执行角色](lambda-intro-execution-role.md)是一个 AWS Identity and Access Management（IAM）角色，用于向 Lambda 函数授予访问 AWS 服务 和资源的权限。在此步骤中，您要使用在之前步骤中创建的权限策略来创建执行角色。

**创建执行角色并附加自定义权限策略**

1. 打开 IAM 控制台的[角色页面](https://console.aws.amazon.com/iam/home#/roles)。

1. 选择**创建角色**。

1. 对于可信实体，选择 **AWS 服务**，对于使用案例，选择 **Lambda**。

1. 选择**下一步**。

1. 在策略搜索框中，输入 **s3-trigger-tutorial**。

1. 在搜索结果中，选择您创建的策略（`s3-trigger-tutorial`），然后选择 **Next**（下一步）。

1. 在 **Role details**（角色详细信息）下，为 **Role name**（角色名称）输入 **lambda-s3-trigger-role**，然后选择 **Create role**（创建角色）。

## 创建 Lambda 函数
<a name="with-s3-example-create-function"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-s3-example/s3trigger_tut_steps5.png)


使用 Python 3.14 运行时系统在控制台中创建 Lambda 函数。

**创建 Lambda 函数**

1. 打开 Lamba 控制台的[函数](https://console.aws.amazon.com/lambda/home#/functions)页面。

1. 确保您在创建 Amazon S3 存储桶所在的同一 AWS 区域 内操作。您可以使用屏幕顶部的下拉列表更改区域。  
![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/console_region_select.png)

1. 选择**创建函数**。

1. 选择**从头开始编写**。

1. 在**基本信息**中，执行以下操作：

   1. 对于**函数名称**，输入 `s3-trigger-tutorial`。

   1. 对于**运行时系统**，选择 **Python 3.14**。

   1. 对于**架构**，选择 **x86\$164**。

1. 在**更改默认执行角色**选项卡中，执行以下操作：

   1. 展开选项卡，然后选择**使用现有角色**。

   1. 选择您之前创建的 `lambda-s3-trigger-role`。

1. 选择**创建函数**。

## 部署函数代码
<a name="with-s3-example-deploy-code"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-s3-example/s3trigger_tut_steps6.png)


本教程使用 Python 3.14 运行时系统，但我们还提供了适用于其他运行时系统的示例代码文件。您可以选择以下框中的选项卡，查看适用于您感兴趣的运行时系统的代码。

Lambda 函数检索已上传对象的键名称和来自该对象从 Amazon S3 收到的 `event` 参数的存储桶名称。然后，该函数使用 适用于 Python (Boto3) 的 AWS SDK 中的 [get\$1object](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3/client/get_object.html) 方法来检索对象的元数据，包括已上传对象的内容类型（MIME 类型）。

**要部署函数代码**

1. 在下框中选择 **Python** 选项卡并复制代码。

------
#### [ .NET ]

**适用于 .NET 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-s3-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
使用 .NET 将 S3 事件与 Lambda 结合使用。  

   ```
   // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
   // SPDX-License-Identifier: Apache-2.0
   ﻿using System.Threading.Tasks;
   using Amazon.Lambda.Core;
   using Amazon.S3;
   using System;
   using Amazon.Lambda.S3Events;
   using System.Web;
   
   // Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
   [assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]
   
   namespace S3Integration
   {
       public class Function
       {
           private static AmazonS3Client _s3Client;
           public Function() : this(null)
           {
           }
   
           internal Function(AmazonS3Client s3Client)
           {
               _s3Client = s3Client ?? new AmazonS3Client();
           }
   
           public async Task<string> Handler(S3Event evt, ILambdaContext context)
           {
               try
               {
                   if (evt.Records.Count <= 0)
                   {
                       context.Logger.LogLine("Empty S3 Event received");
                       return string.Empty;
                   }
   
                   var bucket = evt.Records[0].S3.Bucket.Name;
                   var key = HttpUtility.UrlDecode(evt.Records[0].S3.Object.Key);
   
                   context.Logger.LogLine($"Request is for {bucket} and {key}");
   
                   var objectResult = await _s3Client.GetObjectAsync(bucket, key);
   
                   context.Logger.LogLine($"Returning {objectResult.Key}");
   
                   return objectResult.Key;
               }
               catch (Exception e)
               {
                   context.Logger.LogLine($"Error processing request - {e.Message}");
   
                   return string.Empty;
               }
           }
       }
   }
   ```

------
#### [ Go ]

**适用于 Go 的 SDK V2**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-s3-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
使用 Go 将 S3 事件与 Lambda 结合使用。  

   ```
   // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
   // SPDX-License-Identifier: Apache-2.0
   package main
   
   import (
   	"context"
   	"log"
   
   	"github.com/aws/aws-lambda-go/events"
   	"github.com/aws/aws-lambda-go/lambda"
   	"github.com/aws/aws-sdk-go-v2/config"
   	"github.com/aws/aws-sdk-go-v2/service/s3"
   )
   
   func handler(ctx context.Context, s3Event events.S3Event) error {
   	sdkConfig, err := config.LoadDefaultConfig(ctx)
   	if err != nil {
   		log.Printf("failed to load default config: %s", err)
   		return err
   	}
   	s3Client := s3.NewFromConfig(sdkConfig)
   
   	for _, record := range s3Event.Records {
   		bucket := record.S3.Bucket.Name
   		key := record.S3.Object.URLDecodedKey
   		headOutput, err := s3Client.HeadObject(ctx, &s3.HeadObjectInput{
   			Bucket: &bucket,
   			Key:    &key,
   		})
   		if err != nil {
   			log.Printf("error getting head of object %s/%s: %s", bucket, key, err)
   			return err
   		}
   		log.Printf("successfully retrieved %s/%s of type %s", bucket, key, *headOutput.ContentType)
   	}
   
   	return nil
   }
   
   func main() {
   	lambda.Start(handler)
   }
   ```

------
#### [ Java ]

**适用于 Java 的 SDK 2.x**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-s3-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
使用 Java 将 S3 事件与 Lambda 结合使用。  

   ```
   // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
   // SPDX-License-Identifier: Apache-2.0
   package example;
   
   import software.amazon.awssdk.services.s3.model.HeadObjectRequest;
   import software.amazon.awssdk.services.s3.model.HeadObjectResponse;
   import software.amazon.awssdk.services.s3.S3Client;
   
   import com.amazonaws.services.lambda.runtime.Context;
   import com.amazonaws.services.lambda.runtime.RequestHandler;
   import com.amazonaws.services.lambda.runtime.events.S3Event;
   import com.amazonaws.services.lambda.runtime.events.models.s3.S3EventNotification.S3EventNotificationRecord;
   
   import org.slf4j.Logger;
   import org.slf4j.LoggerFactory;
   
   public class Handler implements RequestHandler<S3Event, String> {
       private static final Logger logger = LoggerFactory.getLogger(Handler.class);
       @Override
       public String handleRequest(S3Event s3event, Context context) {
           try {
             S3EventNotificationRecord record = s3event.getRecords().get(0);
             String srcBucket = record.getS3().getBucket().getName();
             String srcKey = record.getS3().getObject().getUrlDecodedKey();
   
             S3Client s3Client = S3Client.builder().build();
             HeadObjectResponse headObject = getHeadObject(s3Client, srcBucket, srcKey);
   
             logger.info("Successfully retrieved " + srcBucket + "/" + srcKey + " of type " + headObject.contentType());
   
             return "Ok";
           } catch (Exception e) {
             throw new RuntimeException(e);
           }
       }
   
       private HeadObjectResponse getHeadObject(S3Client s3Client, String bucket, String key) {
           HeadObjectRequest headObjectRequest = HeadObjectRequest.builder()
                   .bucket(bucket)
                   .key(key)
                   .build();
           return s3Client.headObject(headObjectRequest);
       }
   }
   ```

------
#### [ JavaScript ]

**SDK for JavaScript（v3）**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-s3-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
使用 JavaScript 将 S3 事件与 Lambda 结合使用。  

   ```
   import { S3Client, HeadObjectCommand } from "@aws-sdk/client-s3";
   
   const client = new S3Client();
   
   export const handler = async (event, context) => {
   
       // Get the object from the event and show its content type
       const bucket = event.Records[0].s3.bucket.name;
       const key = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, ' '));
   
       try {
           const { ContentType } = await client.send(new HeadObjectCommand({
               Bucket: bucket,
               Key: key,
           }));
   
           console.log('CONTENT TYPE:', ContentType);
           return ContentType;
   
       } catch (err) {
           console.log(err);
           const message = `Error getting object ${key} from bucket ${bucket}. Make sure they exist and your bucket is in the same region as this function.`;
           console.log(message);
           throw new Error(message);
       }
   };
   ```
使用 TypeScript 将 S3 事件与 Lambda 结合使用。  

   ```
   // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
   // SPDX-License-Identifier: Apache-2.0
   import { S3Event } from 'aws-lambda';
   import { S3Client, HeadObjectCommand } from '@aws-sdk/client-s3';
   
   const s3 = new S3Client({ region: process.env.AWS_REGION });
   
   export const handler = async (event: S3Event): Promise<string | undefined> => {
     // Get the object from the event and show its content type
     const bucket = event.Records[0].s3.bucket.name;
     const key = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, ' '));
     const params = {
       Bucket: bucket,
       Key: key,
     };
     try {
       const { ContentType } = await s3.send(new HeadObjectCommand(params));
       console.log('CONTENT TYPE:', ContentType);
       return ContentType;
     } catch (err) {
       console.log(err);
       const message = `Error getting object ${key} from bucket ${bucket}. Make sure they exist and your bucket is in the same region as this function.`;
       console.log(message);
       throw new Error(message);
     }
   };
   ```

------
#### [ PHP ]

**适用于 PHP 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-s3-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
通过 PHP 将 S3 事件与 Lambda 结合使用。  

   ```
   <?php
   
   use Bref\Context\Context;
   use Bref\Event\S3\S3Event;
   use Bref\Event\S3\S3Handler;
   use Bref\Logger\StderrLogger;
   
   require __DIR__ . '/vendor/autoload.php';
   
   
   class Handler extends S3Handler 
   {
       private StderrLogger $logger;
       public function __construct(StderrLogger $logger)
       {
           $this->logger = $logger;
       }
       
       public function handleS3(S3Event $event, Context $context) : void
       {
           $this->logger->info("Processing S3 records");
   
           // Get the object from the event and show its content type
           $records = $event->getRecords();
           
           foreach ($records as $record) 
           {
               $bucket = $record->getBucket()->getName();
               $key = urldecode($record->getObject()->getKey());
   
               try {
                   $fileSize = urldecode($record->getObject()->getSize());
                   echo "File Size: " . $fileSize . "\n";
                   // TODO: Implement your custom processing logic here
               } catch (Exception $e) {
                   echo $e->getMessage() . "\n";
                   echo 'Error getting object ' . $key . ' from bucket ' . $bucket . '. Make sure they exist and your bucket is in the same region as this function.' . "\n";
                   throw $e;
               }
           }
       }
   }
   
   $logger = new StderrLogger();
   return new Handler($logger);
   ```

------
#### [ Python ]

**适用于 Python 的 SDK（Boto3）**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-s3-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
使用 Python 将 S3 事件与 Lambda 结合使用。  

   ```
   # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
   # SPDX-License-Identifier: Apache-2.0
   import json
   import urllib.parse
   import boto3
   
   print('Loading function')
   
   s3 = boto3.client('s3')
   
   
   def lambda_handler(event, context):
       #print("Received event: " + json.dumps(event, indent=2))
   
       # Get the object from the event and show its content type
       bucket = event['Records'][0]['s3']['bucket']['name']
       key = urllib.parse.unquote_plus(event['Records'][0]['s3']['object']['key'], encoding='utf-8')
       try:
           response = s3.get_object(Bucket=bucket, Key=key)
           print("CONTENT TYPE: " + response['ContentType'])
           return response['ContentType']
       except Exception as e:
           print(e)
           print('Error getting object {} from bucket {}. Make sure they exist and your bucket is in the same region as this function.'.format(key, bucket))
           raise e
   ```

------
#### [ Ruby ]

**适用于 Ruby 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-s3-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
通过 Ruby 将 S3 事件与 Lambda 结合使用。  

   ```
   require 'json'
   require 'uri'
   require 'aws-sdk'
   
   puts 'Loading function'
   
   def lambda_handler(event:, context:)
     s3 = Aws::S3::Client.new(region: 'region') # Your AWS region
     # puts "Received event: #{JSON.dump(event)}"
   
     # Get the object from the event and show its content type
     bucket = event['Records'][0]['s3']['bucket']['name']
     key = URI.decode_www_form_component(event['Records'][0]['s3']['object']['key'], Encoding::UTF_8)
     begin
       response = s3.get_object(bucket: bucket, key: key)
       puts "CONTENT TYPE: #{response.content_type}"
       return response.content_type
     rescue StandardError => e
       puts e.message
       puts "Error getting object #{key} from bucket #{bucket}. Make sure they exist and your bucket is in the same region as this function."
       raise e
     end
   end
   ```

------
#### [ Rust ]

**适用于 Rust 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-s3-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
使用 Rust 将 S3 事件与 Lambda 结合使用。  

   ```
   // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
   // SPDX-License-Identifier: Apache-2.0
   use aws_lambda_events::event::s3::S3Event;
   use aws_sdk_s3::{Client};
   use lambda_runtime::{run, service_fn, Error, LambdaEvent};
   
   
   /// Main function
   #[tokio::main]
   async fn main() -> Result<(), Error> {
       tracing_subscriber::fmt()
           .with_max_level(tracing::Level::INFO)
           .with_target(false)
           .without_time()
           .init();
   
       // Initialize the AWS SDK for Rust
       let config = aws_config::load_from_env().await;
       let s3_client = Client::new(&config);
   
       let res = run(service_fn(|request: LambdaEvent<S3Event>| {
           function_handler(&s3_client, request)
       })).await;
   
       res
   }
   
   async fn function_handler(
       s3_client: &Client,
       evt: LambdaEvent<S3Event>
   ) -> Result<(), Error> {
       tracing::info!(records = ?evt.payload.records.len(), "Received request from SQS");
   
       if evt.payload.records.len() == 0 {
           tracing::info!("Empty S3 event received");
       }
   
       let bucket = evt.payload.records[0].s3.bucket.name.as_ref().expect("Bucket name to exist");
       let key = evt.payload.records[0].s3.object.key.as_ref().expect("Object key to exist");
   
       tracing::info!("Request is for {} and object {}", bucket, key);
   
       let s3_get_object_result = s3_client
           .get_object()
           .bucket(bucket)
           .key(key)
           .send()
           .await;
   
       match s3_get_object_result {
           Ok(_) => tracing::info!("S3 Get Object success, the s3GetObjectResult contains a 'body' property of type ByteStream"),
           Err(_) => tracing::info!("Failure with S3 Get Object request")
       }
   
       Ok(())
   }
   ```

------

1. 在 Lambda 控制台的**代码源**窗格中，将代码粘贴到代码编辑器中，替换 Lambda 创建的代码。

1. 在**部署**部分，选择**部署**以更新函数的代码：  
![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/getting-started-tutorial/deploy-console.png)

## 创建 Amazon S3 触发器
<a name="with-s3-example-create-trigger"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-s3-example/s3trigger_tut_steps7.png)


**创建 Amazon S3 触发器**

1. 在**函数概述**窗格中，选择**添加触发器**。  
![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/overview-trigger.png)

1. 选择 **S3**。

1. 在**存储桶**下，选择您在本教程前面步骤中创建的存储桶。

1. 在**事件类型**下，确保已选择**所有对象创建事件**。

1. 在**递归调用**下，选中复选框以确认知晓不建议使用相同的 Amazon S3 存储桶用于输入和输出。

1. 选择**添加**。

**注意**  
当您使用 Lambda 控制台为 Lambda 函数创建 Amazon S3 触发器时，Amazon S3 会在您指定的存储桶上配置[事件通知](https://docs.aws.amazon.com/AmazonS3/latest/userguide/EventNotifications.html)。在配置此事件通知之前，Amazon S3 会执行一系列检查以确认事件目标是否存在并具有所需的 IAM 策略。Amazon S3 还会对为该存储桶配置的任何其他事件通知执行这些测试。  
由于这项检查，如果存储桶之前为已不再存在的资源或没有所需权限策略的资源配置了事件目标，则 Amazon S3 将无法创建新的事件通知。您将看到以下错误消息，表明无法创建触发器：  

```
An error occurred when creating the trigger: Unable to validate the following destination configurations.
```
如果您之前使用同一存储桶为另一个 Lambda 函数配置了触发器，并且此后又删除了该函数或修改了其权限策略，则会看到此错误。

## 使用虚拟事件测试 Lambda 函数
<a name="with-s3-example-test-dummy-event"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-s3-example/s3trigger_tut_steps8.png)


**要使用虚拟事件测试 Lambda 函数**

1. 在函数的 Lambda 控制台页面中，选择**测试**选项卡。  
![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/test-tab.png)

1. 对于**事件名称**，输入 `MyTestEvent`。

1. 在**事件 JSON** 中，粘贴以下测试事件。请务必替换以下值：
   + 使用 `us-east-1` 替换要在其中创建 Amazon S3 存储桶的区域。
   + 将 `amzn-s3-demo-bucket` 的两个实例都替换为 Amazon S3 存储桶的名称。
   + 将 `test%2FKey` 替换为您之前上传到存储桶的测试对象的名称（例如，`HappyFace.jpg`）。

   ```
   {
     "Records": [
       {
         "eventVersion": "2.0",
         "eventSource": "aws:s3",
         "awsRegion": "us-east-1",
         "eventTime": "1970-01-01T00:00:00.000Z",
         "eventName": "ObjectCreated:Put",
         "userIdentity": {
           "principalId": "EXAMPLE"
         },
         "requestParameters": {
           "sourceIPAddress": "127.0.0.1"
         },
         "responseElements": {
           "x-amz-request-id": "EXAMPLE123456789",
           "x-amz-id-2": "EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH"
         },
         "s3": {
           "s3SchemaVersion": "1.0",
           "configurationId": "testConfigRule",
           "bucket": {
             "name": "amzn-s3-demo-bucket",
             "ownerIdentity": {
               "principalId": "EXAMPLE"
             },
             "arn": "arn:aws:s3:::amzn-s3-demo-bucket"
           },
           "object": {
             "key": "test%2Fkey",
             "size": 1024,
             "eTag": "0123456789abcdef0123456789abcdef",
             "sequencer": "0A1B2C3D4E5F678901"
           }
         }
       }
     ]
   }
   ```

1. 选择**保存**。

1. 选择**测试**。

1. 如果函数成功运行，您将在**执行结果**选项卡中看到如下输出。

   ```
   Response
   "image/jpeg"
   
   Function Logs
   START RequestId: 12b3cae7-5f4e-415e-93e6-416b8f8b66e6 Version: $LATEST
   2021-02-18T21:40:59.280Z    12b3cae7-5f4e-415e-93e6-416b8f8b66e6    INFO    INPUT BUCKET AND KEY:  { Bucket: 'amzn-s3-demo-bucket', Key: 'HappyFace.jpg' }
   2021-02-18T21:41:00.215Z    12b3cae7-5f4e-415e-93e6-416b8f8b66e6    INFO    CONTENT TYPE: image/jpeg
   END RequestId: 12b3cae7-5f4e-415e-93e6-416b8f8b66e6
   REPORT RequestId: 12b3cae7-5f4e-415e-93e6-416b8f8b66e6    Duration: 976.25 ms    Billed Duration: 977 ms    Memory Size: 128 MB    Max Memory Used: 90 MB    Init Duration: 430.47 ms        
   
   Request ID
   12b3cae7-5f4e-415e-93e6-416b8f8b66e6
   ```

### 使用 Amazon S3 触发器测试 Lambda 函数
<a name="with-s3-example-test-s3-trigger"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-s3-example/s3trigger_tut_steps9.png)


要使用配置的触发器测试函数，请使用控制台将对象上传到 Amazon S3 存储桶。要验证 Lambda 函数是否按预期运行，请使用 CloudWatch Logs 查看函数的输出。

**要将对象上传到 Amazon S3 存储桶**

1. 打开 Amazon S3 控制台的[存储桶](https://console.aws.amazon.com/s3/buckets)页面，选择之前创建的存储桶。

1. 选择**上传**。

1. 选择**添加文件**，然后使用文件选择器选择要上传的对象。此对象可以是您选择的任何文件。

1. 选择**打开**，然后选择**上传**。

**使用 CloudWatch Logs 验证函数调用情况**

1. 打开 [CloudWatch 控制台](https://console.aws.amazon.com/cloudwatch/home)。

1. 确保您在创建 Lambda 函数所在相同的 AWS 区域 操作。您可以使用屏幕顶部的下拉列表更改区域。  
![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/console_region_select.png)

1. 选择**日志**，然后选择**日志组**。

1. 选择函数 (`/aws/lambda/s3-trigger-tutorial`) 的日志组。

1. 在**日志流**下，选择最新的日志流。

1. 如果已正确调用函数来响应 Amazon S3 触发器，您会看到如下输出。您看到的 `CONTENT TYPE` 取决于上传到存储桶的文件类型。

   ```
   2022-05-09T23:17:28.702Z	0cae7f5a-b0af-4c73-8563-a3430333cc10	INFO	CONTENT TYPE: image/jpeg
   ```

## 清除资源
<a name="cleanup"></a>

除非您想要保留为本教程创建的资源，否则可立即将其删除。通过删除您不再使用的 AWS 资源，可防止您的 AWS 账户 产生不必要的费用。

**删除 Lambda 函数**

1. 打开 Lamba 控制台的 [Functions（函数）页面](https://console.aws.amazon.com/lambda/home#/functions)。

1. 选择您创建的函数。

1. 依次选择**操作**和**删除**。

1. 在文本输入字段中键入 **confirm**，然后选择**删除**。

**删除执行角色**

1. 打开 IAM 控制台的[角色页面](https://console.aws.amazon.com/iam/home#/roles)。

1. 选择您创建的执行角色。

1. 选择**删除**。

1. 在文本输入字段中输入角色名称，然后选择 **Delete**（删除）。

**删除 S3 存储桶**

1. 打开 [Amazon S3 控制台](https://console.aws.amazon.com//s3/home#)。

1. 选择您创建的存储桶。

1. 选择**删除**。

1. 在文本输入字段中输入存储桶的名称。

1. 选择**删除存储桶**。

## 后续步骤
<a name="next-steps"></a>

在 [教程：使用 Amazon S3 触发器创建缩略图](with-s3-tutorial.md) 中，Amazon S3 触发器会调用一个函数，该函数会为上传到存储桶的每个图像文件创建缩略图。本教程需要适度的AWS和 Lambda 领域知识水平。其展示了如何使用 AWS Command Line Interface（AWS CLI）来创建资源，以及如何为函数及其依赖项创建 .zip 文件存档部署包。

# 教程：使用 Amazon S3 触发器创建缩略图
<a name="with-s3-tutorial"></a>

在本教程中，您将创建并配置 Lambda 函数，用于调整添加到 Amazon Simple Storage Service（Amazon S3）存储桶的图像大小。当您向存储桶添加图像文件时，Amazon S3 会调用您的 Lambda 函数。然后，该函数会创建图像的缩略图版本，并将其输出到不同的 Amazon S3 存储桶。

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-s3-tutorial/s3thumb_tut_resources.png)


要完成本教程，请执行以下步骤：

1. 创建源和目标 Amazon S3 存储桶并上传示例图片。

1. 创建一个 Lambda 函数，用于调整图像大小并将缩略图输出到 Amazon S3 存储桶。

1. 配置一个 Lambda 触发器，该触发器将在对象上传到源存储桶时调用函数。

1. 若要测试您的函数，先使用虚拟事件，然后将图像上传到源存储桶。

完成这些步骤后，您将了解如何使用 Lambda 对添加到 Amazon S3 存储桶的对象执行文件处理任务。您可以使用 AWS Command Line Interface 或 AWS CLI (AWS 管理控制台) 完成此教程。

如果您相同通过更简单的示例来了解如何为 Lambda 配置 Amazon S3 触发器，则可以参阅[教程：使用 Amazon S3 触发器调用 Lambda 函数](https://docs.aws.amazon.com/lambda/latest/dg/with-s3-example.html)。

**Topics**
+ [

## 先决条件
](#with-s3-example-prereqs)
+ [

## 创建两个 Amazon S3 存储桶
](#with-s3-tutorial-prepare-create-buckets)
+ [

## 将测试图片上传到源存储桶
](#with-s3-tutorial-test-image)
+ [

## 创建权限策略
](#with-s3-tutorial-create-policy)
+ [

## 创建执行角色
](#with-s3-tutorial-create-execution-role)
+ [

## 创建函数部署包
](#with-s3-tutorial-create-function-package)
+ [

## 创建 Lambda 函数
](#with-s3-tutorial-create-function-createfunction)
+ [

## 配置 Amazon S3 来调用函数
](#with-s3-tutorial-configure-s3-trigger)
+ [

## 使用虚拟事件测试 Lambda 函数
](#with-s3-tutorial-dummy-test)
+ [

## 使用 Amazon S3 触发器测试函数
](#with-s3-tutorial-test-s3)
+ [

## 清除资源
](#s3-tutorial-cleanup)

## 先决条件
<a name="with-s3-example-prereqs"></a>

如果您想使用 AWS CLI 来完成教程，请安装[最新版本的 AWS Command Line Interface]()。

对于 Lambda 函数代码，您可以使用 Python 或 Node.js。为您要使用的语言安装语言支持工具和软件包管理器。

### 安装 AWS Command Line Interface
<a name="install_aws_cli"></a>

如果您尚未安装 AWS Command Line Interface，请按照[安装或更新最新版本的 AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) 中的步骤进行安装。

本教程需要命令行终端或 Shell 来运行命令。在 Linux 和 macOS 中，可使用您首选的 Shell 和程序包管理器。

**注意**  
在 Windows 中，操作系统的内置终端不支持您经常与 Lambda 一起使用的某些 Bash CLI 命令（例如 `zip`）。[安装 Windows Subsystem for Linux](https://docs.microsoft.com/en-us/windows/wsl/install-win10)，获取 Ubuntu 和 Bash 与 Windows 集成的版本。

## 创建两个 Amazon S3 存储桶
<a name="with-s3-tutorial-prepare-create-buckets"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-s3-tutorial/s3thumb_tut_steps1.png)


先创建两个 Amazon S3 存储桶。第一个存储桶是您要向其上传图像的源存储桶。第二个存储桶是 Lambda 用来保存调用函数时调整大小后的缩略图。

------
#### [ AWS 管理控制台 ]

**创建 Amazon S3 存储桶（控制台）**

1. 打开 [Amazon S3 控制台](https://console.aws.amazon.com/s3)并选择**通用存储桶**页面。

1. 选择最接近您地理位置的 AWS 区域。您可以使用屏幕顶部的下拉列表更改区域。在本教程的后面部分，您必须在同个区域中创建 Lambda 函数。  
![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/console_region_select.png)

1. 选择 **Create bucket**（创建存储桶）。

1. 在 **General configuration**（常规配置）下，执行以下操作：

   1. 对于**存储桶类型**，确保选中**通用型**。

   1. 对于**存储桶名称**，输入符合 Amazon S3 存储桶[命名规则](https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html)的全局唯一名称。存储桶名称只能由小写字母、数字、句点（.）和连字符（-）组成。

1. 将所有其他选项设置为默认值并选择**创建存储桶**。

1. 重复步骤 1 到 5 以创建自己的目标存储桶。在**存储桶名称**中输入 `amzn-s3-demo-source-bucket-resized`，其中 `amzn-s3-demo-source-bucket` 是您刚刚创建的源存储桶的名称。

------
#### [ AWS CLI ]

**创建 Amazon S3 存储桶（AWS CLI）**

1. 运行以下 CLI 命令来创建自己的源存储桶。您为存储桶选择的名称必须具有全局唯一性，并遵守 Amazon S3 [存储桶命名规则](https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html)。名称只能由小写字母、数字、句点（.）和连字符（-）组成。对于 `region` 和 `LocationConstraint`，请选择最接近您地理位置的 [AWS 区域](https://docs.aws.amazon.com/general/latest/gr/lambda-service.html)。

   ```
   aws s3api create-bucket --bucket amzn-s3-demo-source-bucket --region us-east-1 \
   --create-bucket-configuration LocationConstraint=us-east-1
   ```

   在本教程的后面部分，您必须在与源存储桶相同的 AWS 区域 中创建 Lambda 函数，因此请记下您选择的区域。

1. 运行以下命令来创建自己的目标存储桶。对于存储桶名称，必须使用 `amzn-s3-demo-source-bucket-resized`，其中 `amzn-s3-demo-source-bucket` 是您在步骤 1 中创建的源存储桶名称。对于 `region` 和 `LocationConstraint`，请选择与用于创建源存储桶时相同的 AWS 区域。

   ```
   aws s3api create-bucket --bucket amzn-s3-demo-source-bucket-resized --region us-east-1 \
   --create-bucket-configuration LocationConstraint=us-east-1
   ```

------

## 将测试图片上传到源存储桶
<a name="with-s3-tutorial-test-image"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-s3-tutorial/s3thumb_tut_steps2.png)


在本教程的后面部分，您将使用 AWS CLI 或 Lambda 控制台调用 Lambda 函数以对其进行测试。要确认函数是否正常运行，您的源存储桶需要包含测试图片。此图像可以是您选择的任何 JPG 或 PNG 文件。

------
#### [ AWS 管理控制台 ]

**将测试图片上传到源存储桶（控制台）**

1. 打开 Amazon S3 控制台的[存储桶](https://console.aws.amazon.com/s3/buckets)页面。

1. 选择您在上一步中创建的源存储桶。

1. 选择**上传**。

1. 选择**添加文件**，然后使用文件选择器选择要上传的对象。

1. 选择**打开**，然后选择**上传**。

------
#### [ AWS CLI ]

**将测试图片上传到源存储桶（AWS CLI）**
+ 在包含要上传的图像的目录中，运行以下 CLI 命令。将 `--bucket` 参数替换为源存储桶的名称。对于`--key` 和 `--body` 参数，请使用测试图像的文件名。

  ```
  aws s3api put-object --bucket amzn-s3-demo-source-bucket --key HappyFace.jpg --body ./HappyFace.jpg
  ```

------

## 创建权限策略
<a name="with-s3-tutorial-create-policy"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-s3-tutorial/s3thumb_tut_steps3.png)


创建 Lambda 函数的第一步是创建权限策略。此策略为函数提供访问其他 AWS 资源所需的权限。在本教程中，此策略授予了 Lambda 对 Amazon S3 存储桶的读取和写入权限，并允许其写入 Amazon CloudWatch Logs。

------
#### [ AWS 管理控制台 ]

**创建策略（控制台）**

1. 打开 AWS Identity and Access Management IAM 控制台的 [Policies（策略）页面](https://console.aws.amazon.com/iamv2/home#policies)。

1. 选择**创建策略**。

1. 选择 **JSON** 选项卡，然后将以下自定义策略粘贴到 JSON 编辑器中。  
****  

   ```
   {
       "Version":"2012-10-17",		 	 	 
       "Statement": [
           {
               "Effect": "Allow",
               "Action": [
                   "logs:PutLogEvents",
                   "logs:CreateLogGroup",
                   "logs:CreateLogStream"
               ],
               "Resource": "arn:aws:logs:*:*:*"
           },
           {
               "Effect": "Allow",
               "Action": [
                   "s3:GetObject"
               ],
               "Resource": "arn:aws:s3:::*/*"
           },
           {
               "Effect": "Allow",
               "Action": [
                   "s3:PutObject"
               ],
               "Resource": "arn:aws:s3:::*/*"
           }
       ]
   }
   ```

1. 选择**下一步**。

1. 在**策略详细信息**下，为**策略名称**输入 `LambdaS3Policy`。

1. 选择**创建策略**。

------
#### [ AWS CLI ]

**创建策略（AWS CLI）**

1. 将下列 JSON 保存在名为 `policy.json` 的文件中。  
****  

   ```
   {
       "Version":"2012-10-17",		 	 	 
       "Statement": [
           {
               "Effect": "Allow",
               "Action": [
                   "logs:PutLogEvents",
                   "logs:CreateLogGroup",
                   "logs:CreateLogStream"
               ],
               "Resource": "arn:aws:logs:*:*:*"
           },
           {
               "Effect": "Allow",
               "Action": [
                   "s3:GetObject"
               ],
               "Resource": "arn:aws:s3:::*/*"
           },
           {
               "Effect": "Allow",
               "Action": [
                   "s3:PutObject"
               ],
               "Resource": "arn:aws:s3:::*/*"
           }
       ]
   }
   ```

1. 在保存 JSON 策略文档的目录中，运行以下 CLI 命令。

   ```
   aws iam create-policy --policy-name LambdaS3Policy --policy-document file://policy.json
   ```

------

## 创建执行角色
<a name="with-s3-tutorial-create-execution-role"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-s3-tutorial/s3thumb_tut_steps4.png)


执行角色是一个 IAM 角色，用于向 Lambda 函数授予访问 AWS 服务 和资源的权限。要授予函数读取和写入 Amazon S3 存储桶的权限，您需要附加上一步中创建的权限策略。

------
#### [ AWS 管理控制台 ]

**创建执行角色并附加权限策略（控制台）**

1. 打开（IAM）控制台的[角色](https://console.aws.amazon.com/iamv2/home#roles)页面。

1. 选择**创建角色**。

1. 在**可信实体类型**中选择 **AWS 服务**，在**使用案例**中选择 **Lambda**。

1. 选择**下一步**。

1. 执行以下操作添加您在上一步中创建的权限策略：

   1. 在策略搜索框中，输入 `LambdaS3Policy`。

   1. 在搜索结果中，选中 `LambdaS3Policy` 的复选框。

   1. 选择**下一步**。

1. 在**角色详细信息**下的**角色名称**中输入 `LambdaS3Role`。

1. 选择**创建角色**。

------
#### [ AWS CLI ]

**创建执行角色并附加权限策略（AWS CLI）**

1. 将下列 JSON 保存在名为 `trust-policy.json` 的文件中。此信任策略允许 Lambda 通过向服务主体 `lambda.amazonaws.com` 授予调用 AWS Security Token Service（AWS STS）`AssumeRole` 操作的权限来使用该角色的权限。  
****  

   ```
   {
     "Version":"2012-10-17",		 	 	 
     "Statement": [
       {
         "Effect": "Allow",
         "Principal": {
           "Service": "lambda.amazonaws.com"
         },
         "Action": "sts:AssumeRole"
       }
     ]
   }
   ```

1. 在保存 JSON 信任策略文档的目录中，运行以下 CLI 命令来创建执行角色。

   ```
   aws iam create-role --role-name LambdaS3Role --assume-role-policy-document file://trust-policy.json
   ```

1. 要附加您在上一步中创建的权限策略，请运行以下 CLI 命令。将策略 ARN 中的 AWS 账户 号码替换为自己的账号。

   ```
   aws iam attach-role-policy --role-name LambdaS3Role --policy-arn arn:aws:iam::123456789012:policy/LambdaS3Policy
   ```

------

## 创建函数部署包
<a name="with-s3-tutorial-create-function-package"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-s3-tutorial/s3thumb_tut_steps5.png)


要创建函数，您需要创建包含函数代码和所有依赖项的*部署包*。对于此 `CreateThumbnail` 函数，您的函数代码使用单独的库来调整图像大小。按照所选语言的说明创建包含所需库的部署包。

------
#### [ Node.js ]

**创建部署包（Node.js）**

1. 为您的函数代码和依赖项创建一个名为 `lambda-s3` 的目录并导航到此目录。

   ```
   mkdir lambda-s3
   cd lambda-s3
   ```

1. 使用 `npm` 创建新的 Node.js 项目。要在交互式体验中接受提供的默认选项，请按 `Enter`。

   ```
   npm init
   ```

1. 将以下函数代码保存在名为 `index.mjs` 的文件中。请务必将 `us-east-1` 替换您创建源存储桶和目标存储桶时所在的 AWS 区域。

   ```
   // dependencies
   import { S3Client, GetObjectCommand, PutObjectCommand } from '@aws-sdk/client-s3';
   
   import { Readable } from 'stream';
   
   import sharp from 'sharp';
   import util from 'util';
   
   
   // create S3 client
   const s3 = new S3Client({region: 'us-east-1'});
   
   // define the handler function
   export const handler = async (event, context) => {
   
   // Read options from the event parameter and get the source bucket
   console.log("Reading options from event:\n", util.inspect(event, {depth: 5}));
     const srcBucket = event.Records[0].s3.bucket.name;
     
   // Object key may have spaces or unicode non-ASCII characters
   const srcKey    = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, " "));
   const dstBucket = srcBucket + "-resized";
   const dstKey    = "resized-" + srcKey;
   
   // Infer the image type from the file suffix
   const typeMatch = srcKey.match(/\.([^.]*)$/);
   if (!typeMatch) {
     console.log("Could not determine the image type.");
     return;
   }
   
   // Check that the image type is supported
   const imageType = typeMatch[1].toLowerCase();
   if (imageType != "jpg" && imageType != "png") {
     console.log(`Unsupported image type: ${imageType}`);
     return;
   }
   
   // Get the image from the source bucket. GetObjectCommand returns a stream.
   try {
     const params = {
       Bucket: srcBucket,
       Key: srcKey
     };
     var response = await s3.send(new GetObjectCommand(params));
     var stream = response.Body;
     
   // Convert stream to buffer to pass to sharp resize function.
     if (stream instanceof Readable) {
       var content_buffer = Buffer.concat(await stream.toArray());
       
     } else {
       throw new Error('Unknown object stream type');
     }
   
   
   } catch (error) {
     console.log(error);
     return;
   }
   
     
   // set thumbnail width. Resize will set the height automatically to maintain aspect ratio.
   const width  = 200;
   
   // Use the sharp module to resize the image and save in a buffer.
   try {    
     var output_buffer = await sharp(content_buffer).resize(width).toBuffer();
   
   } catch (error) {
     console.log(error);
     return;
   }
   
   // Upload the thumbnail image to the destination bucket
   try {
     const destparams = {
       Bucket: dstBucket,
       Key: dstKey,
       Body: output_buffer,
       ContentType: "image"
     };
   
     const putResult = await s3.send(new PutObjectCommand(destparams));
   
     } catch (error) {
       console.log(error);
       return;
     }
   
     console.log('Successfully resized ' + srcBucket + '/' + srcKey +
       ' and uploaded to ' + dstBucket + '/' + dstKey);
     };
   ```

1. 在 `lambda-s3` 目录中，使用 npm 安装 sharp 库。请注意，最新版本的 sharp（0.33）与 Lambda 不兼容。安装版本 0.32.6 以完成本教程。

   ```
   npm install sharp@0.32.6
   ```

   npm `install` 命令会为模块创建 `node_modules` 目录。完成此步骤后，目录结构应如下所示：

   ```
   lambda-s3
   |- index.mjs
   |- node_modules
   |  |- base64js
   |  |- bl
   |  |- buffer
   ...
   |- package-lock.json
   |- package.json
   ```

1. 创建一个包含函数代码和依赖项的部署包。在 MacOS 或 Linux 中，运行以下命令。

   ```
   zip -r function.zip .
   ```

   在 Windows 中，使用您首选的 ZIP 实用工具来创建 .zip 文件。确保您的 `index.mjs`、`package.json`、和 `package-lock.json` 文件以及您的 `node_modules` 目录都在.zip 文件的根目录中。

------
#### [ Python ]

**创建部署包（Python）**

1. 将代码示例保存为名为 `lambda_function.py` 的文件。

   ```
   import boto3
   import os
   import sys
   import uuid
   from urllib.parse import unquote_plus
   from PIL import Image
   import PIL.Image
               
   s3_client = boto3.client('s3')
               
   def resize_image(image_path, resized_path):
     with Image.open(image_path) as image:
       image.thumbnail(tuple(x / 2 for x in image.size))
       image.save(resized_path)
               
   def lambda_handler(event, context):
     for record in event['Records']:
       bucket = record['s3']['bucket']['name']
       key = unquote_plus(record['s3']['object']['key'])
       tmpkey = key.replace('/', '')
       download_path = '/tmp/{}{}'.format(uuid.uuid4(), tmpkey)
       upload_path = '/tmp/resized-{}'.format(tmpkey)
       s3_client.download_file(bucket, key, download_path)
       resize_image(download_path, upload_path)
       s3_client.upload_file(upload_path, '{}-resized'.format(bucket), 'resized-{}'.format(key))
   ```

1. 在创建 `lambda_function.py` 文件的同一目录中，创建一个名为 `package` 的新目录并安装 [Pillow（PIL）](https://pypi.org/project/Pillow/)库和 适用于 Python (Boto3) 的 AWS SDK。虽然 Lambda Python 运行时系统包含 Boto3 SDK 的一个版本，但建议您将所有函数的依赖项添加到部署包中，即使这些依赖项已经包含在了运行时系统中。有关更多信息，请参阅 [Python 中的运行时系统依赖项](https://docs.aws.amazon.com/lambda/latest/dg/python-package.html#python-package-dependencies)。

   ```
   mkdir package
   pip install \
   --platform manylinux2014_x86_64 \
   --target=package \
   --implementation cp \
   --python-version 3.12 \
   --only-binary=:all: --upgrade \
   pillow boto3
   ```

   Pillow 库包含 C/C\$1\$1 代码。通过使用 `--platform manylinux_2014_x86_64` 和 `--only-binary=:all:` 选项，pip 将下载并安装包含与 Amazon Linux 2 操作系统兼容的预编译二进制文件的 Pillow 版本。这可以确保无论本地构建计算机的操作系统和架构如何，部署包都能在 Lambda 执行环境中正常发挥作用。

1. 创建包含应用程序代码和 Pillow 以及 Boto3 库的 zip 文件。在 Linux 或 MacOS 中，从命令行界面运行以下命令。

   ```
   cd package
   zip -r ../lambda_function.zip .
   cd ..
   zip lambda_function.zip lambda_function.py
   ```

    在 Windows 中，使用您首选的压缩工具来创建 `lambda_function.zip` 文件。确保您的 `lambda_function.py` 文件和包含依赖项的文件夹都位于.zip 文件的根目录下。

您也可以使用 Python 虚拟环境创建部署包。请参阅 [将 .zip 文件归档用于 Python Lambda 函数](python-package.md)。

------

## 创建 Lambda 函数
<a name="with-s3-tutorial-create-function-createfunction"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-s3-tutorial/s3thumb_tut_steps6.png)


您也可以使用 AWS CLI 或 Lambda 控制台创建 Lambda 函数。按照所选语言的说明创建函数。

------
#### [ AWS 管理控制台 ]

**创建函数（控制台）**

要使用控制台创建 Lambda 函数，首先要创建包含一些“Hello world”代码的基本函数。然后，通过上传在上一步中创建的 .zip 或 JAR 文件，将此代码替换为自己的函数代码。

1. 打开 Lamba 控制台的[函数](https://console.aws.amazon.com/lambda/home#/functions)页面。

1. 确保您在创建 Amazon S3 存储桶所在的同一 AWS 区域 内操作。您可以使用屏幕顶部的下拉列表更改区域。  
![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/console_region_select.png)

1. 选择**创建函数**。

1. 选择**从头开始创作**。

1. 在**基本信息**中，执行以下操作：

   1. 对于 **Function name（函数名称）**，请输入 `CreateThumbnail`。

   1. 对于**运行时**，根据您为函数选择的语言，选择 **Node.js 22.x** 或 **Python 3.12**。

   1. 对于**架构**，选择 **x86\$164**。

1. 在**更改默认执行角色**选项卡中，执行以下操作：

   1. 展开选项卡，然后选择**使用现有角色**。

   1. 选择您之前创建的 `LambdaS3Role`。

1. 选择**创建函数**。

**上传函数代码（控制台）**

1. 在**代码源**窗格中，选择**上传自**。

1. 选择 **.zip 文件**。

1. 选择**上传**。

1. 在文件选择器中，选择 .zip 文件，然后选择**打开**。

1. 选择**保存**。

------
#### [ AWS CLI ]

**创建函数（AWS CLI）**
+ 运行您所选语言的 CLI 命令。对于 `role` 参数，确保将 `123456789012` 替换为自己的 AWS 账户 ID。对于 `region` 参数，将 `us-east-1` 替换为在其中创建 Amazon S3 存储桶的区域。
  + 对于 **Node.js**，从包含 `function.zip` 文件的目录中运行以下命令。

    ```
    aws lambda create-function --function-name CreateThumbnail \
    --zip-file fileb://function.zip --handler index.handler --runtime nodejs24.x \
    --timeout 10 --memory-size 1024 \
    --role arn:aws:iam::123456789012:role/LambdaS3Role --region us-east-1
    ```
  + 对于 **Python**，从包含 `lambda_function.zip` 文件的目录中运行以下命令。

    ```
    aws lambda create-function --function-name CreateThumbnail \
    --zip-file fileb://lambda_function.zip --handler lambda_function.lambda_handler \
    --runtime python3.14 --timeout 10 --memory-size 1024 \
    --role arn:aws:iam::123456789012:role/LambdaS3Role --region us-east-1
    ```

------

## 配置 Amazon S3 来调用函数
<a name="with-s3-tutorial-configure-s3-trigger"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-s3-tutorial/s3thumb_tut_steps7.png)


要在将图像上传到源存储桶时运行 Lambda 函数，您需要为函数配置触发器。您可以使用控制台或 AWS CLI 配置 Amazon S3 触发器。

**重要**  
此程序将 Amazon S3 存储桶配置为每次在此存储桶中创建对象时调用您的函数。请确保仅在源存储桶上配置。如果您的 Lambda 函数在调用此函数的同一个存储桶中创建对象，则可以[在循环中持续调用](https://serverlessland.com/content/service/lambda/guides/aws-lambda-operator-guide/recursive-runaway)您的函数。这可能会导致您的 AWS 账户 产生额外费用。

------
#### [ AWS 管理控制台 ]

**配置 Amazon S3 触发器（控制台）**

1. 打开 Lambda 控制台的[函数页面](https://console.aws.amazon.com/lambda/home#/functions)，然后选择函数 (`CreateThumbnail`)。

1. 选择**添加触发器**。

1. 选择 **S3**。

1. 在**存储桶**下，选择自己的源存储桶。

1. 在**事件类型**下，选择**所有对象创建事件**。

1. 在**递归调用**下，选中复选框以确认知晓不建议使用相同的 Amazon S3 存储桶用于输入和输出。您可以阅读 Serverless Land 中的 [Recursive patterns that cause run-away Lambda functions](https://serverlessland.com/content/service/lambda/guides/aws-lambda-operator-guide/recursive-runaway)，进一步了解 Lambda 中的递归调用模式。

1. 选择**添加**。

   在您使用 Lambda 控制台创建触发器时，Lambda 会自动创建[基于资源的策略](https://docs.aws.amazon.com/lambda/latest/dg/access-control-resource-based.html)，授予您选择的服务调用函数的权限。

------
#### [ AWS CLI ]

**配置 Amazon S3 触发器（AWS CLI）**

1. 要让 Amazon S3 源存储桶在添加图像文件时调用函数，您首先需要使用[基于资源的策略](https://docs.aws.amazon.com/lambda/latest/dg/access-control-resource-based.html)为函数配置权限。基于资源的策略声明授予其他 AWS 服务 调用您函数的权限。要授予 Amazon S3 调用函数的权限，请运行以下 CLI 命令。请务必将 `source-account` 参数替换为您的 AWS 账户 ID 并使用自己的源存储桶名称。

   ```
   aws lambda add-permission --function-name CreateThumbnail \
   --principal s3.amazonaws.com --statement-id s3invoke --action "lambda:InvokeFunction" \
   --source-arn arn:aws:s3:::amzn-s3-demo-source-bucket \
   --source-account 123456789012
   ```

   您使用此命令定义的策略允许 Amazon S3 仅在源存储桶上执行操作时调用函数。
**注意**  
虽然 Amazon S3 存储桶名称具有全局唯一性，但在使用基于资源的策略时，最佳做法是指定存储桶必须属于您的账户。这是因为，如果删除一个存储桶，则另一个 AWS 账户 账户可能会创建具有相同 Amazon 资源名称（ARN）的存储桶。

1. 将下列 JSON 保存在名为 `notification.json` 的文件中。在应用到您的源存储桶时，此 JSON 会将存储桶配置为在每次添加新对象时向 Lambda 函数发送通知。将 Lambda 函数 ARN 中的 AWS 账户 号码和 AWS 区域 替换为您自己的账号和区域。

   ```
   {
   "LambdaFunctionConfigurations": [
       {
         "Id": "CreateThumbnailEventConfiguration",
         "LambdaFunctionArn": "arn:aws:lambda:us-east-1:123456789012:function:CreateThumbnail",
         "Events": [ "s3:ObjectCreated:Put" ]
       }
     ]
   }
   ```

1. 运行以下 CLI 命令，将您创建的 JSON 文件中的通知设置应用到源存储桶。将 `amzn-s3-demo-source-bucket` 替换为源存储桶的名称。

   ```
   aws s3api put-bucket-notification-configuration --bucket amzn-s3-demo-source-bucket \
   --notification-configuration file://notification.json
   ```

   要了解有关 `put-bucket-notification-configuration` 命令和 `notification-configuration` 选项的更多信息，请参阅 *AWS CLI 命令参考*中的 [put-bucket-notification-configuration](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3api/put-bucket-notification-configuration.html)。

------

## 使用虚拟事件测试 Lambda 函数
<a name="with-s3-tutorial-dummy-test"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-s3-tutorial/s3thumb_tut_steps8.png)


在通过向 Amazon S3 源存储桶添加图像文件来测试整个设置之前，您可以通过使用虚拟事件调用 Lambda 函数来测试此函数是否正常运行。Lambda 中的事件是 JSON 格式的文档，其中包含要处理的函数数据。Amazon S3 调用您的函数时，发送到函数的事件包含存储桶名称、存储桶 ARN 和对象键等信息。

------
#### [ AWS 管理控制台 ]

**使用虚拟事件测试 Lambda 函数（控制台）**

1. 打开 Lambda 控制台的[函数页面](https://console.aws.amazon.com/lambda/home#/functions)，然后选择函数 (`CreateThumbnail`)。

1. 选择**测试**选项卡。

1. 要创建测试事件，在**测试事件**窗格中，执行以下操作：

   1. 在**测试事件操作**下，选择**创建新事件**。

   1. 对于**事件名称**，输入 **myTestEvent**。

   1. 在**模板**中，选择 **S3 Put**。

   1. 将以下参数的值替换为您自己的值。
      + 对于 `awsRegion`，将 `us-east-1` 替换为在其中创建 Amazon S3 存储桶的 AWS 区域。
      + 对于 `name`，将 `amzn-s3-demo-bucket` 替换为您自己的 Amazon S3 源存储桶的名称。
      + 对于 `key`，将 `test%2Fkey` 替换为您在步骤 [将测试图片上传到源存储桶](#with-s3-tutorial-test-image) 中上传到源存储桶的测试对象的文件名。

      ```
      {
        "Records": [
          {
            "eventVersion": "2.0",
            "eventSource": "aws:s3",
            "awsRegion": "us-east-1",
            "eventTime": "1970-01-01T00:00:00.000Z",
            "eventName": "ObjectCreated:Put",
            "userIdentity": {
              "principalId": "EXAMPLE"
            },
            "requestParameters": {
              "sourceIPAddress": "127.0.0.1"
            },
            "responseElements": {
              "x-amz-request-id": "EXAMPLE123456789",
              "x-amz-id-2": "EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH"
            },
            "s3": {
              "s3SchemaVersion": "1.0",
              "configurationId": "testConfigRule",
              "bucket": {
                "name": "amzn-s3-demo-bucket",
                "ownerIdentity": {
                  "principalId": "EXAMPLE"
                },
                "arn": "arn:aws:s3:::amzn-s3-demo-bucket"
              },
              "object": {
                "key": "test%2Fkey",
                "size": 1024,
                "eTag": "0123456789abcdef0123456789abcdef",
                "sequencer": "0A1B2C3D4E5F678901"
              }
            }
          }
        ]
      }
      ```

   1. 选择**保存**。

1. 在**测试事件**窗格中，选择**测试**。

1. 要检查您的函数是否已创建图像的调整大小版本并将其存储在目标 Amazon S3 存储桶中，请执行以下操作：

   1. 打开 Amazon S3 控制台的[存储桶页面](https://console.aws.amazon.com/s3/buckets)。

   1. 选择目标存储桶并确认调整大小的文件已在**对象**窗格中列出。

------
#### [ AWS CLI ]

**使用虚拟事件测试 Lambda 函数（AWS CLI）**

1. 将下列 JSON 保存在名为 `dummyS3Event.json` 的文件中。将以下参数的值替换为您自己的值：
   + 对于 `awsRegion`，将 `us-east-1` 替换为在其中创建 Amazon S3 存储桶的 AWS 区域。
   + 对于 `name`，将 `amzn-s3-demo-bucket` 替换为您自己的 Amazon S3 源存储桶的名称。
   + 对于 `key`，将 `test%2Fkey` 替换为您在步骤 [将测试图片上传到源存储桶](#with-s3-tutorial-test-image) 中上传到源存储桶的测试对象的文件名。

   ```
   {
     "Records": [
       {
         "eventVersion": "2.0",
         "eventSource": "aws:s3",
         "awsRegion": "us-east-1",
         "eventTime": "1970-01-01T00:00:00.000Z",
         "eventName": "ObjectCreated:Put",
         "userIdentity": {
           "principalId": "EXAMPLE"
         },
         "requestParameters": {
           "sourceIPAddress": "127.0.0.1"
         },
         "responseElements": {
           "x-amz-request-id": "EXAMPLE123456789",
           "x-amz-id-2": "EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH"
         },
         "s3": {
           "s3SchemaVersion": "1.0",
           "configurationId": "testConfigRule",
           "bucket": {
             "name": "amzn-s3-demo-bucket",
             "ownerIdentity": {
               "principalId": "EXAMPLE"
             },
             "arn": "arn:aws:s3:::amzn-s3-demo-bucket"
           },
           "object": {
             "key": "test%2Fkey",
             "size": 1024,
             "eTag": "0123456789abcdef0123456789abcdef",
             "sequencer": "0A1B2C3D4E5F678901"
           }
         }
       }
     ]
   }
   ```

1. 在保存 `dummyS3Event.json` 文件的目录中，运行以下 CLI 命令来调用函数。此命令通过指定 `RequestResponse` 作为调用类型参数的值来同步调用 Lambda 函数。要了解有关同步和异步调用的更多信息，请参阅[调用 Lambda 函数](https://docs.aws.amazon.com/lambda/latest/dg/lambda-invocation.html)。

   ```
   aws lambda invoke --function-name CreateThumbnail \
   --invocation-type RequestResponse --cli-binary-format raw-in-base64-out \
   --payload file://dummyS3Event.json outputfile.txt
   ```

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

1. 验证您的函数是否已创建图像的缩略图版本并已保存到目标 Amazon S3 存储桶中。运行以下 CLI 命令，将 `amzn-s3-demo-source-bucket-resized` 替换为自己的目标存储桶的名称。

   ```
   aws s3api list-objects-v2 --bucket amzn-s3-demo-source-bucket-resized
   ```

   您应该可以看到类似于如下所示的输出内容。`Key` 参数显示调整大小后的图像文件的文件名。

   ```
   {
       "Contents": [
           {
               "Key": "resized-HappyFace.jpg",
               "LastModified": "2023-06-06T21:40:07+00:00",
               "ETag": "\"d8ca652ffe83ba6b721ffc20d9d7174a\"",
               "Size": 2633,
               "StorageClass": "STANDARD"
           }
       ]
   }
   ```

------

## 使用 Amazon S3 触发器测试函数
<a name="with-s3-tutorial-test-s3"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-s3-tutorial/s3thumb_tut_steps9.png)


此时，您已确认 Lambda 函数运行正常，您可以通过向 Amazon S3 源存储桶添加图像文件来测试完整设置。将图像添加到源存储桶时，应自动调用 Lambda 函数。函数会创建文件已调整大小的版本并将其存储在目标存储桶中。

------
#### [ AWS 管理控制台 ]

**使用 Amazon S3 触发器测试 Lambda 函数（控制台）**

1. 要将图像上传到 Amazon S3 存储桶，请执行以下操作：

   1. 打开 Amazon S3 控制台的[存储桶](https://console.aws.amazon.com/s3/buckets)页面，然后选择您的源存储桶。

   1. 选择**上传**。

   1. 选择**添加文件**，然后使用文件选择器选择要上传的图像文件。您的图像对象可以是任何.jpg 或.png 文件。

   1. 选择**打开**，然后选择**上传**。

1. 执行以下操作，验证 Lambda 是否已将调整大小后的图像文件保存在目标存储桶中：

   1. 导航回 Amazon S3 控制台的[存储桶](https://console.aws.amazon.com/s3/buckets)页面，然后选择您的目标存储桶。

   1. 在**对象**窗格中，您现在应该能看到两个已调整大小的图像文件，其中一个来自 Lambda 函数的每次测试。要下载已调整大小的图像，请选择所需文件，然后选择**下载**。

------
#### [ AWS CLI ]

**使用 Amazon S3 触发器测试 Lambda 函数（AWS CLI）**

1. 在包含要上传的图像的目录中，运行以下 CLI 命令。将 `--bucket` 参数替换为源存储桶的名称。对于`--key` 和 `--body` 参数，请使用测试图像的文件名。您的测试图像可以是任何.jpg 或.png 文件。

   ```
   aws s3api put-object --bucket amzn-s3-demo-source-bucket --key SmileyFace.jpg --body ./SmileyFace.jpg
   ```

1. 验证您的函数是否已创建图像的缩略图版本并已保存到目标 Amazon S3 存储桶中。运行以下 CLI 命令，将 `amzn-s3-demo-source-bucket-resized` 替换为自己的目标存储桶的名称。

   ```
   aws s3api list-objects-v2 --bucket amzn-s3-demo-source-bucket-resized
   ```

   如果函数成功运行，您将看到类似于以下内容的输出。您的目标存储桶现在应该包含两个已调整大小的文件。

   ```
   {
       "Contents": [
           {
               "Key": "resized-HappyFace.jpg",
               "LastModified": "2023-06-07T00:15:50+00:00",
               "ETag": "\"7781a43e765a8301713f533d70968a1e\"",
               "Size": 2763,
               "StorageClass": "STANDARD"
           },
           {
               "Key": "resized-SmileyFace.jpg",
               "LastModified": "2023-06-07T00:13:18+00:00",
               "ETag": "\"ca536e5a1b9e32b22cd549e18792cdbc\"",
               "Size": 1245,
               "StorageClass": "STANDARD"
           }
       ]
   }
   ```

------

## 清除资源
<a name="s3-tutorial-cleanup"></a>

除非您想要保留为本教程创建的资源，否则可立即将其删除。通过删除您不再使用的 AWS 资源，可防止您的 AWS 账户 产生不必要的费用。

**删除 Lambda 函数**

1. 打开 Lamba 控制台的 [Functions（函数）页面](https://console.aws.amazon.com/lambda/home#/functions)。

1. 选择您创建的函数。

1. 依次选择**操作**和**删除**。

1. 在文本输入字段中键入 **confirm**，然后选择 **Delete**（删除）。

**删除您创建的策略**

1. 打开 IAM 控制台的 [Policies（策略）页面](https://console.aws.amazon.com/iam/home#/policies)。

1. 选择您创建的策略 (**AWSLambdaS3Policy**)。

1. 选择 **Policy actions**（策略操作）、**Delete**（删除）。

1. 选择 **Delete**（删除）。

**删除执行角色**

1. 打开 IAM 控制台的[角色页面](https://console.aws.amazon.com/iam/home#/roles)。

1. 选择您创建的执行角色。

1. 选择**删除**。

1. 在文本输入字段中输入角色名称，然后选择 **Delete**（删除）。

**删除 S3 存储桶**

1. 打开 [Amazon S3 控制台](https://console.aws.amazon.com//s3/home#)。

1. 选择您创建的存储桶。

1. 选择**删除**。

1. 在文本输入字段中输入存储桶的名称。

1. 选择**删除存储桶**。

# 在 Lambda 函数中使用 Secrets Manager 密钥
<a name="with-secrets-manager"></a>

AWS Secrets Manager 可帮助您管理 Lambda 函数所需的凭证、API 密钥和其他密钥。您有两种主要方法可以在 Lambda 函数中检索密钥，与直接使用 AWS SDK 检索密钥相比，这两种方法的性能更佳且成本更低：
+ **AWS 参数和密钥 Lambda 扩展** - 一种与运行时无关的解决方案，提供一个简单的 HTTP 接口来检索密钥
+ **Powertools for AWS Lambda 参数实用程序** - 支持多个提供商（Secrets Manager、Parameter Store、AppConfig）的代码集成解决方案，具有内置转换功能

这两种方法都维护密钥的本地缓存，从而无需您的函数在每次调用时都调用 Secrets Manager。当您的函数请求密钥时，将首先检查缓存。如果密钥可用且尚未过期，则会立即返回该密钥。否则，将从 Secrets Manager 中检索、缓存并返回密钥。这种缓存机制通过最大限度地减少 API 调用，可以缩短响应时间并降低成本。

## 选择方法
<a name="lambda-secrets-manager-choosing-approach"></a>

在扩展和 PowerTools 之间进行选择时，请考虑以下因素：

在以下情况下使用 AWS 参数和密钥 Lambda 扩展：  
+ 您需要一个与任何 Lambda 运行时兼容的、与运行时无关的解决方案
+ 您不想在函数中添加代码依赖项
+ 您只需从 Secrets Manager 或 Parameter Store 检索密钥

在以下情况下使用 Powertools for AWS Lambda 参数实用程序：  
+ 您想要获得与应用程序代码集成的开发体验
+ 您需要支持多个提供商（Secrets Manager、Parameter Store、AppConfig）
+ 您需要内置数据转换（JSON 解析、base64 解码）
+ 您正在使用 Python、TypeScript、Java 或 .NET 运行时

## 何时将 Secrets Manager 与 Lambda 结合使用
<a name="lambda-secrets-manager-when-to-use"></a>

将 Secrets Manager 与 Lambda 结合使用的常见场景包括：
+ 存储您的函数用于连接到 Amazon RDS 或其他数据库的数据库凭证
+ 管理您的函数调用的外部服务的 API 密钥
+ 存储加密密钥或其他敏感配置数据
+ 自动轮换凭证，无需更新函数代码

## 使用 AWS 参数和密钥 Lambda 扩展
<a name="lambda-secrets-manager-extension-approach"></a>

AWS 参数和密钥 Lambda 扩展使用与任何 Lambda 运行时兼容的简单 HTTP 接口。默认情况下，它会缓存密钥 300 秒（5 分钟），最多可容纳 1,000 个密钥。您可以[使用环境变量自定义这些设置](#lambda-secrets-manager-env-vars)。

### 在 Lambda 函数中使用 Secrets Manager
<a name="lambda-secrets-manager-setup"></a>

本节假设您已经拥有 Secrets Manager 密钥。要创建密钥，请参阅 [Create an AWS Secrets Manager secret](https://docs.aws.amazon.com/secretsmanager/latest/userguide/create_secret.html)。

#### 创建部署程序包
<a name="lambda-secrets-manager-function-code"></a>

选择您的首选运行时，并按照步骤创建用于从 Secrets Manager 检索密钥的函数。该示例函数从 Secrets Manager 中检索密钥，并可用于访问数据库凭证、API 密钥或应用程序中的其他敏感配置数据。

------
#### [ Python ]

**创建 Python 函数**

1. 创建并导航到新的项目目录。示例：

   ```
   mkdir my_function
   cd my_function
   ```

1. 使用以下代码创建名为 `lambda_function.py` 的文件。对于 `secret_name`，使用您的密钥的名称或 Amazon 资源名称 (ARN)。

   ```
   import json
   import os
   import requests
   
   def lambda_handler(event, context):
       try:
           # Replace with the name or ARN of your secret
           secret_name = "arn:aws:secretsmanager:us-east-1:111122223333:secret:SECRET_NAME"
           
           secrets_extension_endpoint = f"http://localhost:2773/secretsmanager/get?secretId={secret_name}"
           headers = {"X-Aws-Parameters-Secrets-Token": os.environ.get('AWS_SESSION_TOKEN')}
           
           response = requests.get(secrets_extension_endpoint, headers=headers)
           print(f"Response status code: {response.status_code}")
           
           secret = json.loads(response.text)["SecretString"]
           print(f"Retrieved secret: {secret}")
           
           return {
               'statusCode': response.status_code,
               'body': json.dumps({
                   'message': 'Successfully retrieved secret',
                   'secretRetrieved': True
               })
           }
       
       except Exception as e:
           print(f"Error: {str(e)}")
           return {
               'statusCode': 500,
               'body': json.dumps({
                   'message': 'Error retrieving secret',
                   'error': str(e)
               })
           }
   ```

1. 使用此内容创建名为 `requirements.txt` 的文件：

   ```
   requests
   ```

1. 安装依赖项：

   ```
   pip install -r requirements.txt -t .
   ```

1. 创建包含所有文件的 .zip 文件：

   ```
   zip -r function.zip .
   ```

------
#### [ Node.js ]

**创建 Node.js 函数**

1. 创建并导航到新的项目目录。示例：

   ```
   mkdir my_function
   cd my_function
   ```

1. 使用以下代码创建名为 `index.mjs` 的文件。对于 `secret_name`，使用您的密钥的名称或 Amazon 资源名称 (ARN)。

   ```
   import http from 'http';
   
   export const handler = async (event) => {
       try {
           // Replace with the name or ARN of your secret
           const secretName = "arn:aws:secretsmanager:us-east-1:111122223333:secret:SECRET_NAME";
           const options = {
               hostname: 'localhost',
               port: 2773,
               path: `/secretsmanager/get?secretId=${secretName}`,
               headers: {
                   'X-Aws-Parameters-Secrets-Token': process.env.AWS_SESSION_TOKEN
               }
           };
   
           const response = await new Promise((resolve, reject) => {
               http.get(options, (res) => {
                   let data = '';
                   res.on('data', (chunk) => { data += chunk; });
                   res.on('end', () => {
                       resolve({ 
                           statusCode: res.statusCode, 
                           body: data 
                       });
                   });
               }).on('error', reject);
           });
   
           const secret = JSON.parse(response.body).SecretString;
           console.log('Retrieved secret:', secret);
   
           return {
               statusCode: response.statusCode,
               body: JSON.stringify({
                   message: 'Successfully retrieved secret',
                   secretRetrieved: true
               })
           };
       } catch (error) {
           console.error('Error:', error);
           return {
               statusCode: 500,
               body: JSON.stringify({
                   message: 'Error retrieving secret',
                   error: error.message
               })
           };
       }
   };
   ```

1. 创建包含 `index.mjs` 文件的 .zip 文件：

   ```
   zip -r function.zip index.mjs
   ```

------
#### [ Java ]

**创建 Java 函数**

1. 创建 Maven 项目：

   ```
   mvn archetype:generate \
       -DgroupId=example \
       -DartifactId=lambda-secrets-demo \
       -DarchetypeArtifactId=maven-archetype-quickstart \
       -DarchetypeVersion=1.4 \
       -DinteractiveMode=false
   ```

1. 导航到项目目录：

   ```
   cd lambda-secrets-demo
   ```

1. 打开 `pom.xml` 并将内容替换为以下内容：

   ```
   <project xmlns="http://maven.apache.org/POM/4.0.0"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
       <modelVersion>4.0.0</modelVersion>
   
       <groupId>example</groupId>
       <artifactId>lambda-secrets-demo</artifactId>
       <version>1.0-SNAPSHOT</version>
   
       <properties>
           <maven.compiler.source>11</maven.compiler.source>
           <maven.compiler.target>11</maven.compiler.target>
       </properties>
   
       <dependencies>
           <dependency>
               <groupId>com.amazonaws</groupId>
               <artifactId>aws-lambda-java-core</artifactId>
               <version>1.2.1</version>
           </dependency>
       </dependencies>
   
       <build>
           <plugins>
               <plugin>
                   <groupId>org.apache.maven.plugins</groupId>
                   <artifactId>maven-shade-plugin</artifactId>
                   <version>3.2.4</version>
                   <executions>
                       <execution>
                           <phase>package</phase>
                           <goals>
                               <goal>shade</goal>
                           </goals>
                           <configuration>
                               <createDependencyReducedPom>false</createDependencyReducedPom>
                               <finalName>function</finalName>
                           </configuration>
                       </execution>
                   </executions>
               </plugin>
           </plugins>
       </build>
   </project>
   ```

1. 将 `/lambda-secrets-demo/src/main/java/example/App.java` 重命名为 `Hello.java`，以匹配 Lambda 的默认 Java 处理程序名称 (`example.Hello::handleRequest`)：

   ```
   mv src/main/java/example/App.java src/main/java/example/Hello.java
   ```

1. 打开 `Hello.java` 文件并将其内容替换为以下内容。对于 `secretName`，使用您的密钥的名称或 Amazon 资源名称 (ARN)。

   ```
   package example;
   
   import com.amazonaws.services.lambda.runtime.Context;
   import com.amazonaws.services.lambda.runtime.RequestHandler;
   import java.net.URI;
   import java.net.http.HttpClient;
   import java.net.http.HttpRequest;
   import java.net.http.HttpResponse;
   
   public class Hello implements RequestHandler<Object, String> {
       private final HttpClient client = HttpClient.newHttpClient();
   
       @Override
       public String handleRequest(Object input, Context context) {
           try {
               // Replace with the name or ARN of your secret
               String secretName = "arn:aws:secretsmanager:us-east-1:111122223333:secret:SECRET_NAME";
               String endpoint = "http://localhost:2773/secretsmanager/get?secretId=" + secretName;
   
               HttpRequest request = HttpRequest.newBuilder()
                   .uri(URI.create(endpoint))
                   .header("X-Aws-Parameters-Secrets-Token", System.getenv("AWS_SESSION_TOKEN"))
                   .GET()
                   .build();
   
               HttpResponse<String> response = client.send(request, 
                   HttpResponse.BodyHandlers.ofString());
   
               String secret = response.body();
               secret = secret.substring(secret.indexOf("SecretString") + 15);
               secret = secret.substring(0, secret.indexOf("\""));
   
               System.out.println("Retrieved secret: " + secret);
               return String.format(
                   "{\"statusCode\": %d, \"body\": \"%s\"}",
                   response.statusCode(), "Successfully retrieved secret"
               );
   
           } catch (Exception e) {
               e.printStackTrace();
               return String.format(
                   "{\"body\": \"Error retrieving secret: %s\"}", 
                   e.getMessage()
               );
           }
       }
   }
   ```

1. 删除测试目录。Maven 默认创建此项，但在本例中我们不需要它。

   ```
   rm -rf src/test
   ```

1. 构建项目：

   ```
   mvn package
   ```

1. 下载 JAR 文件 (`target/function.jar`) 以供稍后使用。

------

#### 创建函数
<a name="lambda-secrets-manager-create"></a>

1. 打开 Lamba 控制台的 [Functions page](https://console.aws.amazon.com/lambda/home#/functions)（函数页面）。

1. 选择**创建函数**。

1. 选择**从头开始编写**。

1. 对于**函数名称**，请输入 **secret-retrieval-demo**。

1. 选择您的首选**运行时**。

1. 选择**创建函数**。

**上传部署包**

1. 在函数的**代码**选项卡中，选择**上传自**，然后选择 **.zip 文件**（对于 Python 和 Node.js）或 **.jar 文件**（对于 Java）。

1. 上传您之前创建的部署包。

1. 选择**保存**。

#### 添加扩展
<a name="lambda-secrets-manager-extension"></a>

**将 AWS 参数和密钥 Lambda 扩展添加为层**

1. 在函数的**代码**选项卡中，向下滚动到**层**。

1. 选择 **Add a layer**。

1. 选择 **AWS 层**。

1. 选择 **AWS-Parameters-and-Secrets-Lambda-Extension**。

1. 选择最新版本。

1. 选择**添加**。

#### 添加权限
<a name="lambda-secrets-manager-permissions"></a>

**向您的执行角色添加 Secrets Manager 权限**

1. 选择 **Configuration**（配置）选项卡，然后选择 **Permissions**（权限）。

1. 在**角色名称**下，选择至执行角色的链接。此角色将在 IAM 控制台中打开角色。  
![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/execution-role-console.png)

1. 选择**添加权限**，然后选择**创建内联策略**。  
![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/create-inline-policy.png)

1. 选择 **JSON** 选项卡，然后添加以下策略。对于 `Resource`，输入您的密钥的 ARN。

------
#### [ JSON ]

****  

   ```
   {
       "Version":"2012-10-17",		 	 	 
       "Statement": [
           {
               "Effect": "Allow",
               "Action": "secretsmanager:GetSecretValue",
               "Resource": "arn:aws:secretsmanager:us-east-1:111122223333:secret:SECRET_NAME"
           }
       ]
   }
   ```

------

1. 选择**下一步**。

1. 输入策略的名称。

1. 选择**创建策略**。

#### 测试此函数
<a name="lambda-secrets-manager-test"></a>

**测试此函数**

1. 返回 Lambda 控制台。

1. 选择**测试**选项卡。

1. 选择**测试**。您应看到以下响应：  
![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/execution-results-secret.png)

### 环境变量
<a name="lambda-secrets-manager-env-vars"></a>

AWS 参数和密钥 Lambda 扩展使用下面的默认设置。您可以通过创建相应的[环境变量](configuration-envvars.md#create-environment-variables)来覆盖这些设置。要查看函数的当前设置，请将 `PARAMETERS_SECRETS_EXTENSION_LOG_LEVEL` 设置为 `DEBUG`。该扩展将在每次函数调用开始时将其配置信息记录到 CloudWatch Logs 中。


| 设置 | 默认 值 | 有效值 | 环境变量 | Details | 
| --- | --- | --- | --- | --- | 
| HTTP 端口 | 2773 | 1 - 65535 | PARAMETERS\$1SECRETS\$1EXTENSION\$1HTTP\$1PORT | 本地 HTTP 服务器的端口 | 
| 已启用缓存 | TRUE | TRUE \$1 FALSE | PARAMETERS\$1SECRETS\$1EXTENSION\$1CACHE\$1ENABLED | 启用或禁用缓存 | 
| 缓存大小 | 1000 | 0 - 1000 | PARAMETERS\$1SECRETS\$1EXTENSION\$1CACHE\$1SIZE | 设置为 0 以禁用缓存 | 
| Secrets Manager TTL | 300 秒 | 0 - 300 秒 | SECRETS\$1MANAGER\$1TTL | 缓存密钥的生存时间。设置为 0 以禁用缓存。如果 PARAMETERS\$1SECRETS\$1EXTENSION\$1CACHE\$1SIZE 的值为 0，则忽略此变量。 | 
| Parameter Store TTL | 300 秒 | 0 - 300 秒 | SSM\$1PARAMETER\$1STORE\$1TTL | 缓存参数的生存时间。设置为 0 以禁用缓存。如果 PARAMETERS\$1SECRETS\$1EXTENSION\$1CACHE\$1SIZE 的值为 0，则忽略此变量。 | 
| 日志级别 | INFO | 调试 \$1 信息 \$1 警告 \$1 错误 \$1 无 | PARAMETERS\$1SECRETS\$1EXTENSION\$1LOG\$1LEVEL | 扩展日志中报告的详细信息级别 | 
| 最大连接数 | 3 | 1 或更多 | PARAMETERS\$1SECRETS\$1EXTENSION\$1MAX\$1CONNECTIONS | 对 Parameter Store 或 Secrets Manager 的请求的最大 HTTP 连接数 | 
| Secrets Manager 超时 | 0（无超时） | 所有整数 | SECRETS\$1MANAGER\$1TIMEOUT\$1MILLIS | Secrets Manager 的请求超时（以毫秒为单位） | 
| Parameter Store 超时 | 0（无超时） | 所有整数 | SSM\$1PARAMETER\$1STORE\$1TIMEOUT\$1MILLIS | Parameter Store 的请求超时（以毫秒为单位） | 

### 使用密钥轮换
<a name="lambda-secrets-manager-rotation"></a>

如果频繁轮换密钥，则默认 300 秒缓存持续时间可能会导致您的函数使用过时的密钥。您有两种方法可以确保函数使用最新的密钥值：
+ 通过将 `SECRETS_MANAGER_TTL` 环境变量设置为较低的值（以秒为单位）来缩短缓存 TTL。例如，将其设置为 `60` 可确保函数永远不会使用超过一分钟的密钥。
+ 在密钥请求中使用 `AWSCURRENT` 或 `AWSPREVIOUS` 暂存标签来确保获得所需的特定版本：

  ```
  secretsmanager/get?secretId=YOUR_SECRET_NAME&versionStage=AWSCURRENT
  ```

选择最能平衡您对性能和新鲜度需求的方法。较低 TTL 意味着对 Secrets Manager 的调用更频繁，但可确保您使用最新的密钥值。

## 使用 Powertools for AWS Lambda 中的参数实用程序
<a name="lambda-secrets-manager-powertools-approach"></a>

Powertools for AWS Lambda 中的参数实用程序提供了一个统一的界面，用于从多个提供商（包括 Secrets Manager、Parameter Store 和 AppConfig）检索密钥。它处理缓存、转换，并提供与扩展方法相比更加集成的开发体验。

### 参数实用程序的好处
<a name="lambda-secrets-manager-powertools-benefits"></a>
+ **多个提供商** - 使用相同的接口从 Secrets Manager、Parameter Store 和 AppConfig 检索参数
+ **内置转换** - 自动 JSON 解析、base64 解码和其他数据转换
+ **集成缓存** - 支持 TTL 的可配置缓存，可减少 API 调用
+ **键入安全** - 在 TypeScript 和其他支持的运行时中提供强大的键入支持
+ **错误处理** - 内置重试逻辑和错误处理

### 代码示例
<a name="lambda-secrets-manager-powertools-examples"></a>

以下示例展示了如何在不同的运行时使用参数实用程序检索密钥：

**Python**  
有关完整示例和设置说明，请参阅[参数实用程序文档](https://docs.powertools.aws.dev/lambda/python/latest/utilities/parameters/)。
使用 Powertools for AWS Lambda 参数实用程序从 Secrets Manager 中检索密钥。  

```
from aws_lambda_powertools import Logger
from aws_lambda_powertools.utilities import parameters

logger = Logger()

def lambda_handler(event, context):
    try:
        # Get secret with caching (default TTL: 5 seconds)
        secret_value = parameters.get_secret("my-secret-name")
        
        # Get secret with custom TTL
        secret_with_ttl = parameters.get_secret("my-secret-name", max_age=300)
        
        # Get secret and transform JSON
        secret_json = parameters.get_secret("my-json-secret", transform="json")
        
        logger.info("Successfully retrieved secrets")
        
        return {
            'statusCode': 200,
            'body': 'Successfully retrieved secrets'
        }
        
    except Exception as e:
        logger.error(f"Error retrieving secret: {str(e)}")
        return {
            'statusCode': 500,
            'body': f'Error: {str(e)}'
        }
```

**TypeScript**  
有关完整示例和设置说明，请参阅[参数实用程序文档](https://docs.aws.amazon.com/powertools/typescript/2.1.1/utilities/parameters/)。
使用 Powertools for AWS Lambda 参数实用程序从 Secrets Manager 中检索密钥。  

```
import { Logger } from '@aws-lambda-powertools/logger';
import { getSecret } from '@aws-lambda-powertools/parameters/secrets';
import type { Context } from 'aws-lambda';

const logger = new Logger();

export const handler = async (event: any, context: Context) => {
    try {
        // Get secret with caching (default TTL: 5 seconds)
        const secretValue = await getSecret('my-secret-name');
        
        // Get secret with custom TTL
        const secretWithTtl = await getSecret('my-secret-name', { maxAge: 300 });
        
        // Get secret and transform JSON
        const secretJson = await getSecret('my-json-secret', { transform: 'json' });
        
        logger.info('Successfully retrieved secrets');
        
        return {
            statusCode: 200,
            body: 'Successfully retrieved secrets'
        };
        
    } catch (error) {
        logger.error('Error retrieving secret', { error });
        return {
            statusCode: 500,
            body: `Error: ${error}`
        };
    }
};
```

**Java**  
有关完整示例和设置说明，请参阅[参数实用程序文档](https://docs.powertools.aws.dev/lambda/java/latest/utilities/parameters/)。
使用 Powertools for AWS Lambda 参数实用程序从 Secrets Manager 中检索密钥。  

```
import software.amazon.lambda.powertools.logging.Logging;
import software.amazon.lambda.powertools.parameters.SecretsProvider;
import software.amazon.lambda.powertools.parameters.ParamManager;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;

public class SecretHandler implements RequestHandler<Object, String> {
    
    private final SecretsProvider secretsProvider = ParamManager.getSecretsProvider();
    
    @Logging
    @Override
    public String handleRequest(Object input, Context context) {
        try {
            // Get secret with caching (default TTL: 5 seconds)
            String secretValue = secretsProvider.get("my-secret-name");
            
            // Get secret with custom TTL (300 seconds)
            String secretWithTtl = secretsProvider.withMaxAge(300).get("my-secret-name");
            
            // Get secret and transform JSON
            MySecret secretJson = secretsProvider.get("my-json-secret", MySecret.class);
            
            return "Successfully retrieved secrets";
            
        } catch (Exception e) {
            return "Error retrieving secret: " + e.getMessage();
        }
    }
    
    public static class MySecret {
        // Define your secret structure here
    }
}
```

**.NET**  
有关完整示例和设置说明，请参阅[参数实用程序文档](https://docs.aws.amazon.com/powertools/typescript/latest/features/parameters/)。
使用 Powertools for AWS Lambda 参数实用程序从 Secrets Manager 中检索密钥。  

```
using AWS.Lambda.Powertools.Logging;
using AWS.Lambda.Powertools.Parameters;
using Amazon.Lambda.Core;

[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]

public class Function
{
    private readonly ISecretsProvider _secretsProvider;
    
    public Function()
    {
        _secretsProvider = ParametersManager.SecretsProvider;
    }
    
    [Logging]
    public async Task<string> FunctionHandler(object input, ILambdaContext context)
    {
        try
        {
            // Get secret with caching (default TTL: 5 seconds)
            var secretValue = await _secretsProvider.GetAsync("my-secret-name");
            
            // Get secret with custom TTL
            var secretWithTtl = await _secretsProvider.WithMaxAge(TimeSpan.FromMinutes(5))
                .GetAsync("my-secret-name");
            
            // Get secret and transform JSON
            var secretJson = await _secretsProvider.GetAsync<MySecret>("my-json-secret");
            
            return "Successfully retrieved secrets";
        }
        catch (Exception e)
        {
            return $"Error retrieving secret: {e.Message}";
        }
    }
    
    public class MySecret
    {
        // Define your secret structure here
    }
}
```

### 设置和权限
<a name="lambda-secrets-manager-powertools-setup"></a>

要使用参数实用程序，您需要：

1. 为您的运行时安装 Powertools for AWS Lambda。有关更多信息，请参阅 [Powertools for AWS Lambda](powertools-for-lambda.md)。

1. 向您的函数的执行角色添加必要的 IAM 权限。有关详细信息，请参阅[在 AWS Lambda 中管理权限](lambda-permissions.md)。

1. 通过[环境变量](configuration-envvars.md)配置任何可选设置。

所需的 IAM 权限与扩展方法相同。该实用程序将根据您的配置自动处理缓存和对 Secrets Manager 的 API 调用。

# 将 Lambda 与 Amazon SQS 结合使用
<a name="with-sqs"></a>

**注意**  
如果想要将数据发送到 Lambda 函数以外的目标，或要在发送数据之前丰富数据，请参阅 [Amazon EventBridge Pipes](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-pipes.html)（Amazon EventBridge 管道）。

您可以使用 Lambda 函数来处理某个 Amazon Simple Queue Service（Amazon SQS）队列中的消息。Lambda 支持[事件源映射](invocation-eventsourcemapping.md)的[标准队列](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/standard-queues.html)和[先进先出（FIFO）队列](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/FIFO-queues.html)。您也可以使用预置模式为您的 Amazon SQS 事件源映射分配专用的轮询资源。Lambda 函数和 Amazon SQS 队列必须位于同一 AWS 区域，即便二者可能位于[不同的 AWS 账户](with-sqs-cross-account-example.md)。

在处理 Amazon SQS 消息时，您需要实施部分批处理响应逻辑，以防止在批处理中的某些消息失败时重试成功处理的消息。Powertools for AWS Lambda 中的[批处理器实用程序](https://docs.powertools.aws.dev/lambda/python/latest/utilities/batch/)通过自动处理部分批处理响应逻辑简化了此实现，减少了开发时间并提高了可靠性。

**Topics**
+ [

## 了解 Amazon SQS 事件源映射的轮询和批处理行为
](#sqs-polling-behavior)
+ [

## 对 Amazon SQS 事件源映射使用预置模式
](#sqs-provisioned-mode)
+ [

## 为 Amazon SQS 事件源映射配置预置模式
](#sqs-configuring-provisioned-mode)
+ [

## 示例标准队列消息事件
](#example-standard-queue-message-event)
+ [

## 示例 FIFO 队列消息事件
](#sample-fifo-queues-message-event)
+ [

# 创建和配置 Amazon SQS 事件源映射
](services-sqs-configure.md)
+ [

# 为 SQS 事件源映射配置扩展行为
](services-sqs-scaling.md)
+ [

# 在 Lambda 中处理 SQS 事件源错误
](services-sqs-errorhandling.md)
+ [

# Amazon SQS 事件源映射的 Lambda 参数
](services-sqs-parameters.md)
+ [

# 对 Amazon SQS 事件源使用事件筛选
](with-sqs-filtering.md)
+ [

# 教程：将 Lambda 与 Amazon SQS 结合使用
](with-sqs-example.md)
+ [

# 教程：将跨账户 Amazon SQS 队列用作事件源
](with-sqs-cross-account-example.md)

## 了解 Amazon SQS 事件源映射的轮询和批处理行为
<a name="sqs-polling-behavior"></a>

使用 Amazon SQS 事件源映射，Lambda 会轮询队列并通过事件[同步](invocation-sync.md)调用您的函数。每个事件可以包含来自队列的一批多条消息。Lambda 每次接收一批这些事件，并为每个批次调用一次您的函数。当您的函数成功处理一个批次后，Lambda 就会将其消息从队列中删除。

当 Lambda 接收某个批次时，消息将保留在队列中，但会根据队列的[可见性超时](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-visibility-timeout.html)长度隐藏。如果您的函数成功处理了批次中的所有消息，Lambda 会将消息从队列中删除。预设情况下，如果您的函数在处理某个批处理时遇到错误，则该批处理中的所有消息都会在可见性超时过期后在队列中重新可见。因此，函数代码必须能够多次处理同一条消息，而不会产生意外的副作用。

**警告**  
Lambda 事件源映射至少处理每个事件一次，有可能出现重复处理记录的情况。为避免与重复事件相关的潜在问题，我们强烈建议您将函数代码设为幂等性。要了解更多信息，请参阅 AWS 知识中心的[如何让我的 Lambda 函数保持幂等性](https://repost.aws/knowledge-center/lambda-function-idempotent)。

要防止 Lambda 多次处理消息，您可以将事件源映射配置为在函数响应中包含[批次项目失败](services-sqs-errorhandling.md#services-sqs-batchfailurereporting)，也可以在 Lambda 函数成功处理消息后使用 [DeleteMessage](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_DeleteMessage.html) API 将消息从队列中删除。

有关 Lambda 支持的 SQS 事件源映射配置参数的更多信息，请参阅 [创建 SQS 事件源映射](services-sqs-configure.md#events-sqs-eventsource)。

## 对 Amazon SQS 事件源映射使用预置模式
<a name="sqs-provisioned-mode"></a>

对于需要微调事件源映射吞吐量的工作负载，您可以使用预调配模式。在预调配模式下，您可以为预调配事件轮询器数量定义最小和最大限制。这些预调配事件轮询器专用于事件源映射，并且可以通过响应式自动扩缩处理意外的消息激增。配置了预置模式的 Amazon SQS 事件源映射的扩展速度比默认的 Amazon SQS 事件源映射功能快 3 倍（每分钟最多 1000 次并发调用），并且支持高 16 倍的并发性（最多 20000 次并发调用）。我们建议，对于具有严格性能要求的 Amazon SQS 事件驱动型工作负载（例如处理市场数据源的金融服务公司、提供实时个性化推荐的电子商务平台以及管理实时玩家互动的游戏公司等），您应采用预置模式。使用预调配模式会产生额外成本。有关详细定价，请参阅 [AWS Lambda 定价](https://aws.amazon.com/lambda/pricing/)。

每个事件轮询器在预置模式下能处理最高 1 MB/s 的吞吐量、最多 10 次并发调用，或者每秒最多进行 10 次 Amazon SQS 轮询 API 调用。最小事件轮询器数量（MinimumPollers）的可接受值范围介于 2 到 200 之间（默认为 2）。最大事件轮询器数量（MaximumPollers）的可接受值范围介于 2 到 2000 之间（默认为 200）。MaximumPollers 必须大于或等于 MinimumPollers。

### 确定所需的事件轮询器
<a name="sqs-determining-event-pollers"></a>

要估算在使用 SQS ESM 的预置模式下确保最佳消息处理性能所需的事件轮询器数量，请为您的应用程序收集以下指标：每秒需要低延迟处理的峰值 SQS 事件数量、平均 SQS 事件有效载荷大小、平均 Lambda 函数执行持续时间以及配置的批处理大小。

首先，您可以使用以下公式来估算事件轮询器为您的工作负载支持的每秒 SQS 事件数量（EPS）：

```
EPS per event poller = 
        minimum(
            ceiling(1024 / average event size in KB),
            ceiling(10 / average function duration in seconds) * batch size, 
            min(100, 10 * batch size)
                )
```

然后，您可以使用以下公式计算所需的最小轮询器数量。此计算可确保您预置足够的容量来满足峰值流量要求。

```
Required event pollers = (Peak number of events per second in Queue) / EPS per event poller
```

考虑这样一个工作负载，其默认批处理大小为 10，平均事件大小为 3KB，平均函数执行时间为 100 ms，并且需要每秒处理 1000 个事件。在这种情况下，每个事件轮询器每秒将支持大约 100 个事件（EPS）。因此，您应将最小轮询器数量设置为 10，以充分满足您的峰值流量需求。如果您的工作负载具有相同的特性，但函数平均持续时间为 1 秒，则每个轮询器仅能支持 10 个 EPS，这就要求您将最小轮询器数量配置为 100，以在低延迟的情况下每秒支持 1000 个事件。

我们建议使用 10 或更高的默认批处理大小，以最大限度地提高预置模式事件轮询器的效率。更大的批次大小使得每个轮询器在每次调用时能够处理更多的事件，从而提高了吞吐量和成本效益。在规划事件轮询器容量时，请考虑潜在的流量峰值，并考虑将 minimumPollers 值设置为略高于计算得出的最小值，以提供缓冲区。此外，持续监控您的工作负载特征的变化情况，因为消息大小、函数持续时间或流量模式的改变可能会需要对您的事件轮询器配置进行调整，以保持最佳性能和成本效益。为了进行精确的容量规划，我们建议您测试您的特定工作负载，以确定每个事件轮询器可以驱动的实际 EPS。

## 为 Amazon SQS 事件源映射配置预置模式
<a name="sqs-configuring-provisioned-mode"></a>

您可以使用控制台或 Lambda API 为 Amazon SQS 事件源映射配置预置模式。

**为现有的 Amazon SQS 事件源映射配置预置模式（控制台）**

1. 打开 Lamba 控制台的[函数](https://console.aws.amazon.com/lambda/home#/functions)页面。

1. 选择具有要为其配置预置模式的 Amazon SQS 事件源映射的函数。

1. 选择**配置**，然后选择**触发器**。

1. 选择要为其配置预置模式的 Amazon SQS 事件源映射，然后选择**编辑**。

1. 在**事件源映射配置**下，选择**配置预调配模式**。
   + 对于**最少事件轮询器**，输入介于 2 到 200 之间的值。如果未指定值，则 Lambda 将选择默认值 2。
   + 对于**最大事件轮询器**，输入介于 2 到 2000 之间的值。此值必须大于或等于**最少事件轮询器**的值。如果未指定值，则 Lambda 将选择默认值 200。

1. 选择**保存**。

您可以使用 `EventSourceMappingConfiguration` 中的 `ProvisionedPollerConfig` 对象以编程方式配置预置模式。例如，以下 `UpdateEventSourceMapping` CLI 命令将 `MinimumPollers` 值配置为 5，将 `MaximumPollers` 值配置为 100。

```
aws lambda update-event-source-mapping \
    --uuid a1b2c3d4-5678-90ab-cdef-EXAMPLE11111 \
    --provisioned-poller-config '{"MinimumPollers": 5, "MaximumPollers": 100}'
```

配置预调配模式后，您可以通过监控 `ProvisionedPollers` 指标来观测事件轮询器对您的工作负载的使用情况。有关更多信息，请参阅事件源映射指标。

要禁用预置模式并返回默认（按需）模式，您可以使用以下 `UpdateEventSourceMapping` CLI 命令：

```
aws lambda update-event-source-mapping \
    --uuid a1b2c3d4-5678-90ab-cdef-EXAMPLE11111 \
    --provisioned-poller-config '{}'
```

**注意**  
预置模式不能与最大并发设置结合使用。使用预置模式时，您可以通过事件轮询器的最大数量来控制最大并发性。

有关配置预置模式的更多信息，请参阅[创建和配置 Amazon SQS 事件源映射](services-sqs-configure.md)。

## 示例标准队列消息事件
<a name="example-standard-queue-message-event"></a>

**Example Amazon SQS 消息事件（标准队列）**  

```
{
    "Records": [
        {
            "messageId": "059f36b4-87a3-44ab-83d2-661975830a7d",
            "receiptHandle": "AQEBwJnKyrHigUMZj6rYigCgxlaS3SLy0a...",
            "body": "Test message.",
            "attributes": {
                "ApproximateReceiveCount": "1",
                "SentTimestamp": "1545082649183",
                "SenderId": "AIDAIENQZJOLO23YVJ4VO",
                "ApproximateFirstReceiveTimestamp": "1545082649185"
            },
            "messageAttributes": {
                "myAttribute": {
                    "stringValue": "myValue", 
                    "stringListValues": [], 
                    "binaryListValues": [], 
                    "dataType": "String"
                }
            },
            "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3",
            "eventSource": "aws:sqs",
            "eventSourceARN": "arn:aws:sqs:us-east-2:123456789012:my-queue",
            "awsRegion": "us-east-2"
        },
        {
            "messageId": "2e1424d4-f796-459a-8184-9c92662be6da",
            "receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...",
            "body": "Test message.",
            "attributes": {
                "ApproximateReceiveCount": "1",
                "SentTimestamp": "1545082650636",
                "SenderId": "AIDAIENQZJOLO23YVJ4VO",
                "ApproximateFirstReceiveTimestamp": "1545082650649"
            },
            "messageAttributes": {},
            "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3",
            "eventSource": "aws:sqs",
            "eventSourceARN": "arn:aws:sqs:us-east-2:123456789012:my-queue",
            "awsRegion": "us-east-2"
        }
    ]
}
```

默认情况下，Lambda 将一次性轮询队列中最多 10 条消息，并将该批次发送到函数。为避免在记录数量较少的情况下调用该函数，您可以配置批次时段，将事件源配置为缓冲最多五分钟的记录。在调用函数之前，Lambda 将继续轮询标准队列中的消息，直到批次时段到期、达到[调用有效负载大小配额](gettingstarted-limits.md)或达到配置的最大批次大小为止。

如果您使用的是批处理窗口，并且 SQS 队列包含的流量非常低，Lambda 可能会等待最多 20 秒钟才能调用您的函数。即使您将批处理窗口设置为低于 20 秒，情况依然如此。

**注意**  
在 Java 中，反序列化 JSON 时可能会遇到空指针错误。这可能要归因于 JSON 对象映射器转换“Records”和“eventSourceARN”大小写的方式。

## 示例 FIFO 队列消息事件
<a name="sample-fifo-queues-message-event"></a>

对于 FIFO 队列，记录包含与重复数据消除和顺序相关的其他属性。

**Example Amazon SQS 消息事件（FIFO 队列）**  

```
{
    "Records": [
        {
            "messageId": "11d6ee51-4cc7-4302-9e22-7cd8afdaadf5",
            "receiptHandle": "AQEBBX8nesZEXmkhsmZeyIE8iQAMig7qw...",
            "body": "Test message.",
            "attributes": {
                "ApproximateReceiveCount": "1",
                "SentTimestamp": "1573251510774",
                "SequenceNumber": "18849496460467696128",
                "MessageGroupId": "1",
                "SenderId": "AIDAIO23YVJENQZJOL4VO",
                "MessageDeduplicationId": "1",
                "ApproximateFirstReceiveTimestamp": "1573251510774"
            },
            "messageAttributes": {},
            "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3",
            "eventSource": "aws:sqs",
            "eventSourceARN": "arn:aws:sqs:us-east-2:123456789012:fifo.fifo",
            "awsRegion": "us-east-2"
        }
    ]
}
```

# 创建和配置 Amazon SQS 事件源映射
<a name="services-sqs-configure"></a>

要使用 Lambda 处理 Amazon SQS 消息，请使用适当的设置配置您的队列，然后创建 Lambda 事件源映射。

**Topics**
+ [

## 配置队列以便用于 Lambda
](#events-sqs-queueconfig)
+ [

## 设置 Lambda 执行角色权限
](#events-sqs-permissions)
+ [

## 创建 SQS 事件源映射
](#events-sqs-eventsource)

## 配置队列以便用于 Lambda
<a name="events-sqs-queueconfig"></a>

如果您没有现有的 Amazon SQS 队列，则[创建一个](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-configure-create-queue.html)队列，用作 Lambda 函数的事件源。Lambda 函数和 Amazon SQS 队列必须位于同一 AWS 区域，即便二者可能位于[不同的 AWS 账户](with-sqs-cross-account-example.md)。

为使函数有时间处理每批记录，请将源队列的[可见性超时](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-visibility-timeout.html)设置为函数[配置超时](configuration-timeout.md)的至少六倍。这一额外的时间允许 Lambda 在您的函数处理之前的批次期间遇到限流时进行重试。

**注意**  
函数超时必须小于或等于于队列的可见性超时。当创建或更新事件源映射时，Lambda 会验证此要求，函数超时超过队列的可见性超时则会返回错误。

默认情况下，如果 Lambda 在处理某个批次期间的任何时候遇到错误，则该批次中的所有消息都会返回到队列。[可见性超时](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-visibility-timeout.html)后，Lambda 将再次看到这些消息。您可以将事件源映射配置为使用[部分批次响应](services-sqs-errorhandling.md#services-sqs-batchfailurereporting)，以仅使失败的消息返回队列。此外，如果函数多次都未能处理某条消息，则 Amazon SQS 可以将其发送到某个[死信队列](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-dead-letter-queues.html)。建议将源队列的[重新驱动策略](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-dead-letter-queues.html#policies-for-dead-letter-queues)的 `maxReceiveCount` 设置为至少 5。这让 Lambda 在将失败的消息直接发送到死信队列之前有几次重试的机会。

## 设置 Lambda 执行角色权限
<a name="events-sqs-permissions"></a>

[AWSLambdaSQSQueueExecutionRole](https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AWSLambdaSQSQueueExecutionRole.html) AWS 托管策略包含 Lambda 从您的 Amazon SQS 队列中读取所需的权限。您可以[将此托管策略添加](lambda-intro-execution-role.md)到您的函数的执行角色。

或者，如果您使用的是加密队列，则还需要为执行角色添加以下权限：
+ [kms:Decrypt](https://docs.aws.amazon.com/kms/latest/APIReference/API_Decrypt.html)

## 创建 SQS 事件源映射
<a name="events-sqs-eventsource"></a>

创建事件源映射以指示 Lambda 将队列中的项目发送到 Lambda 函数。您可以创建多个事件源映射，以使用单个函数处理来自多个队列的项目。当 Lambda 调用目标函数时，事件可以包含多个项目（最多为可配置的最大*批处理大小*）。

要将您的函数配置为从 Amazon SQS 中读取，请将 [AWSLambdaSQSQueueExecutionRole](https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AWSLambdaSQSQueueExecutionRole.html) AWS 托管策略附加到您的执行角色。然后，使用以下步骤从控制台创建 **SQS** 事件源映射。

**要添加权限并创建触发器**

1. 打开 Lamba 控制台的[函数](https://console.aws.amazon.com/lambda/home#/functions)页面。

1. 选择一个函数的名称。

1. 选择 **Configuration**（配置）选项卡，然后选择 **Permissions**（权限）。

1. 在**角色名称**下，选择至执行角色的链接。此角色将在 IAM 控制台中打开角色。  
![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/execution-role.png)

1. 选择**添加权限**，然后选择**附加策略**。  
![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/attach-policies.png)

1. 在搜索字段中输入 `AWSLambdaSQSQueueExecutionRole`。向执行角色添加此策略。这是一项 AWS 托管策略，其中包含您的函数从 Amazon SQS 队列中读取所需的权限。有关此策略的更多信息，请参阅《AWS Managed Policy Reference》**中的 [AWSLambdaSQSQueueExecutionRole](https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AWSLambdaSQSQueueExecutionRole.html)。

1. 在 Lambda 控制台中返回您的函数。在 **Function overview**（函数概览）下，选择 **Add trigger**（添加触发器）。  
![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/add-trigger.png)

1. 选择触发器类型。

1. 配置必填选项，然后选择 **Add**（添加）。

Lambda 支持 Amazon SQS 事件源的以下配置选项：

**SQS 队列**  
要从中读取记录的 Amazon SQS 队列。Lambda 函数和 Amazon SQS 队列必须位于同一 AWS 区域，即便二者可能位于[不同的 AWS 账户](with-sqs-cross-account-example.md)。

**启用触发器**  
事件源映射的状态。**Enable trigger**（启用触发器）默认处于选中状态。

**批次大小**  
每个批次中要发送给函数的最大记录数。对于标准队列，这最高可为 10,000 条记录。对于 FIFO 队列，最大值为 10。对于超过 10 的批处理大小，还必须将批处理时间（`MaximumBatchingWindowInSeconds`）设置为至少 1 秒。  
配置[函数超时](https://serverlessland.com/content/service/lambda/guides/aws-lambda-operator-guide/configurations#timeouts)，以允许有足够的时间来处理整个批次的项目。如果项目处理需要很长时间，请选择一个较小的批处理大小。大批量处理可以提高非常快速或拥有大量开销的工作负载的效率。如果您在函数上配置了[预留并发](configuration-concurrency.md)，请将最小并发执行数设置为 5，以降低在 Lambda 调用函数时出现节流错误的几率。  
Lambda 通过单个调用将批次中的所有记录传递给函数，前提是事件的总大小未超出同步调用的[调用有效负载大小配额](gettingstarted-limits.md)（6 MB）。Lambda 和 Amazon SQS 都会为每条记录生成元数据。这一额外的元数据将会计入总有效负载大小，并且可能导致批处理中发送的记录总数低于配置的批处理大小。Amazon SQS 发送的元数据字段的长度是可变的。有关 Amazon SQS 元数据字段的更多信息，请参阅*《Amazon Simple Queue Service API 参考》*中的 [ReceiveMessage](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_ReceiveMessage.html) API 操作文档。

**Batch 时间**  
在调用函数之前收集记录的最长时间（以秒为单位）。它仅适用于标准队列。  
如果您使用的批次时段大于 0 秒，则必须考虑队列[可见性超时](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-visibility-timeout.html)中增加的处理时间。我们建议将队列可见性超时设置为[函数超时](configuration-timeout.md)的六倍，加上 `MaximumBatchingWindowInSeconds` 的值。这使 Lambda 函数有时间处理每个批次的事件，并在出现节流错误时重试。  
当消息可用时，Lambda 开始批量处理消息。Lambda 通过五次并发调用您的函数开始一次处理五个批处理。如果仍有消息可用，则 Lambda 最多每分钟添加 300 次并发函数调用，最多为 1250 次并发调用。使用预置模式时，每个事件轮询器能处理最高 1 MB/s 的吞吐量、最多 10 次并发调用，或者每秒最多进行 10 次 Amazon SQS 轮询 API 调用。Lambda 会根据您所配置的最小值和最大值来扩展事件轮询器的数量，能够迅速将并发调用数量提升至最多每分钟 1000 次，从而实现对 Amazon SQS 事件的低延迟处理。您可以通过这些最小和最大事件轮询器数量设置来控制扩展和并发性。要了解函数扩展和并发的更多信息，请参阅 [了解 Lambda 函数扩展](lambda-concurrency.md)。  
要处理更多消息，您可以优化 Lambda 函数以提高吞吐量。有关更多信息，请参阅[了解 AWS Lambda 如何使用 Amazon SQS 标准队列进行扩展](https://aws.amazon.com/blogs/compute/understanding-how-aws-lambda-scales-when-subscribed-to-amazon-sqs-queues/#:~:text=If there are more messages,messages from the SQS queue.)。

**筛选条件**  
添加筛选条件以控制 Lambda 将哪些事件发送给函数进行处理。有关更多信息，请参阅 [控制 Lambda 向您的函数发送的事件](invocation-eventfiltering.md)。

**最大并发数**  
事件源可调用的最大并发函数数量。不能与已启用的预置模式一起使用。有关更多信息，请参阅 [为 Amazon SQS 事件源配置最大并发](services-sqs-scaling.md#events-sqs-max-concurrency)。

**预置模式**  
启用后，将为您的事件源映射分配专用的轮询资源。您可以配置事件轮询器的最小数量（2-200）和最大数量（2-2000）。每个事件轮询器能处理最高 1 MB/秒 的吞吐量、最多 10 次并发调用，或者每秒最多进行 10 次 10 Amazon SQS 轮询调用。  
注意：您不能同时使用预置模式和最大并发性这两种配置。启用预置模式后，使用最大轮询器数量设置来控制并发性。

# 为 SQS 事件源映射配置扩展行为
<a name="services-sqs-scaling"></a>

您可以通过最大并发数设置或者启用预置模式来控制您的 Amazon SQS 事件源映射的扩展行为。它们是互斥选项。

默认情况下，Lambda 会根据消息量自动扩展事件轮询器。启用预置模式时，您可以分配最小和最大数量的专用轮询资源，这些资源随时可以处理预期的流量模式。这使您可以通过两种方式优化事件源映射的性能：
+ 标准模式（默认）：Lambda 会自动管理扩展，从少量轮询器开始，然后再根据工作负载纵向扩展或缩减。
+ 预置模式：您可以通过设置最小和最大限制配置专用轮询资源，从而使扩展速度加快 3 倍，处理容量最多提高 16 倍。

对于标准队列，Lambda 使用[长轮询](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-short-and-long-polling.html#sqs-long-polling)来轮询一个队列，直到它变为活动状态。当消息可用时，Lambda 会通过函数的五次并发调用开始一次处理五个批次。如果仍有消息可用，则 Lambda 增加批量读取的进程数，最多每分钟增加 300 次并发调用。事件源映射可以同时处理的最大调用数量为 1250。当流量较低时，Lambda 会将处理规模缩减为 5 个并发调用，并且可优化为少至 2 个并发调用，以减少 Amazon SQS 调用和相应的成本。但是，当您启用最大并发设置时，此优化不可用。

对于 FIFO 队列，Lambda 按照接收消息的顺序向函数发送消息。向 FIFO 队列发送消息时，需要指定[消息组 ID](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/using-messagegroupid-property.html)。Amazon SQS 确保同一组中的消息按顺序传递到 Lambda。当 Lambda 按批次读取消息时，每个批次可能包含来自多个消息组的消息，但消息的顺序保持不变。如果函数返回错误，函数会对受影响的消息尝试所有重试，然后 Lambda 才会接收来自同一个组的其他消息。

使用预置模式时，每个事件轮询器能处理最高 1 MB/秒的吞吐量、最多 10 次并发调用，或者每秒最多进行 10 次 Amazon SQS 轮询 API 调用。Lambda 会根据您所配置的最小值和最大值来扩展事件轮询器的数量，能够迅速将并发次数提升至最多每分钟 1000 次，从而实现对 Amazon SQS 事件的一致性低延迟处理。使用预调配模式会产生额外成本。有关详细定价，请参阅 [AWS Lambda 定价](https://aws.amazon.com/lambda/pricing/)。每个事件轮询器对您的 SQS 队列使用[长轮询](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-short-and-long-polling.html)，每秒最多进行 10 次轮询，这会产生 SQS API 的请求费用。有关详细信息，请参阅 [Amazon SQS 定价](https://aws.amazon.com/sqs/pricing/ )。您可以通过这些最小和最大事件轮询器设置来控制扩展和并发情况，而非使用最大并发设置，因为这两种选项不能同时使用。

**注意**  
您不能同时使用最大并发设置和预置模式。启用预置模式后，您可以通过事件轮询器的最小和最大数量来控制 Amazon SQS 事件源映射的扩展和并发性。

## 为 Amazon SQS 事件源配置最大并发
<a name="events-sqs-max-concurrency"></a>

您可以使用最大并发设置来控制 SQS 事件源的扩展行为。请注意，启用预置模式后无法使用最大并发数。最大并发设置限制了 Amazon SQS 事件源可以调用的函数的并发实例数。最大并发属于事件源级别的设置。如果您将多个 Amazon SQS 事件源映射到一个函数，则每个事件源均可进行单独的最大并发设置。您可以使用最大并发，从而防止某个队列使用函数的所有[预留并发](configuration-concurrency.md)或[账户的其余并发限额](gettingstarted-limits.md)。在 Amazon SQS 事件源上配置最大并发不收取任何费用。

重要的是，最大并发和预留并发为两个独立的设置。不要将最大并发设置为高于函数的预留并发。配置最大并发后，请确保您的函数的预留并发大于或等于该函数上所有 Amazon SQS 事件源的最大总并发数。否则，Lambda 可能会限制您的消息。

当您账户的并发配额设置为默认值 1000 时，Amazon SQS 事件源映射可以扩展为调用最高此值的函数实例，除非您指定最大并发量。

如果您账户的默认并发配额有所增加，Lambda 可能无法调用最高达到新配额的并发函数实例。默认情况下，Lambda 可以扩展为针对 Amazon SQS 事件源映射调用最高 1250 个并发函数实例。如果这不足以满足您的应用场景，请联系 AWS 支持人员，讨论如何提高您账户的 Amazon SQS 事件源映射并发度。

**注意**  
对于 FIFO 队列，并发调用的上限为[消息组 ID](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/using-messagegroupid-property.html) 的数量 (`messageGroupId`) 或最大并发设置，以较低者为准。例如，如果您有六个消息组 ID 并且最大并发设置为 10，则函数最多可以进行六次并发调用。

您可以在新的和现有的 Amazon SQS 事件源映射上配置最大并发。

**使用 Lambda 控制台配置最大并发**

1. 打开 Lamba 控制台的[函数页面](https://console.aws.amazon.com/lambda/home#/functions)。

1. 选择一个函数的名称。

1. 在 **Function overview**（函数概览）下，选择 **SQS**。此操作将打开 **Configuration**（配置）选项卡。

1. 选择 Amazon SQS 触发器，然后选择 **Edit**（编辑）。

1. 对于 **Maximum concurrency**（最大并发），输入 2 到 1,000 之间的数字。要关闭最大并发，将该框保留为空。

1. 选择**保存**。

**使用 AWS Command Line Interface（AWS CLI）配置最大并发**  
使用带 `--scaling-config` 选项的 [update-event-source-mapping](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/update-event-source-mapping.html) 命令。示例：

```
aws lambda update-event-source-mapping \
    --uuid "a1b2c3d4-5678-90ab-cdef-11111EXAMPLE" \
    --scaling-config '{"MaximumConcurrency":5}'
```

要关闭最大并发，请为 `--scaling-config` 输入一个空值：

```
aws lambda update-event-source-mapping \
    --uuid "a1b2c3d4-5678-90ab-cdef-11111EXAMPLE" \
    --scaling-config "{}"
```

**使用 Lambda API 配置最大并发**  
将 [CreateEventSourceMapping](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html) 或 [UpdateEventSourceMapping](https://docs.aws.amazon.com/lambda/latest/api/API_UpdateEventSourceMapping.html) 操作与 [ScalingConfig](https://docs.aws.amazon.com/lambda/latest/api/API_ScalingConfig.html) 对象结合使用。

# 在 Lambda 中处理 SQS 事件源错误
<a name="services-sqs-errorhandling"></a>

为了处理与 SQS 事件源相关的错误，Lambda 会自动使用带有回退策略的重试策略。您还可以通过配置 SQS 事件源映射来返回[部分批次响应](#services-sqs-batchfailurereporting)，从而自定义错误处理行为。

## 失败调用的回退策略
<a name="services-sqs-backoff-strategy"></a>

如果调用失败，Lambda 会在实施回退策略时尝试重试调用。回退策略略有不同，具体取决于 Lambda 的故障原因是函数代码中的错误还是节流所致。
+  如果您的**函数代码**导致了该错误，Lambda 将停止处理并重试调用。同时，Lambda 会逐渐退出，以减少分配给 Amazon SQS 事件源映射的并发量。队列的可见性超时结束后，消息将再次显示在队列中。
+ 如果调用失败是**节流**造成的，Lambda 会通过减少分配给 Amazon SQS 事件源映射的并发量来逐渐停止重试。Lambda 会继续重试该消息，直到消息的时间戳超过队列的可见性超时，此时 Lambda 会删除该消息。

## 实施部分批处理响应
<a name="services-sqs-batchfailurereporting"></a>

预设情况下,如果您的 Lambda 函数在处理某个批处理时遇到错误，则该批处理中的所有消息都会在队列中重新可见，包括 Lambda 已经成功处理的消息。因此，您的函数最终可能会多次处理同一消息。

对于处理失败的批处理，要避免重新处理其中已经成功处理的消息，您可以将事件源映射配置为仅使失败的消息重新可见。这称为部分批处理响应。要开启部分批处理响应，请在配置事件源映射时为 [FunctionResponseTypes](https://docs.aws.amazon.com/lambda/latest/api/API_UpdateEventSourceMapping.html#lambda-UpdateEventSourceMapping-request-FunctionResponseTypes) 操作指定 `ReportBatchItemFailures`。这可以让您的函数返回部分成功，从而有助于减少对记录进行不必要的重试次数。

**注意**  
Powertools for AWS Lambda 中的[批处理实用程序](https://docs.powertools.aws.dev/lambda/python/latest/utilities/batch/)会自动处理所有部分批处理响应逻辑。此实用程序简化了批处理模式的实现，并减少了正确处理批处理项目失败所需的自定义代码。它适用于 Python、Java、Typescript 和 .NET。

激活 `ReportBatchItemFailures` 后，当函数调用失败时，Lambda 不会 [缩减消息轮询范围](#services-sqs-backoff-strategy)。如果您预计某些消息会失败，并且不希望这些失败影响消息处理速率，请使用 `ReportBatchItemFailures`。

**注意**  
在使用部分批处理响应时，请记住以下几点：  
如果您函数发现了一个异常，则整个批处理将被视为完全失败。
如果您将此功能与 FIFO 队列结合使用，则您的函数应在第一次失败后停止处理消息，并返回 `batchItemFailures` 中的所有失败和未处理的消息。这有助于保持队列中消息的顺序。

**激活部分批处理报告**

1. 查看 [实施部分批处理响应的最佳实践](https://docs.aws.amazon.com/prescriptive-guidance/latest/lambda-event-filtering-partial-batch-responses-for-sqs/best-practices-partial-batch-responses.html)。

1. 运行以下命令来为您的函数激活 `ReportBatchItemFailures`。要检索事件源映射的 UUID，请运行 [list-event-source-mappings](https://docs.aws.amazon.com/cli/latest/reference/lambda/list-event-source-mappings.html) AWS CLI 命令。

   ```
   aws lambda update-event-source-mapping \
   --uuid "a1b2c3d4-5678-90ab-cdef-11111EXAMPLE" \
   --function-response-types "ReportBatchItemFailures"
   ```

1. 更新您的函数代码以捕获所有异常并在 `batchItemFailures` JSON 响应中返回处理失败的消息。`batchItemFailures` 响应必须包含消息 ID 列表，以作为 `itemIdentifier` JSON 值。

   例如，假设一个批处理有五条消息，消息 ID 分别为 `id1`、`id2`、`id3`、`id4` 和 `id5`。您的函数成功处理了 `id1`、`id3` 和 `id5`。要使消息 `id2` 和 `id4` 在队列中重新可见，您的函数应运行以下响应：

   ```
   { 
     "batchItemFailures": [ 
           {
               "itemIdentifier": "id2"
           },
           {
               "itemIdentifier": "id4"
           }
       ]
   }
   ```

   以下函数代码示例将返回批处理中处理失败消息的 ID 列表：

------
#### [ .NET ]

**适用于 .NET 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/lambda-function-sqs-report-batch-item-failures)存储库中查找完整示例，并了解如何进行设置和运行。
报告使用 .NET 进行 Lambda SQS 批处理项目失败。  

   ```
   // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
   // SPDX-License-Identifier: Apache-2.0
   using Amazon.Lambda.Core;
   using Amazon.Lambda.SQSEvents;
   
   // Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
   [assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]
   namespace sqsSample;
   
   public class Function
   {
       public async Task<SQSBatchResponse> FunctionHandler(SQSEvent evnt, ILambdaContext context)
       {
           List<SQSBatchResponse.BatchItemFailure> batchItemFailures = new List<SQSBatchResponse.BatchItemFailure>();
           foreach(var message in evnt.Records)
           {
               try
               {
                   //process your message
                   await ProcessMessageAsync(message, context);
               }
               catch (System.Exception)
               {
                   //Add failed message identifier to the batchItemFailures list
                   batchItemFailures.Add(new SQSBatchResponse.BatchItemFailure{ItemIdentifier=message.MessageId}); 
               }
           }
           return new SQSBatchResponse(batchItemFailures);
       }
   
       private async Task ProcessMessageAsync(SQSEvent.SQSMessage message, ILambdaContext context)
       {
           if (String.IsNullOrEmpty(message.Body))
           {
               throw new Exception("No Body in SQS Message.");
           }
           context.Logger.LogInformation($"Processed message {message.Body}");
           // TODO: Do interesting work based on the new message
           await Task.CompletedTask;
       }
   }
   ```

------
#### [ Go ]

**适用于 Go 的 SDK V2**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/lambda-function-sqs-report-batch-item-failures)存储库中查找完整示例，并了解如何进行设置和运行。
报告使用 Go 进行 Lambda SQS 批处理项目失败。  

   ```
   // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
   // SPDX-License-Identifier: Apache-2.0
   package main
   
   import (
   	"context"
   	"fmt"
   	"github.com/aws/aws-lambda-go/events"
   	"github.com/aws/aws-lambda-go/lambda"
   )
   
   func handler(ctx context.Context, sqsEvent events.SQSEvent) (map[string]interface{}, error) {
   	batchItemFailures := []map[string]interface{}{}
   
   	for _, message := range sqsEvent.Records {
   		if len(message.Body) > 0 {
   			// Your message processing condition here
   			fmt.Printf("Successfully processed message: %s\n", message.Body)
   		} else {
   			// Message processing failed
   			fmt.Printf("Failed to process message %s\n", message.MessageId)
   			batchItemFailures = append(batchItemFailures, map[string]interface{}{"itemIdentifier": message.MessageId})
   		}
   	}
   
   	sqsBatchResponse := map[string]interface{}{
   		"batchItemFailures": batchItemFailures,
   	}
   	return sqsBatchResponse, nil
   }
   
   func main() {
   	lambda.Start(handler)
   }
   ```

------
#### [ Java ]

**适用于 Java 的 SDK 2.x**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/lambda-function-sqs-report-batch-item-failures)存储库中查找完整示例，并了解如何进行设置和运行。
报告使用 Java 进行 Lambda SQS 批处理项目失败。  

   ```
   // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
   // SPDX-License-Identifier: Apache-2.0
   import com.amazonaws.services.lambda.runtime.Context;
   import com.amazonaws.services.lambda.runtime.RequestHandler;
   import com.amazonaws.services.lambda.runtime.events.SQSEvent;
   import com.amazonaws.services.lambda.runtime.events.SQSBatchResponse;
    
   import java.util.ArrayList;
   import java.util.List;
    
   public class ProcessSQSMessageBatch implements RequestHandler<SQSEvent, SQSBatchResponse> {
       @Override
       public SQSBatchResponse handleRequest(SQSEvent sqsEvent, Context context) {
            List<SQSBatchResponse.BatchItemFailure> batchItemFailures = new ArrayList<SQSBatchResponse.BatchItemFailure>();
   
            for (SQSEvent.SQSMessage message : sqsEvent.getRecords()) {
                try {
                    //process your message
                } catch (Exception e) {
                    //Add failed message identifier to the batchItemFailures list
                    batchItemFailures.add(new SQSBatchResponse.BatchItemFailure(message.getMessageId()));
                }
            }
            return new SQSBatchResponse(batchItemFailures);
        }
   }
   ```

------
#### [ JavaScript ]

**SDK for JavaScript（v3）**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/lambda-function-sqs-report-batch-item-failures)存储库中查找完整示例，并了解如何进行设置和运行。
报告使用 JavaScript 进行 Lambda SQS 批处理项目失败。  

   ```
   // Node.js 20.x Lambda runtime, AWS SDK for Javascript V3
   export const handler = async (event, context) => {
       const batchItemFailures = [];
       for (const record of event.Records) {
           try {
               await processMessageAsync(record, context);
           } catch (error) {
               batchItemFailures.push({ itemIdentifier: record.messageId });
           }
       }
       return { batchItemFailures };
   };
   
   async function processMessageAsync(record, context) {
       if (record.body && record.body.includes("error")) {
           throw new Error("There is an error in the SQS Message.");
       }
       console.log(`Processed message: ${record.body}`);
   }
   ```
报告使用 TypeScript 进行 Lambda SQS 批处理项目失败。  

   ```
   // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
   // SPDX-License-Identifier: Apache-2.0
   import { SQSEvent, SQSBatchResponse, Context, SQSBatchItemFailure, SQSRecord } from 'aws-lambda';
   
   export const handler = async (event: SQSEvent, context: Context): Promise<SQSBatchResponse> => {
       const batchItemFailures: SQSBatchItemFailure[] = [];
   
       for (const record of event.Records) {
           try {
               await processMessageAsync(record);
           } catch (error) {
               batchItemFailures.push({ itemIdentifier: record.messageId });
           }
       }
   
       return {batchItemFailures: batchItemFailures};
   };
   
   async function processMessageAsync(record: SQSRecord): Promise<void> {
       if (record.body && record.body.includes("error")) {
           throw new Error('There is an error in the SQS Message.');
       }
       console.log(`Processed message ${record.body}`);
   }
   ```

------
#### [ PHP ]

**适用于 PHP 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/lambda-function-sqs-report-batch-item-failures)存储库中查找完整示例，并了解如何进行设置和运行。
报告使用 PHP 进行 Lambda SQS 批处理项目失败。  

   ```
   // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
   // SPDX-License-Identifier: Apache-2.0
   <?php
   
   use Bref\Context\Context;
   use Bref\Event\Sqs\SqsEvent;
   use Bref\Event\Sqs\SqsHandler;
   use Bref\Logger\StderrLogger;
   
   require __DIR__ . '/vendor/autoload.php';
   
   class Handler extends SqsHandler
   {
       private StderrLogger $logger;
       public function __construct(StderrLogger $logger)
       {
           $this->logger = $logger;
       }
   
       /**
        * @throws JsonException
        * @throws \Bref\Event\InvalidLambdaEvent
        */
       public function handleSqs(SqsEvent $event, Context $context): void
       {
           $this->logger->info("Processing SQS records");
           $records = $event->getRecords();
   
           foreach ($records as $record) {
               try {
                   // Assuming the SQS message is in JSON format
                   $message = json_decode($record->getBody(), true);
                   $this->logger->info(json_encode($message));
                   // TODO: Implement your custom processing logic here
               } catch (Exception $e) {
                   $this->logger->error($e->getMessage());
                   // failed processing the record
                   $this->markAsFailed($record);
               }
           }
           $totalRecords = count($records);
           $this->logger->info("Successfully processed $totalRecords SQS records");
       }
   }
   
   $logger = new StderrLogger();
   return new Handler($logger);
   ```

------
#### [ Python ]

**适用于 Python 的 SDK（Boto3）**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/lambda-function-sqs-report-batch-item-failures)存储库中查找完整示例，并了解如何进行设置和运行。
报告使用 Python 进行 Lambda SQS 批处理项目失败。  

   ```
   # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
   # SPDX-License-Identifier: Apache-2.0
   
   def lambda_handler(event, context):
       if event:
           batch_item_failures = []
           sqs_batch_response = {}
        
           for record in event["Records"]:
               try:
                   print(f"Processed message: {record['body']}")
               except Exception as e:
                   batch_item_failures.append({"itemIdentifier": record['messageId']})
           
           sqs_batch_response["batchItemFailures"] = batch_item_failures
           return sqs_batch_response
   ```

------
#### [ Ruby ]

**适用于 Ruby 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-sqs-to-lambda-with-batch-item-handling)存储库中查找完整示例，并了解如何进行设置和运行。
报告使用 Ruby 进行 Lambda SQS 批处理项目失败。  

   ```
   # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
   # SPDX-License-Identifier: Apache-2.0
   require 'json'
   
   def lambda_handler(event:, context:)
     if event
       batch_item_failures = []
       sqs_batch_response = {}
   
       event["Records"].each do |record|
         begin
           # process message
         rescue StandardError => e
           batch_item_failures << {"itemIdentifier" => record['messageId']}
         end
       end
   
       sqs_batch_response["batchItemFailures"] = batch_item_failures
       return sqs_batch_response
     end
   end
   ```

------
#### [ Rust ]

**适用于 Rust 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/lambda-function-sqs-report-batch-item-failures)存储库中查找完整示例，并了解如何进行设置和运行。
报告使用 Rust 进行 Lambda SQS 批处理项目失败。  

   ```
   // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
   // SPDX-License-Identifier: Apache-2.0
   use aws_lambda_events::{
       event::sqs::{SqsBatchResponse, SqsEvent},
       sqs::{BatchItemFailure, SqsMessage},
   };
   use lambda_runtime::{run, service_fn, Error, LambdaEvent};
   
   async fn process_record(_: &SqsMessage) -> Result<(), Error> {
       Err(Error::from("Error processing message"))
   }
   
   async fn function_handler(event: LambdaEvent<SqsEvent>) -> Result<SqsBatchResponse, Error> {
       let mut batch_item_failures = Vec::new();
       for record in event.payload.records {
           match process_record(&record).await {
               Ok(_) => (),
               Err(_) => batch_item_failures.push(BatchItemFailure {
                   item_identifier: record.message_id.unwrap(),
               }),
           }
       }
   
       Ok(SqsBatchResponse {
           batch_item_failures,
       })
   }
   
   #[tokio::main]
   async fn main() -> Result<(), Error> {
       run(service_fn(function_handler)).await
   }
   ```

------

如果处理失败的事件没有返回到队列，请参阅 AWS 知识中心中的 [如何排查 Lambda 函数 SQS ReportBatchItemFailures 问题？](https://aws.amazon.com/premiumsupport/knowledge-center/lambda-sqs-report-batch-item-failures/)。

### 成功和失败的条件
<a name="sqs-batchfailurereporting-conditions"></a>

如果您的函数返回以下任意一项，则 Lambda 会将批处理视为完全成功：
+ 空的 `batchItemFailures` 列表
+ Null `batchItemFailures` 列表
+ 空的 `EventResponse`
+ Null `EventResponse`

如果您的函数返回以下任意一项，则 Lambda 会将批处理视为完全失败：
+ JSON 响应无效
+ 空字符串 `itemIdentifier`
+ Null `itemIdentifier`
+ 包含错误密钥名的 `itemIdentifier`
+ 具有某个消息 ID 的 `itemIdentifier` 值不存在

### CloudWatch 指标
<a name="sqs-batchfailurereporting-metrics"></a>

要确定函数是否在正确报告批处理项目失败情况，您可以监控 Amazon CloudWatch 中的 `NumberOfMessagesDeleted` 和 `ApproximateAgeOfOldestMessage` Amazon SQS 指标。
+ `NumberOfMessagesDeleted` 会跟踪从队列中删除的消息数量。如果该值降至 0，则表明您的函数响应没有正确返回失败的消息。
+ `ApproximateAgeOfOldestMessage` 会跟踪最早的消息在队列中停留的时间。如果此指标急剧增加，则可能表明您的函数没有正确返回失败的消息。

### 使用 Powertools for AWS Lambda 批处理器
<a name="services-sqs-batchfailurereporting-powertools"></a>

Powertools for AWS Lambda 中的批处理器实用程序会自动处理部分批处理响应逻辑，从而降低实施批处理故障报告的复杂性。下面是使用批处理器的示例：

**Python**  
有关完整的示例和设置说明，请参阅[批处理器文档](https://docs.powertools.aws.dev/lambda/python/latest/utilities/batch/)。
使用 AWS Lambda 批处理器处理 Amazon SQS 消息。  

```
import json
from aws_lambda_powertools import Logger
from aws_lambda_powertools.utilities.batch import BatchProcessor, EventType, process_partial_response
from aws_lambda_powertools.utilities.data_classes import SQSEvent
from aws_lambda_powertools.utilities.typing import LambdaContext

processor = BatchProcessor(event_type=EventType.SQS)
logger = Logger()

def record_handler(record):
    logger.info(record)
    # Your business logic here
    # Raise an exception to mark this record as failed
    
def lambda_handler(event, context: LambdaContext):
    return process_partial_response(
        event=event, 
        record_handler=record_handler, 
        processor=processor,
        context=context
    )
```

**TypeScript**  
有关完整的示例和设置说明，请参阅[批处理器文档](https://docs.aws.amazon.com/powertools/typescript/latest/features/batch/)。
使用 AWS Lambda 批处理器处理 Amazon SQS 消息。  

```
import { BatchProcessor, EventType, processPartialResponse } from '@aws-lambda-powertools/batch';
import { Logger } from '@aws-lambda-powertools/logger';
import type { SQSEvent, Context } from 'aws-lambda';

const processor = new BatchProcessor(EventType.SQS);
const logger = new Logger();

const recordHandler = async (record: any): Promise<void> => {
    logger.info('Processing record', { record });
    // Your business logic here
    // Throw an error to mark this record as failed
};

export const handler = async (event: SQSEvent, context: Context) => {
    return processPartialResponse(event, recordHandler, processor, {
        context,
    });
};
```

# Amazon SQS 事件源映射的 Lambda 参数
<a name="services-sqs-parameters"></a>

所有 Lambda 事件源类型共享相同的 [CreateEventSourceMapping](https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html) 和 [UpdateEventSourceMapping](https://docs.aws.amazon.com/lambda/latest/api/API_UpdateEventSourceMapping.html) API 操作。但是，只有部分参数适用于 Amazon SQS。


| 参数 | 必需 | 默认值 | 备注 | 
| --- | --- | --- | --- | 
|  BatchSize  |  N  |  10  |  对于标准队列，最大值为 10000。对于 FIFO 队列，最大值为 10。  | 
|  启用  |  N  |  真实  | none  | 
|  EventSourceArn  |  Y  | 不适用 |  数据流或流使用者的 ARN  | 
|  FunctionName  |  是  | 不适用  | none  | 
|  FilterCriteria  |  N  |  不适用   |  [控制 Lambda 向您的函数发送的事件](invocation-eventfiltering.md)  | 
|  FunctionResponseTypes  |  N  | 不适用  |  要使您的函数报告某个批处理中的特定失败，请在 `FunctionResponseTypes` 中包含值 `ReportBatchItemFailures`。有关更多信息，请参阅 [实施部分批处理响应](services-sqs-errorhandling.md#services-sqs-batchfailurereporting)。  | 
|  MaximumBatchingWindowInSeconds  |  N  |  0  | FIFO 队列不支持批处理窗口 | 
|  ProvisionedPollerConfig  |  N  |  不适用  |  为 SQS 事件源映射配置专用事件轮询器的最小数量（2-200）和最大数量（2-2000）。每个轮询器可以处理最高 1 MB/秒的吞吐量和 10 次并发调用。  | 
|  ScalingConfig  |  N  |  不适用   |  [为 Amazon SQS 事件源配置最大并发](services-sqs-scaling.md#events-sqs-max-concurrency)  | 

# 对 Amazon SQS 事件源使用事件筛选
<a name="with-sqs-filtering"></a>

您可以使用事件筛选，控制 Lambda 将流或队列中的哪些记录发送给函数。有关事件筛选工作原理的一般信息，请参阅 [控制 Lambda 向您的函数发送的事件](invocation-eventfiltering.md)。

本节重点介绍 Amazon SQS 事件源的事件筛选。

**注意**  
Amazon SQS 事件源映射仅支持对 `body` 键进行筛选。

**Topics**
+ [

## Amazon SQS 事件筛选基础知识
](#filtering-SQS)

## Amazon SQS 事件筛选基础知识
<a name="filtering-SQS"></a>

假设 Amazon SQS 队列包含以下 JSON 格式的消息。

```
{
    "RecordNumber": 1234,
    "TimeStamp": "yyyy-mm-ddThh:mm:ss",
    "RequestCode": "AAAA"
}
```

此队列的示例记录将如下所示。

```
{
    "messageId": "059f36b4-87a3-44ab-83d2-661975830a7d",
    "receiptHandle": "AQEBwJnKyrHigUMZj6rYigCgxlaS3SLy0a...",
    "body": "{\n "RecordNumber": 1234,\n "TimeStamp": "yyyy-mm-ddThh:mm:ss",\n "RequestCode": "AAAA"\n}",
    "attributes": {
        "ApproximateReceiveCount": "1",
        "SentTimestamp": "1545082649183",
        "SenderId": "AIDAIENQZJOLO23YVJ4VO",
        "ApproximateFirstReceiveTimestamp": "1545082649185"
        },
    "messageAttributes": {},
    "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3",
    "eventSource": "aws:sqs",
    "eventSourceARN": "arn:aws:sqs:us-west-2:123456789012:my-queue",
    "awsRegion": "us-west-2"
}
```

要根据 Amazon SQS 消息的内容进行筛选，请使用 Amazon SQS 消息记录中的 `body` 键。假设您只想处理 Amazon SQS 消息中 `RequestCode` 为“BBBB”的记录。`FilterCriteria` 对象将如下所示。

```
{
    "Filters": [
        {
            "Pattern": "{ \"body\" : { \"RequestCode\" : [ \"BBBB\" ] } }"
        }
    ]
}
```

为了更清楚起见，以下是在纯 JSON 中展开的筛选条件 `Pattern` 的值。

```
{
    "body": {
        "RequestCode": [ "BBBB" ]
        }
}
```

您可以使用控制台、AWS CLI 或 AWS SAM 模板添加筛选条件。

------
#### [ Console ]

要使用控制台添加此筛选条件，请按照 [将筛选条件附加到事件源映射（控制台）](invocation-eventfiltering.md#filtering-console) 中的说明，为**筛选条件**输入以下字符串。

```
{ "body" : { "RequestCode" : [ "BBBB" ] } }
```

------
#### [ AWS CLI ]

要使用 AWS Command Line Interface（AWS CLI）创建包含这些筛选条件的新事件源映射，请运行以下命令。

```
aws lambda create-event-source-mapping \
    --function-name my-function \
    --event-source-arn arn:aws:sqs:us-east-2:123456789012:my-queue \
    --filter-criteria '{"Filters": [{"Pattern": "{ \"body\" : { \"RequestCode\" : [ \"BBBB\" ] } }"}]}'
```

要将这些筛选条件添加到现有事件源映射中，请运行以下命令。

```
aws lambda update-event-source-mapping \
    --uuid "a1b2c3d4-5678-90ab-cdef-11111EXAMPLE" \
    --filter-criteria '{"Filters": [{"Pattern": "{ \"body\" : { \"RequestCode\" : [ \"BBBB\" ] } }"}]}'
```

------
#### [ AWS SAM ]

要使用 AWS SAM 添加此筛选条件，请将以下代码段添加到事件源的 YAML 模板中。

```
FilterCriteria:
  Filters:
    - Pattern: '{ "body" : { "RequestCode" : [ "BBBB" ] } }'
```

------

假设您希望函数只处理 `RecordNumber` 大于 9999 的记录。`FilterCriteria` 对象将如下所示。

```
{
    "Filters": [
        {
            "Pattern": "{ \"body\" : { \"RecordNumber\" : [ { \"numeric\": [ \">\", 9999 ] } ] } }"
        }
    ]
}
```

为了更清楚起见，以下是在纯 JSON 中展开的筛选条件 `Pattern` 的值。

```
{
    "body": {
        "RecordNumber": [
            {
                "numeric": [ ">", 9999 ]
            }
        ]
    }
}
```

您可以使用控制台、AWS CLI 或 AWS SAM 模板添加筛选条件。

------
#### [ Console ]

要使用控制台添加此筛选条件，请按照 [将筛选条件附加到事件源映射（控制台）](invocation-eventfiltering.md#filtering-console) 中的说明，为**筛选条件**输入以下字符串。

```
{ "body" : { "RecordNumber" : [ { "numeric": [ ">", 9999 ] } ] } }
```

------
#### [ AWS CLI ]

要使用 AWS Command Line Interface（AWS CLI）创建包含这些筛选条件的新事件源映射，请运行以下命令。

```
aws lambda create-event-source-mapping \
    --function-name my-function \
    --event-source-arn arn:aws:sqs:us-east-2:123456789012:my-queue \
    --filter-criteria '{"Filters": [{"Pattern": "{ \"body\" : { \"RecordNumber\" : [ { \"numeric\": [ \">\", 9999 ] } ] } }"}]}'
```

要将这些筛选条件添加到现有事件源映射中，请运行以下命令。

```
aws lambda update-event-source-mapping \
    --uuid "a1b2c3d4-5678-90ab-cdef-11111EXAMPLE" \
    --filter-criteria '{"Filters": [{"Pattern": "{ \"body\" : { \"RecordNumber\" : [ { \"numeric\": [ \">\", 9999 ] } ] } }"}]}'
```

------
#### [ AWS SAM ]

要使用 AWS SAM 添加此筛选条件，请将以下代码段添加到事件源的 YAML 模板中。

```
FilterCriteria:
  Filters:
    - Pattern: '{ "body" : { "RecordNumber" : [ { "numeric": [ ">", 9999 ] } ] } }'
```

------

对于 Amazon SQS，消息正文可以是任何字符串。但如果您的 `FilterCriteria` 期望 `body` 为有效的 JSON 格式，则可能会导致问题。反之亦然：如果传入的消息正文为 JSON 格式，但筛选条件期望 `body` 为纯字符串，这可能会导致出现意外行为。

要避免此问题，请确保 `FilterCriteria` 中的正文格式与您从队列中收到的消息中的 `body` 的期望格式一致。在筛选消息之前，Lambda 会自动评估传入消息正文的格式以及 `body` 的筛选条件模式的格式。如果不一致，Lambda 将会删除此消息。下表汇总了此评估：


| 传入消息 `body` 格式 | 筛选条件模式 `body` 格式 | 导致的操作 | 
| --- | --- | --- | 
|  纯字符串  |  纯字符串  |  Lambda 根据您的筛选条件进行筛选。  | 
|  纯字符串  |  数据属性中没有筛选条件模式  |  Lambda 根据您的筛选条件进行筛选（仅限其他元数据属性）。  | 
|  纯字符串  |  有效 JSON  |  Lambda 将会丢弃消息。  | 
|  有效 JSON  |  纯字符串  |  Lambda 将会丢弃消息。  | 
|  有效 JSON  |  数据属性中没有筛选条件模式  |  Lambda 根据您的筛选条件进行筛选（仅限其他元数据属性）。  | 
|  有效 JSON  |  有效 JSON  |  Lambda 根据您的筛选条件进行筛选。  | 

# 教程：将 Lambda 与 Amazon SQS 结合使用
<a name="with-sqs-example"></a>

在此教程中，您将创建一个会使用来自某个 [Amazon Simple Queue Service（Amazon SQS）](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/welcome.html)队列的消息的 Lambda 函数。只要有新消息添加到队列，Lambda 函数就会运行。函数会将消息写入 Amazon CloudWatch Logs 日志流。下图显示了您用于完成教程的 AWS 资源。

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/sqs_tut_resources.png)


要完成本教程，请执行以下步骤：

1. 创建将消息写入 CloudWatch Logs 的 Lambda 函数。

1. 创建 Amazon SQS 队列。

1. 创建 Lambda 事件源映射。事件源映射会读取 Amazon SQS 队列并在添加新消息时调用 Lambda 函数。

1. 将消息添加到队列并在 CloudWatch Logs 中监控结果来测试设置。

## 先决条件
<a name="with-sqs-prepare"></a>

### 安装 AWS Command Line Interface
<a name="install_aws_cli"></a>

如果您尚未安装 AWS Command Line Interface，请按照[安装或更新最新版本的 AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) 中的步骤进行安装。

本教程需要命令行终端或 Shell 来运行命令。在 Linux 和 macOS 中，可使用您首选的 Shell 和程序包管理器。

**注意**  
在 Windows 中，操作系统的内置终端不支持您经常与 Lambda 一起使用的某些 Bash CLI 命令（例如 `zip`）。[安装 Windows Subsystem for Linux](https://docs.microsoft.com/en-us/windows/wsl/install-win10)，获取 Ubuntu 和 Bash 与 Windows 集成的版本。

## 创建执行角色
<a name="with-sqs-create-execution-role"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/sqs_tut_steps1.png)


[执行角色](lambda-intro-execution-role.md)是一个 AWS Identity and Access Management（IAM）角色，用于向 Lambda 函数授予访问 AWS 服务 和资源的权限。要允许函数从 Amazon SQS 中读取项目，请附加 **AWSLambdaSQSQueueExecutionRole** 权限策略。

**创建执行角色并附加 Amazon SQS 权限策略**

1. 打开 IAM 控制台的[角色页面](https://console.aws.amazon.com/iam/home#/roles)。

1. 选择**创建角色**。

1. 在**可信实体类型**中选择 **AWS 服务**。

1. 在**使用案例**中选择 **Lambda**。

1. 选择**下一步**。

1. 在**权限策略**搜索框中输入 **AWSLambdaSQSQueueExecutionRole**。

1. 选择 **AWSLambdaSQSQueueExecutionRole** 策略，然后选择**下一步**。

1. 在**角色详细信息**下，为**角色名称**输入 **lambda-sqs-role**，然后选择**创建角色**。

角色创建后，记下执行角色的 Amazon 资源名称（ARN）。您将在后面的步骤中用到它。

## 创建函数
<a name="with-sqs-create-function"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/sqs_tut_steps2.png)


创建一个处理您的 Amazon SQS 消息的 Lambda 函数。函数代码将 Amazon SQS 消息的正文记录到 CloudWatch Logs 中。

本教程使用 Node.js 24 运行时系统，但我们还提供了其他运行时系统语言的示例代码。您可以选择以下框中的选项卡，查看适用于您感兴趣的运行时系统的代码。您将在此步骤中使用的 JavaScript 代码是 **JavaScript** 选项卡中显示的第一个示例。

------
#### [ .NET ]

**适用于 .NET 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-sqs-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
通过 .NET 将 SQS 事件与 Lambda 结合使用。  

```
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
﻿using Amazon.Lambda.Core;
using Amazon.Lambda.SQSEvents;


// Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]

namespace SqsIntegrationSampleCode
{
    public async Task FunctionHandler(SQSEvent evnt, ILambdaContext context)
    {
        foreach (var message in evnt.Records)
        {
            await ProcessMessageAsync(message, context);
        }

        context.Logger.LogInformation("done");
    }

    private async Task ProcessMessageAsync(SQSEvent.SQSMessage message, ILambdaContext context)
    {
        try
        {
            context.Logger.LogInformation($"Processed message {message.Body}");

            // TODO: Do interesting work based on the new message
            await Task.CompletedTask;
        }
        catch (Exception e)
        {
            //You can use Dead Letter Queue to handle failures. By configuring a Lambda DLQ.
            context.Logger.LogError($"An error occurred");
            throw;
        }

    }
}
```

------
#### [ Go ]

**适用于 Go 的 SDK V2**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-sqs-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
使用 Go 将 SQS 事件与 Lambda 结合使用。  

```
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package integration_sqs_to_lambda

import (
	"fmt"
	"github.com/aws/aws-lambda-go/events"
	"github.com/aws/aws-lambda-go/lambda"
)

func handler(event events.SQSEvent) error {
	for _, record := range event.Records {
		err := processMessage(record)
		if err != nil {
			return err
		}
	}
	fmt.Println("done")
	return nil
}

func processMessage(record events.SQSMessage) error {
	fmt.Printf("Processed message %s\n", record.Body)
	// TODO: Do interesting work based on the new message
	return nil
}

func main() {
	lambda.Start(handler)
}
```

------
#### [ Java ]

**适用于 Java 的 SDK 2.x**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-sqs-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
通过 Java 将 SQS 事件与 Lambda 结合使用。  

```
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.SQSEvent;
import com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage;

public class Function implements RequestHandler<SQSEvent, Void> {
    @Override
    public Void handleRequest(SQSEvent sqsEvent, Context context) {
        for (SQSMessage msg : sqsEvent.getRecords()) {
            processMessage(msg, context);
        }
        context.getLogger().log("done");
        return null;
    }

    private void processMessage(SQSMessage msg, Context context) {
        try {
            context.getLogger().log("Processed message " + msg.getBody());

            // TODO: Do interesting work based on the new message

        } catch (Exception e) {
            context.getLogger().log("An error occurred");
            throw e;
        }

    }
}
```

------
#### [ JavaScript ]

**SDK for JavaScript（v3）**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/blob/main/integration-sqs-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
通过 JavaScript 将 SQS 事件与 Lambda 结合使用。  

```
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
exports.handler = async (event, context) => {
  for (const message of event.Records) {
    await processMessageAsync(message);
  }
  console.info("done");
};

async function processMessageAsync(message) {
  try {
    console.log(`Processed message ${message.body}`);
    // TODO: Do interesting work based on the new message
    await Promise.resolve(1); //Placeholder for actual async work
  } catch (err) {
    console.error("An error occurred");
    throw err;
  }
}
```
通过 TypeScript 将 SQS 事件与 Lambda 结合使用。  

```
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import { SQSEvent, Context, SQSHandler, SQSRecord } from "aws-lambda";

export const functionHandler: SQSHandler = async (
  event: SQSEvent,
  context: Context
): Promise<void> => {
  for (const message of event.Records) {
    await processMessageAsync(message);
  }
  console.info("done");
};

async function processMessageAsync(message: SQSRecord): Promise<any> {
  try {
    console.log(`Processed message ${message.body}`);
    // TODO: Do interesting work based on the new message
    await Promise.resolve(1); //Placeholder for actual async work
  } catch (err) {
    console.error("An error occurred");
    throw err;
  }
}
```

------
#### [ PHP ]

**适用于 PHP 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-sqs-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
使用 PHP 将 SQS 事件与 Lambda 结合使用。  

```
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
<?php

# using bref/bref and bref/logger for simplicity

use Bref\Context\Context;
use Bref\Event\InvalidLambdaEvent;
use Bref\Event\Sqs\SqsEvent;
use Bref\Event\Sqs\SqsHandler;
use Bref\Logger\StderrLogger;

require __DIR__ . '/vendor/autoload.php';

class Handler extends SqsHandler
{
    private StderrLogger $logger;
    public function __construct(StderrLogger $logger)
    {
        $this->logger = $logger;
    }

    /**
     * @throws InvalidLambdaEvent
     */
    public function handleSqs(SqsEvent $event, Context $context): void
    {
        foreach ($event->getRecords() as $record) {
            $body = $record->getBody();
            // TODO: Do interesting work based on the new message
        }
    }
}

$logger = new StderrLogger();
return new Handler($logger);
```

------
#### [ Python ]

**适用于 Python 的 SDK（Boto3）**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-sqs-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
使用 Python 将 SQS 事件与 Lambda 结合使用。  

```
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
def lambda_handler(event, context):
    for message in event['Records']:
        process_message(message)
    print("done")

def process_message(message):
    try:
        print(f"Processed message {message['body']}")
        # TODO: Do interesting work based on the new message
    except Exception as err:
        print("An error occurred")
        raise err
```

------
#### [ Ruby ]

**适用于 Ruby 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-sqs-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
使用 Ruby 将 SQS 事件与 Lambda 结合使用。  

```
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
def lambda_handler(event:, context:)
  event['Records'].each do |message|
    process_message(message)
  end
  puts "done"
end

def process_message(message)
  begin
    puts "Processed message #{message['body']}"
    # TODO: Do interesting work based on the new message
  rescue StandardError => err
    puts "An error occurred"
    raise err
  end
end
```

------
#### [ Rust ]

**适用于 Rust 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-sqs-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
通过 Rust 将 SQS 事件与 Lambda 结合使用。  

```
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
use aws_lambda_events::event::sqs::SqsEvent;
use lambda_runtime::{run, service_fn, Error, LambdaEvent};

async fn function_handler(event: LambdaEvent<SqsEvent>) -> Result<(), Error> {
    event.payload.records.iter().for_each(|record| {
        // process the record
        tracing::info!("Message body: {}", record.body.as_deref().unwrap_or_default())
    });

    Ok(())
}

#[tokio::main]
async fn main() -> Result<(), Error> {
    tracing_subscriber::fmt()
        .with_max_level(tracing::Level::INFO)
        // disable printing the name of the module in every log line.
        .with_target(false)
        // disabling time is handy because CloudWatch will add the ingestion time.
        .without_time()
        .init();

    run(service_fn(function_handler)).await
}
```

------

**创建 Node.js Lambda 函数**

1. 为项目创建一个目录，然后切换到该目录。

   ```
   mkdir sqs-tutorial
   cd sqs-tutorial
   ```

1. 将示例 JavaScript 代码复制到名为 `index.js` 的新文件中。

1. 使用以下 `zip` 命令创建部署包。

   ```
   zip function.zip index.js
   ```

1. 使用 [create-function](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/create-function.html) AWS CLI 命令创建 Lambda 函数。对于 `role` 参数，请输入您之前创建的执行角色的 ARN。
**注意**  
Lambda 函数和 Amazon SQS 队列必须位于同一 AWS 区域。

   ```
   aws lambda create-function --function-name ProcessSQSRecord \
   --zip-file fileb://function.zip --handler index.handler --runtime nodejs24.x \
   --role arn:aws:iam::111122223333:role/lambda-sqs-role
   ```

## 测试此函数
<a name="with-sqs-create-test-function"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/sqs_tut_steps3.png)


使用 `invoke` AWS CLI 命令和一个示例 Amazon SQS 事件手动调用您的 Lambda 函数。

**使用示例事件调用 Lambda 函数**

1. 将下列 JSON 保存为名为 `input.json` 的文件。此 JSON 模拟 Amazon SQS 可能发送到 Lambda 函数的事件，其中 `"body"` 包含来自该队列的实际消息。在本示例中，消息为 `"test"`。  
**Example Amazon SQS 事件**  

   此为测试事件，无需您更改消息或账号。

   ```
   {
       "Records": [
           {
               "messageId": "059f36b4-87a3-44ab-83d2-661975830a7d",
               "receiptHandle": "AQEBwJnKyrHigUMZj6rYigCgxlaS3SLy0a...",
               "body": "test",
               "attributes": {
                   "ApproximateReceiveCount": "1",
                   "SentTimestamp": "1545082649183",
                   "SenderId": "AIDAIENQZJOLO23YVJ4VO",
                   "ApproximateFirstReceiveTimestamp": "1545082649185"
               },
               "messageAttributes": {},
               "md5OfBody": "098f6bcd4621d373cade4e832627b4f6",
               "eventSource": "aws:sqs",
               "eventSourceARN": "arn:aws:sqs:us-east-1:111122223333:my-queue",
               "awsRegion": "us-east-1"
           }
       ]
   }
   ```

1. 运行以下[调用](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/invoke.html) AWS CLI 命令。此命令将在响应中返回 CloudWatch 日志。有关检索日志的更多信息，请参阅[使用 AWS CLI 访问日志](monitoring-cloudwatchlogs-view.md#monitoring-cloudwatchlogs-cli)。

   ```
   aws lambda invoke --function-name ProcessSQSRecord --payload file://input.json out --log-type Tail \
   --query 'LogResult' --output text --cli-binary-format raw-in-base64-out | base64 --decode
   ```

   如果使用 **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)。

1. 在响应中查找 `INFO` 日志。Lambda 函数会在此处记录消息正文。应看到类似如下内容的日志：

   ```
   2023-09-11T22:45:04.271Z	348529ce-2211-4222-9099-59d07d837b60	INFO	Processed message test
   2023-09-11T22:45:04.288Z	348529ce-2211-4222-9099-59d07d837b60	INFO	done
   ```

## 创建 Amazon SQS 队列
<a name="with-sqs-configure-sqs"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/sqs_tut_steps4.png)


创建一个可由 Lambda 函数用作事件源的 Amazon SQS 队列。Lambda 函数和 Amazon SQS 队列必须位于同一 AWS 区域。

**创建队列**

1. 打开 [Amazon SQS 控制台](https://console.aws.amazon.com/sqs)。

1. 选择**创建队列**。

1. 输入队列名称。将所有其他选项保留为默认设置。

1. 选择**创建队列**。

在创建队列后，记下其 ARN。在下一步中将该队列与您的 Lambda 函数关联时，您将需要此类信息。

## 配置事件源
<a name="with-sqs-attach-notification-configuration"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/sqs_tut_steps5.png)


创建[事件源映射](invocation-eventsourcemapping.md)，将 Amazon SQS 队列连接到 Lambda 函数。事件源映射会读取 Amazon SQS 队列并在添加新消息时调用 Lambda 函数。

要在 Amazon SQS 队列与 Lambda 函数之间创建映射，请使用以下 [create-event-source-mapping](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/create-event-source-mapping.html) AWS CLI 命令。示例：

```
aws lambda create-event-source-mapping --function-name ProcessSQSRecord  --batch-size 10 \
--event-source-arn arn:aws:sqs:us-east-1:111122223333:my-queue
```

要获取事件源映射列表，请使用 [list-event-source-mappings](https://awscli.amazonaws.com/v2/documentation/api/2.1.29/reference/lambda/list-event-source-mappings.html) 命令。示例：

```
aws lambda list-event-source-mappings --function-name ProcessSQSRecord
```

## 发送测试消息
<a name="with-sqs-test-message"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/sqs_tut_steps6.png)


**将 Amazon SQS 消息发送到 Lambda 函数**

1. 打开 [Amazon SQS 控制台](https://console.aws.amazon.com/sqs)。

1. 选择您之前创建的队列。

1. 选择**发送和接收消息**。

1. 在**消息正文**下输入测试消息，例如“这是一条测试消息”。

1. 选择**发送消息**。

Lambda 轮询队列以获取更新。当有新消息时，Lambda 会使用该新的事件数据从队列中调用您的函数。如果该函数处理程序正常返回并且没有异常，则 Lambda 认为该消息得到成功处理并开始读取队列中的新消息。成功处理消息后，Lambda 从队列中将其自动删除。如果该处理程序引发异常，则 Lambda 认为消息的批量处理并未成功进行，并且 Lambda 会用相同的批量消息调用该函数。

## 查看 CloudWatch 日志
<a name="with-sqs-check-logs"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/sqs_tut_steps7.png)


**确认函数已处理消息**

1. 打开 Lamba 控制台的[函数](https://console.aws.amazon.com/lambda/home#/functions)页面。

1. 选择 **ProcessSQSRecord** 函数。

1. 选择 **Monitor (监控)**。

1. 选择**查看 CloudWatch 日志**。

1. 在 CloudWatch 控制台中，为该函数选择**日志流**。

1. 查找 `INFO` 日志。Lambda 函数会在此处记录消息正文。您应该能看到从 Amazon SQS 队列发送的消息。示例：

   ```
   2023-09-11T22:49:12.730Z b0c41e9c-0556-5a8b-af83-43e59efeec71 INFO Processed message this is a test message.
   ```

## 清除资源
<a name="cleanup"></a>

除非您想要保留为本教程创建的资源，否则可立即将其删除。通过删除您不再使用的 AWS 资源，可防止您的 AWS 账户 产生不必要的费用。

**删除执行角色**

1. 打开 IAM 控制台的[角色页面](https://console.aws.amazon.com/iam/home#/roles)。

1. 选择您创建的执行角色。

1. 选择**删除**。

1. 在文本输入字段中输入角色名称，然后选择**删除**。

**删除 Lambda 函数**

1. 打开 Lamba 控制台的 [Functions（函数）页面](https://console.aws.amazon.com/lambda/home#/functions)。

1. 选择您创建的函数。

1. 依次选择**操作**和**删除**。

1. 在文本输入字段中键入 **confirm**，然后选择 **Delete**（删除）。

**删除 Amazon SQS 队列**

1. 登录到 AWS 管理控制台 并打开 Amazon SQS 控制台，网址：[https://console.aws.amazon.com/vpc/](https://console.aws.amazon.com/sqs/)。

1. 选择创建的队列。

1. 选择**删除**。

1. 在文本输入字段中输入 **confirm**。

1. 选择**删除**。

# 教程：将跨账户 Amazon SQS 队列用作事件源
<a name="with-sqs-cross-account-example"></a>

在此教程中，您将创建一个使用来自不同 AWS 账户中 Amazon Simple Queue Service (Amazon SQS) 队列的消息的 Lambda 函数。本教程涉及两个 AWS 账户：**账户 A** 指包含您的 Lambda 函数的账户，而**账户 B** 指包含 Amazon SQS 队列的账户。

## 先决条件
<a name="with-sqs-cross-account-prepare"></a>

### 安装 AWS Command Line Interface
<a name="install_aws_cli"></a>

如果您尚未安装 AWS Command Line Interface，请按照[安装或更新最新版本的 AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) 中的步骤进行安装。

本教程需要命令行终端或 Shell 来运行命令。在 Linux 和 macOS 中，可使用您首选的 Shell 和程序包管理器。

**注意**  
在 Windows 中，操作系统的内置终端不支持您经常与 Lambda 一起使用的某些 Bash CLI 命令（例如 `zip`）。[安装 Windows Subsystem for Linux](https://docs.microsoft.com/en-us/windows/wsl/install-win10)，获取 Ubuntu 和 Bash 与 Windows 集成的版本。

## 创建执行角色（账户 A）
<a name="with-sqs-cross-account-create-execution-role"></a>

在**账户 A** 中，创建一个[执行角色](lambda-intro-execution-role.md)，该角色向您的函数授予访问所需 AWS 资源的权限。

**创建执行角色**

1. 在 AWS Identity and Access Management IAM 控制台中，打开 [Roles (角色) 页面](https://console.aws.amazon.com/iam/home#/roles)。

1. 选择 **Create role**（创建角色）。

1. 创建具有以下属性的角色。
   + **Trusted entity (可信任的实体)** – **AWS Lambda**
   + **Permissions（权限）** – **AWSLambdaSQSQueueExecutionRole**
   + **Role name**（角色名称）– **cross-account-lambda-sqs-role**

**AWSLambdaSQSQueueExecutionRole** 策略具有该函数从 Amazon SQS 中读取项目并将日志写入 Amazon CloudWatch Logs 所需的权限。

## 创建函数（账户 A）
<a name="with-sqs-cross-account-create-function"></a>

在**账户 A** 中，创建一个处理您 Amazon SQS 消息的 Lambda 函数。Lambda 函数和 Amazon SQS 队列必须位于同一 AWS 区域。

下列 Node.js 代码示例将每条消息写入 CloudWatch Logs 中的日志。

**Example index.mjs**  

```
export const handler = async function(event, context) {
  event.Records.forEach(record => {
    const { body } = record;
    console.log(body);
  });
  return {};
}
```

**创建函数**
**注意**  
按照这些步骤在将创建一个 Node.js 函数。对于其他语言，步骤类似，但有些细节不同。

1. 将代码示例保存为名为 `index.mjs` 的文件。

1. 创建部署程序包。

   ```
   zip function.zip index.mjs
   ```

1. 使用 `create-function` AWS Command Line Interface (AWS CLI) 命令创建函数。将 `arn:aws:iam::111122223333:role/cross-account-lambda-sqs-role` 替换为您之前创建的执行角色的 ARN。

   ```
   aws lambda create-function --function-name CrossAccountSQSExample \
   --zip-file fileb://function.zip --handler index.handler --runtime nodejs24.x \
   --role arn:aws:iam::111122223333:role/cross-account-lambda-sqs-role
   ```

## 测试函数（账户 A）
<a name="with-sqs-cross-account-create-test-function"></a>

在**账户 A** 中，使用 `invoke` AWS CLI 命令和示例 Amazon SQS 事件手动调用 Lambda 函数。

如果该处理程序正常返回并且没有异常，则 Lambda 认为该消息得到成功处理并开始读取队列中的新消息。成功处理消息后，Lambda 从队列中将其自动删除。如果该处理程序引发异常，则 Lambda 认为消息的批量处理并未成功进行，并且 Lambda 会用相同的批量消息调用该函数。

1. 将下列 JSON 保存为名为 `input.txt` 的文件。

   ```
   {
       "Records": [
           {
               "messageId": "059f36b4-87a3-44ab-83d2-661975830a7d",
               "receiptHandle": "AQEBwJnKyrHigUMZj6rYigCgxlaS3SLy0a...",
               "body": "test",
               "attributes": {
                   "ApproximateReceiveCount": "1",
                   "SentTimestamp": "1545082649183",
                   "SenderId": "AIDAIENQZJOLO23YVJ4VO",
                   "ApproximateFirstReceiveTimestamp": "1545082649185"
               },
               "messageAttributes": {},
               "md5OfBody": "098f6bcd4621d373cade4e832627b4f6",
               "eventSource": "aws:sqs",
               "eventSourceARN": "arn:aws:sqs:us-east-1:111122223333:example-queue",
               "awsRegion": "us-east-1"
           }
       ]
   }
   ```

   上述 JSON 模拟 Amazon SQS 可能发送到您的 Lambda 函数的事件，其中 `"body"` 包含来自该队列的实际消息。

1. 运行以下 `invoke` AWS CLI 命令。

   ```
   aws lambda invoke --function-name CrossAccountSQSExample \
   --cli-binary-format raw-in-base64-out \
   --payload file://input.txt outputfile.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)。

1. 在 `outputfile.txt` 文件中验证输出。

## 创建 Amazon SQS 队列（账户 B）。
<a name="with-sqs-cross-account-configure-sqs"></a>

在**账户 B** 中，创建一个可由**账户 A** 中 Lambda 函数用作事件源的 Amazon SQS 队列。Lambda 函数和 Amazon SQS 队列必须位于同一 AWS 区域。

**创建队列**

1. 打开 [Amazon SQS 控制台](https://console.aws.amazon.com/sqs)。

1. 选择**创建队列**。

1. 创建具有以下属性的队列。
   + **类型** - **标准**
   + **名称** - **LambdaCrossAccountQueue**
   + **配置** - 保留默认设置。
   + **访问策略** - 选择 **Advanced (高级)**。粘贴以下 JSON 策略。替换以下值：
     + `111122223333`：**账户 A** 的 AWS 账户 ID
     + `444455556666`：**账户 B** 的 AWS 账户 ID

------
#### [ JSON ]

****  

     ```
     {
         "Version":"2012-10-17",		 	 	 
         "Id": "Queue1_Policy_UUID",
         "Statement": [
             {
                 "Sid": "Queue1_AllActions",
                 "Effect": "Allow",
                 "Principal": {
                     "AWS": [
                         "arn:aws:iam::111122223333:role/cross-account-lambda-sqs-role"
                     ]
                 },
                 "Action": "sqs:*",
                 "Resource": "arn:aws:sqs:us-east-1:444455556666:LambdaCrossAccountQueue"
             }
         ]
     }
     ```

------

     此策略授予**账户 A** 中 Lambda 执行角色权限，以使用 Amazon SQS 队列中的消息。

1. 创建队列后，记录其 Amazon Resource Name (ARN)。在下一步中将该队列与您的 Lambda 函数关联时，您将需要此类信息。

## 配置事件源（帐户 A）
<a name="with-sqs-cross-account-event-source"></a>

在**账户 A** 中，通过运行以下 `create-event-source-mapping` AWS CLI 命令创建**账户 B** 中 Amazon SQS 队列和 Lambda 函数之间的事件源映射。将 `arn:aws:sqs:us-east-1:444455556666:LambdaCrossAccountQueue` 替换为在上一步中创建的 Amazon SQS 队列的 ARN。

```
aws lambda create-event-source-mapping --function-name CrossAccountSQSExample --batch-size 10 \
--event-source-arn arn:aws:sqs:us-east-1:444455556666:LambdaCrossAccountQueue
```

要获取您的事件源映射的列表，请运行下列命令。

```
aws lambda list-event-source-mappings --function-name CrossAccountSQSExample \
--event-source-arn arn:aws:sqs:us-east-1:444455556666:LambdaCrossAccountQueue
```

## 测试设置
<a name="with-sqs-final-integration-test-no-iam"></a>

现在，可以按以下方式测试设置：

1. 在**账户 B** 中，打开[Amazon SQS 控制台](https://console.aws.amazon.com/sqs)。

1. 选择之前创建的 **LambdaCrossAccountQueue**。

1. 选择 **Send and receive messages（发送和接收消息）**。

1. 在 **Message body (消息正文)** 中，输入测试消息。

1. 选择 **Send message**（发送消息）。

您在**账户 A** 中的 Lambda 函数应该收到消息。Lambda 将继续轮询队列以获取更新。当有新消息时，Lambda 会使用该新的事件数据从队列中调用您的函数。您的函数运行并在 Amazon CloudWatch 中创建日志。您可以在 [CloudWatch 控制台](https://console.aws.amazon.com/cloudwatch)中查看这些日志。

## 清除资源
<a name="cleanup"></a>

除非您想要保留为本教程创建的资源，否则可立即将其删除。通过删除您不再使用的 AWS 资源，可防止您的 AWS 账户 产生不必要的费用。

在**账户 A** 中，清理您的执行角色和 Lambda 函数。

**删除执行角色**

1. 打开 IAM 控制台的[角色页面](https://console.aws.amazon.com/iam/home#/roles)。

1. 选择您创建的执行角色。

1. 选择**删除**。

1. 在文本输入字段中输入角色名称，然后选择**删除**。

**删除 Lambda 函数**

1. 打开 Lamba 控制台的 [Functions（函数）页面](https://console.aws.amazon.com/lambda/home#/functions)。

1. 选择您创建的函数。

1. 依次选择**操作**和**删除**。

1. 在文本输入字段中键入 **confirm**，然后选择 **Delete**（删除）。

在**账户 B** 中，清理 Amazon SQS 队列。

**删除 Amazon SQS 队列**

1. 登录到 AWS 管理控制台 并打开 Amazon SQS 控制台，网址：[https://console.aws.amazon.com/vpc/](https://console.aws.amazon.com/sqs/)。

1. 选择创建的队列。

1. 选择**删除**。

1. 在文本输入字段中输入 **confirm**。

1. 选择**删除**。

# 使用 Step Functions 编排 Lambda 函数
<a name="with-step-functions"></a>

AWS Step Functions 提供可视化工作流编排，用于协调 Lambda 函数与其他 AWS 服务。Step Functions 与 220 多种 AWS 服务本机集成，并提供完全托管、零维护基础设施，是您需要可视化工作流设计和完全托管的服务集成时的理想选择。

要在 Lambda 中使用标准编程语言进行编排（其中工作流逻辑与业务逻辑并存），请考虑 [Lambda 持久性函数](durable-functions.md)。有关在这些选项之间进行选择的帮助，请参阅[持久性函数或 Step Functions](durable-step-functions.md)。

例如，处理订单可能需要验证订单详细信息、检查库存水平、处理付款和生成发票。为每项任务编写单独的 Lambda 函数，然后使用 Step Functions 来管理工作流程。Step Functions 协调函数之间的数据流，并处理每个步骤中的错误。随着工作流程变得越来越复杂，这种分离使您的工作流程更易于可视化、修改和维护。

## 何时将 Step Functions 与 Lambda 结合使用
<a name="when-to-use-step-functions"></a>

以下场景很好地说明了 Step Functions 何时特别适合编排基于 Lambda 的应用程序。
+ [顺序处理](#sequential-processing)
+ [复杂的错误处理](#complex-error-handling)
+ [有条件的工作流程和人工批准](#conditional-workflows-human-approvals)
+ [并行处理](#parallel-processing)

### 顺序处理
<a name="sequential-processing"></a>

顺序处理是指必须先完成一项任务才能开始下一个任务。例如，在订单处理系统中，只有在订单验证完成后才能开始付款处理，并且必须等待付款确认后才能生成发票。为每项任务编写单独的 Lambda 函数，然后使用 Step Functions 来管理序列和处理函数之间的数据流。

#### 反模式示例
<a name="anti-pattern-sequential"></a>

单个 Lambda 函数通过以下方式管理整个订单处理工作流程：
+ 按顺序调用其他 Lambda 函数
+ 解析和验证每个函数的响应
+ 实现错误处理和恢复逻辑
+ 管理函数之间的数据流

#### 推荐方法
<a name="recommended-sequential"></a>

使用两个 Lambda 函数：一个用于验证订单，另一个用于处理付款。Step Functions 通过以下方式协调这些函数：
+ 按正确的顺序运行任务
+ 在函数之间传递数据
+ 在每个步骤中实现错误处理
+ 使用 [Choice](https://docs.aws.amazon.com/step-functions/latest/dg/state-choice.html) 状态确保只有有效的订单才能继续付款

**Example 工作流程图**  

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/sequential_workflow.png)


**注意**  
**代码优先替代方案：**有关使用基于代码的检查点进行顺序处理和重试，请参阅 [Lambda 持久性函数步骤](durable-basic-concepts.md)。

### 复杂的错误处理
<a name="complex-error-handling"></a>

虽然 Lambda 为[异步调用和事件源映射提供了重试功能](invocation-retries.md)，但 Step Functions 为复杂的工作流提供了更复杂的错误处理。您可以使用指数回退[配置自动重试](https://docs.aws.amazon.com/step-functions/latest/dg/concepts-error-handling.html#error-handling-retrying-after-an-error)，并针对不同类型的错误设置不同的重试策略。重试次数用完后，使用 `Catch` 将错误路由到[回退状态](https://docs.aws.amazon.com/step-functions/latest/dg/concepts-error-handling.html#error-handling-fallback-states)。当您需要协调多个函数和服务的工作流级错误处理时，这特别有用。

要了解有关在状态机中处理 Lambda 函数错误的更多信息，请参阅 *The AWS Step Functions Workshop* 中的 [Handling errors](https://catalog.workshops.aws/stepfunctions/handling-errors)。

#### 反模式示例
<a name="anti-pattern-error-handling"></a>

单个 Lambda 函数可以处理以下所有操作：
+ 尝试调用付款处理服务
+ 如果付款服务不可用，则该函数将等待并稍后重试。
+ 为等待时间实现自定义指数回退
+ 所有尝试都失败后，捕获错误并选择另一个流程

#### 推荐方法
<a name="recommended-error-handling"></a>

使用仅专注于付款处理的单个 Lambda 函数。Step Functions 通过以下方式管理错误处理：
+ [使用可配置的回退周期自动重试失败的任务](https://docs.aws.amazon.com/step-functions/latest/dg/concepts-error-handling.html#error-handling-retrying-after-an-error)
+ 根据错误类型应用不同的重试策略
+ 将不同类型的错误路由到相应的回退状态
+ 维护错误处理状态和历史记录

**Example 工作流程图**  

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/error_handling_workflow.png)


**注意**  
**代码优先替代方案：**持久性函数通过可配置的重试策略提供 try-catch 错误处理。请参阅[持久性函数中的错误处理](durable-execution-sdk-retries.md)。

### 有条件的工作流程和人工批准
<a name="conditional-workflows-human-approvals"></a>

使用 Step Functions [Choice 状态](https://docs.aws.amazon.com/step-functions/latest/dg/state-choice.html)根据函数输出路由工作流，使用 [waitForTaskToken 后缀](https://docs.aws.amazon.com/step-functions/latest/dg/connect-to-resource.html#connect-wait-token)暂停工作流程以进行人工决策。例如，要处理提高信用额度的请求，请使用 Lambda 函数来评估风险因素。然后，使用 Step Functions 将高风险请求路由到人工批准，将低风险请求路由到自动批准。

要部署使用回调任务令牌集成模式的示例工作流程，请参阅 *The AWS Step Functions Workshop* 中的 [Callback with Task Token](https://catalog.workshops.aws/stepfunctions/integrating-services/3-callback-token)。

#### 反模式示例
<a name="anti-pattern-conditional"></a>

单个 Lambda 函数通过以下方式管理复杂的批准工作流程：
+ 实现嵌套条件逻辑以评估信用申请
+ 根据申请金额调用不同的批准函数
+ 管理多个批准途径和决策点
+ 跟踪待批准的状态
+ 实现批准的超时和通知逻辑

#### 推荐方法
<a name="recommended-conditional"></a>

使用三个 Lambda 函数：一个用于评估每个请求的风险，一个用于批准低风险请求，另一个用于将高风险请求路由给经理进行审核。Step Functions 通过以下方式管理工作流程：
+ 使用 [Choice](https://docs.aws.amazon.com/step-functions/latest/dg/state-choice.html) 状态根据金额和风险级别路由请求
+ 等待人工批准时暂停执行
+ 管理待批准的超时
+ 提供对每个申请当前状态的可见性

**Example 工作流程图**  

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/conditional_workflow.png)


**注意**  
**代码优先替代方案：**持久性函数支持人工介入工作流的回调。请参阅[持久性函数中的回调](durable-execution-sdk.md)。

### 并行处理
<a name="parallel-processing"></a>

Step Functions 提供了三种处理并行处理的方法：
+ [Parallel 状态](https://docs.aws.amazon.com/step-functions/latest/dg/state-parallel.html)会同时执行工作流程的多个分支。当您需要并行运行不同的函数时（例如在提取图像元数据时生成缩略图），请使用此选项。
+ [内联 Map 状态](https://docs.aws.amazon.com/step-functions/latest/dg/state-map-inline.html)最多处理 40 次并发迭代的数据数组。此方法适用于需要对每个项目执行相同操作的中小型数据集。
+ [分布式 Map 状态](https://docs.aws.amazon.com/step-functions/latest/dg/state-map-distributed.html)可处理多达 10000 个并发执行的大规模并行处理，同时支持 JSON 数组和 Amazon Simple Storage Service（Amazon S3）数据来源。在处理大型数据集或需要更高并发度时使用此方法。

#### 反模式示例
<a name="anti-pattern-parallel"></a>

单个 Lambda 函数尝试通过以下方式管理并行处理：
+ 同时调用多个图像处理函数
+ 实现自定义并行执行逻辑
+ 管理每个并行任务的超时和错误处理
+ 收集和汇总所有函数的结果

#### 推荐方法
<a name="recommended-parallel"></a>

使用三个 Lambda 函数：一个用于创建缩略图，一个用于添加水印，一个用于提取元数据。Step Functions 通过以下方式管理这些函数：
+ 使用 [Parallel](https://docs.aws.amazon.com/step-functions/latest/dg/state-parallel.html) 状态同时运行所有函数
+ 将每个函数的结果收集到一个有序数组中
+ 管理所有并行执行的超时和错误处理
+ 仅在所有并行分支完成后才会继续

**Example 工作流程图**  

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/parallel_workflow.png)


**注意**  
**代码优先替代方案：**持久性函数提供 `parallel()` 和 `map()` 操作。请参阅[并行执行](durable-execution-sdk.md)。

## 何时不将 Step Functions 与 Lambda 结合使用
<a name="when-not-to-use"></a>

并非所有基于 Lambda 的应用程序都能从使用 Step Functions 中受益。在选择应用程序架构时，请考虑以下场景。
+ [简单的应用程序](#simple-applications)
+ [复杂的数据处理](#complex-data-processing)
+ [CPU 密集型工作负载](#cpu-intensive)

### 简单的应用程序
<a name="simple-applications"></a>

**注意**  
对于不需要视觉对象设计或大量服务集成的工作流，[Lambda 持久性函数](durable-functions.md)可能是一种更简单的替代方案，其可将工作流逻辑保留在 Lambda 中的代码中。

对于不需要复杂编排的应用程序，使用 Step Functions 可能会增加不必要的复杂性。例如，如果您只是处理 Amazon SQS 队列的消息或响应 Amazon EventBridge 事件，则可以将这些服务配置为直接调用您的 Lambda 函数。同样，如果您的应用程序仅包含一两个 Lambda 函数，且错误处理简单明了，则直接 Lambda 调用或事件驱动的架构可能更易于部署和维护。

### 复杂的数据处理
<a name="complex-data-processing"></a>

您可以使用 Step Functions [分布式 Map](https://docs.aws.amazon.com/step-functions/latest/dg/state-map-distributed.html) 状态通过 Lambda 函数同时处理大型 Amazon S3 数据集。这对于许多大规模并行工作负载非常有效，包括处理 JSON 或 CSV 文件等半结构化数据。但是，对于更复杂的数据转换或高级分析，请考虑以下替代方案：
+ **数据转换管道**：使用 AWS Glue 执行 ETL 作业，处理来自多个来源的结构化或半结构化数据。当您需要内置数据目录和架构管理功能时，AWS Glue 尤其有用。
+ **数据分析：**使用 Amazon EMR 进行 PB 级数据分析，尤其是在您需要 Apache Hadoop 生态系统工具或机器学习工作负载超出 Lambda 的[内存](configuration-memory.md)限制时。

### CPU 密集型工作负载
<a name="cpu-intensive"></a>

虽然 Step Functions 可以编排 CPU 密集型任务，但由于 CPU 资源有限，Lambda 函数可能不适合这些工作负载。对于工作流程中的计算密集型操作，请考虑以下替代方案：
+ **容器编排：**使用 Step Functions 管理 Amazon Elastic Container Service（Amazon ECS）任务，以获得更一致且可扩展的计算资源。
+ **批处理：**将 AWS Batch 与 Step Functions 集成，用于管理需要持续使用 CPU 的计算密集型批处理作业。

# 通过 Amazon S3 批处理事件调用 Lambda 函数
<a name="services-s3-batch"></a>

可以使用 Amazon S3 分批操作对一大组 Amazon S3 对象调用 Lambda 函数。Amazon S3 将跟踪批处理操作的进度，发送通知，并存储显示每个操作的状态的完成报告。

要运行分批操作，请创建 Amazon S3 [分批操作作业](https://docs.aws.amazon.com/AmazonS3/latest/dev/batch-ops-operations.html)。在创建作业时，您将提供清单（对象列表）并配置要对这些对象执行的操作。

在批处理作业启动时，Amazon S3 会为清单中的每个对象[同步](invocation-sync.md)调用 Lambda 函数。事件参数包含存储桶和对象的名称。

以下示例显示了对于 **amzn-s3-demo-bucket** 存储桶中名为 **customerImage1.jpg** 的对象，Amazon S3 发送到 Lambda 函数的事件。

**Example Amazon S3 批处理请求事件**  

```
{
"invocationSchemaVersion": "1.0",
    "invocationId": "YXNkbGZqYWRmaiBhc2RmdW9hZHNmZGpmaGFzbGtkaGZza2RmaAo",
    "job": {
        "id": "f3cc4f60-61f6-4a2b-8a21-d07600c373ce"
    },
    "tasks": [
        {
            "taskId": "dGFza2lkZ29lc2hlcmUK",
            "s3Key": "customerImage1.jpg",
            "s3VersionId": "1",
            "s3BucketArn": "arn:aws:s3:::amzn-s3-demo-bucket"
        }
    ]  
}
```

您的 Lambda 函数必须返回带字段的 JSON 对象，如以下示例所示。您可以从事件参数复制 `invocationId` 和 `taskId`。您可以在 `resultString` 内返回一个字符串。Amazon S3 会保存完成报告中的 `resultString` 值。

**Example Amazon S3 批处理请求响应**  

```
{
  "invocationSchemaVersion": "1.0",
  "treatMissingKeysAs" : "PermanentFailure",
  "invocationId" : "YXNkbGZqYWRmaiBhc2RmdW9hZHNmZGpmaGFzbGtkaGZza2RmaAo",
  "results": [
    {
      "taskId": "dGFza2lkZ29lc2hlcmUK",
      "resultCode": "Succeeded",
      "resultString": "[\"Alice\", \"Bob\"]"
    }
  ]
}
```

## 从 Amazon S3 分批操作调用 Lambda 函数
<a name="invoking"></a>

您可以使用非限定的或限定的函数 ARN 调用 Lambda 函数。如果要对整个批处理作业使用同一函数版本，请在创建作业时在 `FunctionARN` 参数中配置特定的函数版本。在配置别名或 \$1LATEST 限定符的情况下，如果在作业执行期间更新别名或 \$1LATEST，则批处理作业会立即开始调用函数的新版本。

请注意，您不能对分批操作重用现有的 Amazon S3 的基于事件的函数。这是因为 Amazon S3 分批操作会将不同的事件参数传递给 Lambda 函数，并且需要一条带特定 JSON 结构的返回消息。

在为 Amazon S3 Batch Job 创建的[基于资源](access-control-resource-based.md)的策略中，请确保为任务设置调用 Lambda 函数的权限。

在函数的[执行角色](https://docs.aws.amazon.com/AmazonS3/latest/userguide/batch-ops-iam-role-policies.html)中，为 Amazon S3 设置一个信任策略以便它在运行函数时代入该角色。

如果您的函数使用AWS开发工具包来管理 Amazon S3 资源，则您需要在执行角色中添加 Amazon S3 权限。

在作业运行时，Amazon S3 会启动多个函数实例来并行处理 Amazon S3 对象，直至函数的[并发限制](lambda-concurrency.md)。Amazon S3 会限制实例的初始增加以避免较小作业的额外成本。

如果 Lambda 函数返回 `TemporaryFailure` 响应代码，则 Amazon S3 会重试操作。

有关 Amazon S3 分批操作的更多信息，请参阅 *Amazon S3 开发人员指南*中的[执行分批操作](https://docs.aws.amazon.com/AmazonS3/latest/dev/batch-ops.html)。

有关如何在 Amazon S3 分批操作中使用 Lambda 函数的示例，请参阅*开发人员指南*中的[从 Amazon S3 分批操作调用 Lambda 函数](https://docs.aws.amazon.com/AmazonS3/latest/dev/batch-ops-invoke-lambda.html)。

# 使用 Amazon SNS 通知调用 Lambda 函数
<a name="with-sns"></a>

您可以使用 Lambda 函数处理 Amazon Simple Notification Service (Amazon SNS) 通知。Amazon SNS 支持将 Lambda 函数作为发送到主题的消息的目标。您可以将函数订阅到同一账户或其他 AWS 账户中的主题。有关详细的演练过程，请参阅[教程：将 AWS Lambda 与 Amazon Simple Notification Service 结合使用](with-sns-example.md)。

Lambda 仅支持标准 SNS 主题的 SNS 触发器。不支持 FIFO 主题。

Lambda 通过对消息进行排队和处理重试来异步处理 SNS 消息。如果无法联系到 Lambda 或消息被拒绝，Amazon SNS 将在几个小时内以递增的间隔重试。有关详细信息，请参阅 Amazon SNS 常见问题中的[可靠性](https://aws.amazon.com/sns/faqs/#Reliability)。

**警告**  
Lambda 异步调用至少处理每个事件一次，有可能出现重复处理记录的情况。为避免与重复事件相关的潜在问题，我们强烈建议您将函数代码设为幂等性。要了解更多信息，请参阅 AWS 知识中心的[如何让我的 Lambda 函数保持幂等性](https://repost.aws/knowledge-center/lambda-function-idempotent)。

## Powertools for AWS Lambda 中的幂等性实用程序
<a name="services-sns-powertools-idempotency"></a>

Powertools for AWS Lambda 中的幂等性实用程序使您的 Lambda 函数具有等性。它适用于 Python、TypeScript、Java 和 .NET。有关更多信息，请参阅 *Powertools for AWS Lambda (Python) 文档*中的[幂等性实用程序](https://docs.powertools.aws.dev/lambda/python/latest/utilities/idempotency/)、*Powertools for AWS Lambda (TypeScript) 文档*中的[幂等性实用程序](https://docs.aws.amazon.com/powertools/typescript/2.1.1/utilities/idempotency/)、*Powertools for AWS Lambda (Java) 文档*中的[幂等性实用程序](https://docs.powertools.aws.dev/lambda/java/latest/utilities/idempotency/)以及*Powertools for AWS Lambda (.NET) 文档*中的[幂等性实用程序](https://docs.powertools.aws.dev/lambda/dotnet/utilities/idempotency/)。

**Topics**
+ [

## Powertools for AWS Lambda 中的幂等性实用程序
](#services-sns-powertools-idempotency)
+ [

## 使用控制台为 Lambda 函数添​​加 Amazon SNS 主题触发器
](#sns-trigger-console)
+ [

## 为 Lambda 函数手动添加 Amazon SNS 主题触发器
](#sns-trigger-manual)
+ [

## 示例 SNS 事件形状
](#sns-sample-event)
+ [

# 教程：将 AWS Lambda 与 Amazon Simple Notification Service 结合使用
](with-sns-example.md)

## 使用控制台为 Lambda 函数添​​加 Amazon SNS 主题触发器
<a name="sns-trigger-console"></a>

要添加 SNS 主题作为 Lambda 函数的触发器，最简单的方法是使用 Lambda 控制台。当您通过控制台添加触发器时，Lambda 会自动设置必要的权限和订阅以开始从 SNS 主题接收事件。

**添加 SNS 主题作为 Lambda 函数的触发器（控制台）**

1. 打开 Lamba 控制台的[函数页面](https://console.aws.amazon.com/lambda/home#/functions)。

1. 选择您要为其添加触发器的函数的名称。

1. 选择**配置**，然后选择**触发器**。

1. 选择**添加触发器**。

1. 在**触发器配置**下的下拉菜单中，选择 **SNS**。

1. 对于 **SNS 主题**，请选择要订阅的 SNS 主题。

## 为 Lambda 函数手动添加 Amazon SNS 主题触发器
<a name="sns-trigger-manual"></a>

要手动为 Lambda 函数设置 SNS 触发器，您需要完成以下步骤：
+ 为函数定义基于资源的策略，以允许 SNS 调用该函数。
+ 将 Lambda 函数订阅至 Amazon SNS 主题。
**注意**  
如果您的 SNS 主题和 Lambda 函数位于不同的 AWS 账户中，则还需要授予额外权限以允许跨账户订阅 SNS 主题。有关更多信息，请参阅[授予 Amazon SNS 订阅的跨账户权限](with-sns-example.md#with-sns-subscription-grant-permission)。

您可以使用 AWS Command Line Interface（AWS CLI）来完成这两个步骤。首先，要为允许 SNS 调用的 Lambda 函数定义基于资源的策略，请使用以下 AWS CLI 命令。请务必将 `--function-name` 的值替换为您的 Lambda 函数名称，将 `--source-arn` 的值替换为您的 SNS 主题 ARN。

```
aws lambda add-permission --function-name example-function \
    --source-arn arn:aws:sns:us-east-1:123456789012:sns-topic-for-lambda \
    --statement-id function-with-sns --action "lambda:InvokeFunction" \
    --principal sns.amazonaws.com
```

要将您的函数订阅到 SNS 主题，请使用以下 AWS CLI 命令。将 `--topic-arn` 的值替换为您的 SNS 主题 ARN，将 `--notification-endpoint` 的值替换为您的 Lambda 函数 ARN。

```
aws sns subscribe --protocol lambda \
    --region us-east-1 \
    --topic-arn arn:aws:sns:us-east-1:123456789012:sns-topic-for-lambda \
    --notification-endpoint arn:aws:lambda:us-east-1:123456789012:function:example-function
```

## 示例 SNS 事件形状
<a name="sns-sample-event"></a>

Amazon SNS 通过包含消息和元数据的事件[异步](invocation-async.md)调用您的函数。

**Example Amazon SNS 消息事件**  

```
{
  "Records": [
    {
      "EventVersion": "1.0",
      "EventSubscriptionArn": "arn:aws:sns:us-east-1:123456789012:sns-lambda:21be56ed-a058-49f5-8c98-aedd2564c486",
      "EventSource": "aws:sns",
      "Sns": {
        "SignatureVersion": "1",
        "Timestamp": "2019-01-02T12:45:07.000Z",
        "Signature": "tcc6faL2yUC6dgZdmrwh1Y4cGa/ebXEkAi6RibDsvpi+tE/1+82j...65r==",
        "SigningCertURL": "https://sns.us-east-1.amazonaws.com/SimpleNotificationService-ac565b8b1a6c5d002d285f9598aa1d9b.pem",
        "MessageId": "95df01b4-ee98-5cb9-9903-4c221d41eb5e",
        "Message": "Hello from SNS!",
        "MessageAttributes": {
          "Test": {
            "Type": "String",
            "Value": "TestString"
          },
          "TestBinary": {
            "Type": "Binary",
            "Value": "TestBinary"
          }
        },
        "Type": "Notification",
        "UnsubscribeUrl": "https://sns.us-east-1.amazonaws.com/?Action=Unsubscribe&amp;SubscriptionArn=arn:aws:sns:us-east-1:123456789012:test-lambda:21be56ed-a058-49f5-8c98-aedd2564c486",
        "TopicArn":"arn:aws:sns:us-east-1:123456789012:sns-lambda",
        "Subject": "TestInvoke"
      }
    }
  ]
}
```

# 教程：将 AWS Lambda 与 Amazon Simple Notification Service 结合使用
<a name="with-sns-example"></a>

在本教程中，您将使用某个 AWS 账户 中的 Lambda 函数，订阅独立 AWS 账户 中的 Amazon Simple Notiﬁcation Service（Amazon SNS）主题。将消息发布到 Amazon SNS 主题时，Lambda 函数会读取消息内容并将其输出到 Amazon CloudWatch Logs。要完成此教程，需要使用 AWS Command Line Interface（AWS CLI）。

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-sns-tutorial/sns_tut_resources.png)


要完成本教程，请执行以下步骤：
+ 在**账户 A** 中，创建 Amazon SNS 主题。
+ 在**账户 B** 中，创建可从该主题读取消息的 Lambda 函数。
+ 在**账户 B** 中，创建该主题的订阅。
+ 在**账户 A** 中将消息发布到 Amazon SNS 主题，并确认**账户 B** 中的 Lambda 函数将其输出到 CloudWatch Logs。

通过完成这些步骤，您将了解如何配置 Amazon SNS 主题以调用 Lambda 函数。您还将了解如何创建 AWS Identity and Access Management（IAM）策略，向其他 AWS 账户 中的资源授予调用 Lambda 的权限。

在本教程中，您将使用两个独立的 AWS 账户。AWS CLI 命令通过以下方式对此进行说明：使用两个名为 `accountA` 和 `accountB` 的命名配置文件，每个文件配置为用于不同的 AWS 账户。要了解如何配置 AWS CLI 以使用不同的配置文件，请参阅《AWS Command Line Interface User Guide for Version 2**》中的 [Configuration and credential file settings](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html)。确保为两个配置文件配置相同的默认值 AWS 区域。

如果您为两个 AWS 账户 创建的 AWS CLI 配置文件使用不同名称，或者使用默认配置文件和一个命名配置文件，请根据需要按照以下步骤修改 AWS CLI 命令。

## 先决条件
<a name="with-sns-prereqs"></a>

### 安装 AWS Command Line Interface
<a name="install_aws_cli"></a>

如果您尚未安装 AWS Command Line Interface，请按照[安装或更新最新版本的 AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) 中的步骤进行安装。

本教程需要命令行终端或 Shell 来运行命令。在 Linux 和 macOS 中，可使用您首选的 Shell 和程序包管理器。

**注意**  
在 Windows 中，操作系统的内置终端不支持您经常与 Lambda 一起使用的某些 Bash CLI 命令（例如 `zip`）。[安装 Windows Subsystem for Linux](https://docs.microsoft.com/en-us/windows/wsl/install-win10)，获取 Ubuntu 和 Bash 与 Windows 集成的版本。

## 创建 Amazon SNS 主题（账户 A）
<a name="with-sns-create-topic"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-sns-tutorial/sns_tut_steps_1.png)


**创建 主题**
+ 在**账户 A** 中，使用以下 AWS CLI 命令创建 Amazon SNS 标准主题。

  ```
  aws sns create-topic --name sns-topic-for-lambda --profile accountA
  ```

  您应该可以看到类似于如下所示的输出内容。

  ```
  {
      "TopicArn": "arn:aws:sns:us-west-2:123456789012:sns-topic-for-lambda"
  }
  ```

  记下主题的 Amazon 资源名称（ARN）。稍后在本教程中向 Lambda 函数添加权限以订阅主题时，将需要使用此 ARN。

## 创建函数执行角色（账户 B）
<a name="with-sns-example-create-iam-role"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-sns-tutorial/sns_tut_steps_2.png)


执行角色是一个 IAM 角色，用于向 Lambda 函数授予访问 AWS 服务 和资源的权限。在**账户 B** 中创建函数之前，您需要创建一个角色，向该函数授予将日志写入 CloudWatch Logs 的基本权限。我们将在后续步骤中添加读取 Amazon SNS 主题的权限。

**创建执行角色**

1. 在**账户 B** 中，打开 IAM 控制台中的[角色页面](https://console.aws.amazon.com/iam/home#/roles)。

1. 选择**创建角色**。

1. 在**可信实体类型**中选择 **AWS 服务**。

1. 在**使用案例**中选择 **Lambda**。

1. 选择**下一步**。

1. 通过执行以下操作，向角色添加基本权限策略：

   1. 在**权限策略**搜索框中输入 **AWSLambdaBasicExecutionRole**。

   1. 选择**下一步**。

1. 通过执行以下操作，完成角色创建：

   1. 在**角色详细信息**下的**角色名称**中输入 **lambda-sns-role**。

   1. 选择**创建角色**。

## 创建 Lambda 函数（账户 B）
<a name="with-sns-example-create-test-function"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-sns-tutorial/sns_tut_steps_3.png)


创建一个处理您的 Amazon SNS 消息的 Lambda 函数。函数代码会将每条记录的消息内容记录到 Amazon CloudWatch Logs 中。

本教程使用 Node.js 24 运行时系统，但我们还提供了其他运行时系统语言的示例代码。您可以选择以下框中的选项卡，查看适用于您感兴趣的运行时系统的代码。您将在此步骤中使用的 JavaScript 代码是 **JavaScript** 选项卡中显示的第一个示例。

------
#### [ .NET ]

**适用于 .NET 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-sns-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
使用 .NET 将 SNS 事件与 Lambda 结合使用。  

```
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
using Amazon.Lambda.Core;
using Amazon.Lambda.SNSEvents;


// Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]

namespace SnsIntegration;

public class Function
{
    public async Task FunctionHandler(SNSEvent evnt, ILambdaContext context)
    {
        foreach (var record in evnt.Records)
        {
            await ProcessRecordAsync(record, context);
        }
        context.Logger.LogInformation("done");
    }

    private async Task ProcessRecordAsync(SNSEvent.SNSRecord record, ILambdaContext context)
    {
        try
        {
            context.Logger.LogInformation($"Processed record {record.Sns.Message}");

            // TODO: Do interesting work based on the new message
            await Task.CompletedTask;
        }
        catch (Exception e)
        {
            //You can use Dead Letter Queue to handle failures. By configuring a Lambda DLQ.
            context.Logger.LogError($"An error occurred");
            throw;
        }
    }
}
```

------
#### [ Go ]

**适用于 Go 的 SDK V2**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-sns-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
使用 Go 将 SNS 事件与 Lambda 结合使用。  

```
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package main

import (
	"context"
	"fmt"

	"github.com/aws/aws-lambda-go/events"
	"github.com/aws/aws-lambda-go/lambda"
)

func handler(ctx context.Context, snsEvent events.SNSEvent) {
	for _, record := range snsEvent.Records {
		processMessage(record)
	}
	fmt.Println("done")
}

func processMessage(record events.SNSEventRecord) {
	message := record.SNS.Message
	fmt.Printf("Processed message: %s\n", message)
	// TODO: Process your record here
}

func main() {
	lambda.Start(handler)
}
```

------
#### [ Java ]

**适用于 Java 的 SDK 2.x**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-sns-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
通过 Java 将 SNS 事件与 Lambda 结合使用。  

```
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package example;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.LambdaLogger;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.SNSEvent;
import com.amazonaws.services.lambda.runtime.events.SNSEvent.SNSRecord;


import java.util.Iterator;
import java.util.List;

public class SNSEventHandler implements RequestHandler<SNSEvent, Boolean> {
    LambdaLogger logger;

    @Override
    public Boolean handleRequest(SNSEvent event, Context context) {
        logger = context.getLogger();
        List<SNSRecord> records = event.getRecords();
        if (!records.isEmpty()) {
            Iterator<SNSRecord> recordsIter = records.iterator();
            while (recordsIter.hasNext()) {
                processRecord(recordsIter.next());
            }
        }
        return Boolean.TRUE;
    }

    public void processRecord(SNSRecord record) {
        try {
            String message = record.getSNS().getMessage();
            logger.log("message: " + message);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}
```

------
#### [ JavaScript ]

**SDK for JavaScript（v3）**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/blob/main/integration-sns-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
使用 JavaScript 将 SNS 事件与 Lambda 结合使用。  

```
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
exports.handler = async (event, context) => {
  for (const record of event.Records) {
    await processMessageAsync(record);
  }
  console.info("done");
};

async function processMessageAsync(record) {
  try {
    const message = JSON.stringify(record.Sns.Message);
    console.log(`Processed message ${message}`);
    await Promise.resolve(1); //Placeholder for actual async work
  } catch (err) {
    console.error("An error occurred");
    throw err;
  }
}
```
使用 TypeScript 将 SNS 事件与 Lambda 结合使用。  

```
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import { SNSEvent, Context, SNSHandler, SNSEventRecord } from "aws-lambda";

export const functionHandler: SNSHandler = async (
  event: SNSEvent,
  context: Context
): Promise<void> => {
  for (const record of event.Records) {
    await processMessageAsync(record);
  }
  console.info("done");
};

async function processMessageAsync(record: SNSEventRecord): Promise<any> {
  try {
    const message: string = JSON.stringify(record.Sns.Message);
    console.log(`Processed message ${message}`);
    await Promise.resolve(1); //Placeholder for actual async work
  } catch (err) {
    console.error("An error occurred");
    throw err;
  }
}
```

------
#### [ PHP ]

**适用于 PHP 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-sns-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
通过 PHP 将 SNS 事件与 Lambda 结合使用。  

```
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
<?php

/* 
Since native PHP support for AWS Lambda is not available, we are utilizing Bref's PHP functions runtime for AWS Lambda.
For more information on Bref's PHP runtime for Lambda, refer to: https://bref.sh/docs/runtimes/function

Another approach would be to create a custom runtime. 
A practical example can be found here: https://aws.amazon.com/blogs/apn/aws-lambda-custom-runtime-for-php-a-practical-example/
*/

// Additional composer packages may be required when using Bref or any other PHP functions runtime.
// require __DIR__ . '/vendor/autoload.php';

use Bref\Context\Context;
use Bref\Event\Sns\SnsEvent;
use Bref\Event\Sns\SnsHandler;

class Handler extends SnsHandler
{
    public function handleSns(SnsEvent $event, Context $context): void
    {
        foreach ($event->getRecords() as $record) {
            $message = $record->getMessage();

            // TODO: Implement your custom processing logic here
            // Any exception thrown will be logged and the invocation will be marked as failed

            echo "Processed Message: $message" . PHP_EOL;
        }
    }
}

return new Handler();
```

------
#### [ Python ]

**适用于 Python 的 SDK（Boto3）**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-sns-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
使用 Python 将 SNS 事件与 Lambda 结合使用。  

```
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
def lambda_handler(event, context):
    for record in event['Records']:
        process_message(record)
    print("done")

def process_message(record):
    try:
        message = record['Sns']['Message']
        print(f"Processed message {message}")
        # TODO; Process your record here
        
    except Exception as e:
        print("An error occurred")
        raise e
```

------
#### [ Ruby ]

**适用于 Ruby 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-sns-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
通过 Ruby 将 SNS 事件与 Lambda 结合使用。  

```
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
def lambda_handler(event:, context:)
  event['Records'].map { |record| process_message(record) }
end

def process_message(record)
  message = record['Sns']['Message']
  puts("Processing message: #{message}")
rescue StandardError => e
  puts("Error processing message: #{e}")
  raise
end
```

------
#### [ Rust ]

**适用于 Rust 的 SDK**  
 查看 GitHub，了解更多信息。在[无服务器示例](https://github.com/aws-samples/serverless-snippets/tree/main/integration-sns-to-lambda)存储库中查找完整示例，并了解如何进行设置和运行。
通过 Rust 将 SNS 事件与 Lambda 结合使用。  

```
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
use aws_lambda_events::event::sns::SnsEvent;
use aws_lambda_events::sns::SnsRecord;
use lambda_runtime::{run, service_fn, Error, LambdaEvent};
use tracing::info;

// Built with the following dependencies:
//  aws_lambda_events = { version = "0.10.0", default-features = false, features = ["sns"] }
//  lambda_runtime = "0.8.1"
//  tokio = { version = "1", features = ["macros"] }
//  tracing = { version = "0.1", features = ["log"] }
//  tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt"] }

async fn function_handler(event: LambdaEvent<SnsEvent>) -> Result<(), Error> {
    for event in event.payload.records {
        process_record(&event)?;
    }
    
    Ok(())
}

fn process_record(record: &SnsRecord) -> Result<(), Error> {
    info!("Processing SNS Message: {}", record.sns.message);

    // Implement your record handling code here.

    Ok(())
}

#[tokio::main]
async fn main() -> Result<(), Error> {
    tracing_subscriber::fmt()
        .with_max_level(tracing::Level::INFO)
        .with_target(false)
        .without_time()
        .init();

    run(service_fn(function_handler)).await
}
```

------

**创建函数**

1. 为项目创建一个目录，然后切换到该目录。

   ```
   mkdir sns-tutorial
   cd sns-tutorial
   ```

1. 将示例 JavaScript 代码复制到名为 `index.js` 的新文件中。

1. 使用以下 `zip` 命令创建部署包。

   ```
   zip function.zip index.js
   ```

1. 运行以下 AWS CLI 命令，在**账户 B** 中创建 Lambda 函数。

   ```
   aws lambda create-function --function-name Function-With-SNS \
       --zip-file fileb://function.zip --handler index.handler --runtime nodejs24.x \
       --role arn:aws:iam::<AccountB_ID>:role/lambda-sns-role  \
       --timeout 60 --profile accountB
   ```

   您应该可以看到类似于如下所示的输出内容。

   ```
   {
       "FunctionName": "Function-With-SNS",
       "FunctionArn": "arn:aws:lambda:us-west-2:123456789012:function:Function-With-SNS",
       "Runtime": "nodejs24.x",
       "Role": "arn:aws:iam::123456789012:role/lambda_basic_role",
       "Handler": "index.handler",
       ...
       "RuntimeVersionConfig": {
           "RuntimeVersionArn": "arn:aws:lambda:us-west-2::runtime:7d5f06b69c951da8a48b926ce280a9daf2e8bb1a74fc4a2672580c787d608206"
       }
   }
   ```

1. 记下函数的 Amazon 资源名称（ARN）。稍后在本教程中添加权限以允许 Amazon SNS 调用函数时，将需要使用此 ARN。

## 向函数添加权限（账户 B）
<a name="with-sns-create-function-permissions"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-sns-tutorial/sns_tut_steps_4.png)


要让 Amazon SNS 调用函数，您需要在[基于资源的策略](access-control-resource-based.md)语句中向其授予权限。您可以使用 AWS CLI `add-permission` 命令添加此语句。

**授予 Amazon SNS 调用函数的权限**
+ 在**账户 B** 中，使用之前记下的 Amazon SNS 主题的 ARN 运行以下 AWS CLI 命令。

  ```
  aws lambda add-permission --function-name Function-With-SNS \
      --source-arn arn:aws:sns:us-east-1:<AccountA_ID>:sns-topic-for-lambda \
      --statement-id function-with-sns --action "lambda:InvokeFunction" \
      --principal sns.amazonaws.com --profile accountB
  ```

  您应该可以看到类似于如下所示的输出内容。

  ```
  {
      "Statement": "{\"Condition\":{\"ArnLike\":{\"AWS:SourceArn\":
        \"arn:aws:sns:us-east-1:<AccountA_ID>:sns-topic-for-lambda\"}},
        \"Action\":[\"lambda:InvokeFunction\"],
        \"Resource\":\"arn:aws:lambda:us-east-1:<AccountB_ID>:function:Function-With-SNS\",
        \"Effect\":\"Allow\",\"Principal\":{\"Service\":\"sns.amazonaws.com\"},
        \"Sid\":\"function-with-sns\"}"
  }
  ```

**注意**  
如果在[选择加入型 AWS 区域](https://docs.aws.amazon.com/accounts/latest/reference/manage-acct-regions.html) 中托管具有 Amazon SNS 主题的账户，则需要在主体中指定区域。例如，假设您正在亚太地区（香港）区域使用一个 Amazon SNS 主题，则需要为主体指定 `sns.ap-east-1.amazonaws.com`，而不是 `sns.amazonaws.com`。

## 授予 Amazon SNS 订阅的跨账户权限（账户 A）
<a name="with-sns-subscription-grant-permission"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-sns-tutorial/sns_tut_steps_5.png)


要让**账户 B** 中的 Lambda 函数订阅您在**账户 A** 中创建的 Amazon SNS 主题，您需要向**账户 B** 授予订阅主题的权限。您可以使用 AWS CLI `add-permission` 命令授予此权限。

**授予账户 B 订阅主题的权限**
+ 在**账户 A** 中，运行以下 AWS CLI 命令。使用之前记下的 Amazon SNS 主题的 ARN。

  ```
  aws sns add-permission --label lambda-access --aws-account-id <AccountB_ID> \
      --topic-arn arn:aws:sns:us-east-1:<AccountA_ID>:sns-topic-for-lambda \  
      --action-name Subscribe ListSubscriptionsByTopic --profile accountA
  ```

## 创建订阅（账户 B）
<a name="with-sns-create-subscription"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-sns-tutorial/sns_tut_steps_6.png)


在**账户 B** 中，您现在将 Lambda 函数订阅到教程开始时您在**账户 A** 中创建的 Amazon SNS 主题。当消息发送到该主题 (`sns-topic-for-lambda`) 后，Amazon SNS 会调用**账户 B** 中的 Lambda 函数 `Function-With-SNS`。

**创建订阅**
+ 在**账户 B** 中，运行以下 AWS CLI 命令。使用您在其中创建主题的默认区域以及主题和 Lambda 函数的 ARN。

  ```
  aws sns subscribe --protocol lambda \
      --region us-east-1 \
      --topic-arn arn:aws:sns:us-east-1:<AccountA_ID>:sns-topic-for-lambda \
      --notification-endpoint arn:aws:lambda:us-east-1:<AccountB_ID>:function:Function-With-SNS \
      --profile accountB
  ```

  您应该可以看到类似于如下所示的输出内容。

  ```
  {
      "SubscriptionArn": "arn:aws:sns:us-east-1:<AccountA_ID>:sns-topic-for-lambda:5d906xxxx-7c8x-45dx-a9dx-0484e31c98xx"
  }
  ```

## 将消息发布到主题（账户 A 和账户 B）
<a name="with-sns-publish-message"></a>

![\[\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/services-sns-tutorial/sns_tut_steps_7.png)


**账户 B** 中的 Lambda 函数现已订阅**账户 A** 中的 Amazon SNS 主题，现在可以通过将消息发布到主题来测试设置了。要确认 Amazon SNS 是否已调用 Lambda 函数，可以使用 CloudWatch Logs 查看函数的输出。

**将消息发布到主题并查看函数的输出**

1. 在文本文件中输入 `Hello World`，并将其另存为 `message.txt`。

1. 在保存文本文件的同一目录中，在**账户 A** 中运行以下 AWS CLI 命令。将 ARN 用于您自己的主题。

   ```
   aws sns publish --message file://message.txt --subject Test \
       --topic-arn arn:aws:sns:us-east-1:<AccountA_ID>:sns-topic-for-lambda \
       --profile accountA
   ```

   这将返回一个具有唯一标识符的消息 ID，表明 Amazon SNS 服务已接受该消息。然后，Amazon SNS 则尝试将消息传输给主题订阅用户。要确认 Amazon SNS 是否已调用 Lambda 函数，可以使用 CloudWatch Logs 查看函数的输出：

1. 在**账户 B** 中，打开 Amazon CloudWatch 控制台的[日志组](https://console.aws.amazon.com/cloudwatch/home#logsV2:log-groups)页面。

1. 选择函数 (`/aws/lambda/Function-With-SNS`) 的日志组。

1. 选择最新的日志流。

1. 如果函数已正确调用，您将看到类似于以下内容的输出，其中会显示发布到主题的消息内容。

   ```
   2023-07-31T21:42:51.250Z c1cba6b8-ade9-4380-aa32-d1a225da0e48 INFO Processed message Hello World
   2023-07-31T21:42:51.250Z c1cba6b8-ade9-4380-aa32-d1a225da0e48 INFO done
   ```

## 清除资源
<a name="cleanup"></a>

除非您想要保留为本教程创建的资源，否则可立即将其删除。通过删除您不再使用的 AWS 资源，可防止您的 AWS 账户 产生不必要的费用。

在**账户 A** 中，清理您的 Amazon SNS 主题。

**删除 Amazon SNS 主题**

1. 打开 Amazon SNS 控制台中的 [Topics（主题）页面](https://console.aws.amazon.com//sns/home#topics:)。

1. 选择您已创建的主题。

1. 选择**删除**。

1. 在文本输入字段中输入 **delete me**。

1. 选择**删除**。

在**账户 B** 中，清理您的执行角色、Lambda 函数和 Amazon SNS 订阅。

**删除执行角色**

1. 打开 IAM 控制台的[角色页面](https://console.aws.amazon.com/iam/home#/roles)。

1. 选择您创建的执行角色。

1. 选择**删除**。

1. 在文本输入字段中输入角色名称，然后选择**删除**。

**删除 Lambda 函数**

1. 打开 Lamba 控制台的 [Functions（函数）页面](https://console.aws.amazon.com/lambda/home#/functions)。

1. 选择您创建的函数。

1. 依次选择**操作**和**删除**。

1. 在文本输入字段中键入 **confirm**，然后选择 **Delete**（删除）。

**删除 Amazon SNS 订阅**

1. 在 Amazon SNS 控制台中打开 [Subscription（订阅）页面](https://console.aws.amazon.com//sns/home#subscriptions:)。

1. 选择您已创建的订阅。

1. 选择 **Delete**（删除），**Delete**（删除）。