

# 使用 Lambda 递归循环检测来防止无限循环
<a name="invocation-recursion"></a>

当您配置 Lambda 函数输出到调用该函数的同一服务或资源时，就可能会创建无限递归循环。例如，Lambda 函数可能会向 Amazon Simple Queue Service（Amazon SQS）队列写入一条消息，该队列随即调用同一函数。此调用导致该函数向队列写入另一条消息，而队列反过来再次调用该函数。

意外发生的递归循环可能会让您的 AWS 账户 产生意外费用。循环还可能导致 Lambda [扩展](lambda-concurrency.md)并使用您账户的所有可用并发。为了帮助减轻意外循环的影响，Lambda 可以在某些类型的递归循环发生后不久将其检测出。默认情况下，在检测到递归循环时，Lambda 会停止调用函数并向您发送通知。如果您的设计有意使用递归模式，则可以更改函数的默认配置，以允许其以递归方式调用。请参阅[允许 Lambda 函数在递归循环中运行](#invocation-recursion-disable)了解更多信息。

**Topics**
+ [了解递归循环检测](#invocation-recursion-concepts)
+ [受支持的 AWS 服务 和开发工具包](#invocation-recursion-supported)
+ [递归循环通知](#invocation-recursion-monitoring)
+ [响应递归循环检测通知](#invocation-recursion-responding)
+ [允许 Lambda 函数在递归循环中运行](#invocation-recursion-disable)
+ [支持 Lambda 递归循环检测的区域](#invocation-recursion-regions)

## 了解递归循环检测
<a name="invocation-recursion-concepts"></a>

Lambda 中的递归循环检测通过跟踪事件来工作。Lambda 是一种事件驱动型计算服务，可在某些事件发生时运行您的函数代码。例如，将项目添加到 Amazon SQS 队列或 Amazon Simple Notiﬁcation Service（Amazon SNS）主题时，就会如此。Lambda 将事件作为 JSON 对象传递给您的函数，其中包含有关系统状态变化的信息。如果某一事件导致您的函数运行时，这就称为*调用*。

为了检测递归循环，Lambda 会使用 [AWS X-Ray](https://docs.aws.amazon.com/xray/latest/devguide/aws-xray.html) 跟踪标头。当[支持递归循环检测的 AWS 服务](#invocation-recursion-supportedservices) 将事件发送到 Lambda 时，这些事件将自动使用元数据进行注释。当您的 Lambda 函数使用[支持的 AWS 开发工具包版本](#invocation-recursion-supportedsdks)将其中一个事件写入另一个支持的 AWS 服务 时，就会更新此元数据。更新后的元数据包括事件调用该函数的次数计数。

**注意**  
您无需启用 X-Ray 主动追踪，即可使用此功能。默认情况下，所有 AWS 客户都启用递归循环检测。使用该功能不会产生任何费用。

*请求链*是由同一触发事件引起的一系列 Lambda 调用。例如，假设 Amazon SQS 队列调用了您的 Lambda 函数。然后，Lambda 函数将处理过的事件发送回同一 Amazon SQS 队列，该队列将再次调用您的函数。在此示例中，函数的每次调用都属于同一请求链。

如果您的函数在同一请求链中被调用的次数超过 16 次，Lambda 会自动停止该请求链中的下一次函数调用并向您发送通知。如果您的函数配置了多个触发器，来自其他触发器的调用则不会受到影响。

**注意**  
即使在源队列的重新驱动策略的 `maxReceiveCount` 设置高于 16 时，Lambda 递归保护也不会阻止 Amazon SQS 在检测到递归循环并终止后重试消息。当 Lambda 检测到递归循环并丢弃后续调用时，它会向事件源映射返回 `RecursiveInvocationException`。这会增加消息的 `receiveCount` 值。Lambda 会继续重试该消息，并继续阻止函数调用，直到 Amazon SQS 确定已超出 `maxReceiveCount` 并将消息发送到配置的死信队列。

如果您为函数配置了[失败时的目标](invocation-async-retain-records.md#invocation-async-destinations)或[死信队列](invocation-async-retain-records.md#invocation-dlq)，则 Lambda 还会将已停止调用的事件发送到您的目标或死信队列。为函数配置目标或死信队列时，确保不要使用函数也在使用的事件触发器或事件源映射。如果您将事件发送到调用函数的同一资源，则可以创建另一个递归循环，此循环也将终止。如果您选择退出递归循环检测，则不会终止此循环。

## 受支持的 AWS 服务 和开发工具包
<a name="invocation-recursion-supported"></a>

Lambda 只能检测包含某些受支持的 AWS 服务 的递归循环。为了检测到递归循环，您的函数还必须使用一种受支持的 AWS 开发工具包。

### 支持的 AWS 服务
<a name="invocation-recursion-supportedservices"></a>

Lambda 目前可检测您的函数、Amazon SQS、Amazon S3 和 Amazon SNS 之间的递归循环。Lambda 还会检测仅由 Lambda 函数组成的循环，这些函数可以同步或异步地相互调用。下图显示了 Lambda 可以检测的一些循环示例：

![\[Lambda 函数、Amazon SNS、Amazon S3 和 Amazon SQS 队列之间的递归循环图。\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/RunawayWorkloadDetected_v3.png)


如果 Amazon DynamoDB 等其他 AWS 服务构成循环的一部分，Lambda 目前无法对其进行检测和阻止。

由于 Lambda 目前仅检测涉及 Amazon SQS、Amazon S3 和 Amazon SNS 的递归循环，因此涉及其他 AWS 服务的循环仍有可能导致您的 Lambda 函数遭意外使用。

为防止 AWS 账户 产生意外费用，我们建议您配置 [Amazon CloudWatch 警报](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/AlarmThatSendsEmail.html)，提醒自己注意异常使用模式。例如，您可以将 CloudWatch 配置为在 Lambda 函数并发或调用出现峰值时向自己发送通知。您还可以配置[账单警报](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/monitor_estimated_charges_with_cloudwatch.html)，以便在账户中的支出超过指定的阈值时通知您。您也可以使用 [AWS Cost Anomaly Detection](https://docs.aws.amazon.com/cost-management/latest/userguide/manage-ad.html) 来提醒自己注意异常的计费模式。

### 受支持的 AWS 开发工具包
<a name="invocation-recursion-supportedsdks"></a>

要让 Lambda 检测递归循环，您的函数必须使用以下开发工具包版本中的一种版本或更高版本：


| 运行时系统 | 所需 AWS 开发工具包的最低版本 | 
| --- | --- | 
|  Node.js  |  2.1147.0（开发工具包版本 2） 3.105.0（开发工具包版本 3）  | 
|  Python  |  1.24.46（boto3） 1.27.46（botocore）  | 
|  Java 8 和 Java 11  |  2.17.135  | 
|  Java 17  |  2.20.81  | 
|  Java 21  |  2.21.24  | 
|  .NET  |  3.7.293.0  | 
|  Ruby  |  3.134.0  | 
|  PHP  |  3.232.0  | 
|  Go  |  V2 SDK 1.57.0  | 

某些 Lambda 运行时系统（例如 Python 和 Node.js）包含 AWS 开发工具包的一个版本。如果函数运行时系统中包含的开发工具包版本低于所需的最低版本，您可以将受支持的开发工具包版本添加到函数的部署包中。您还可以使用 [Lambda 层](chapter-layers.md)将受支持的开发工具包版本添加到自己的函数中。有关每个 Lambda 运行时系统包含的开发工具包的列表，请参阅 [Lambda 运行时](lambda-runtimes.md)。

## 递归循环通知
<a name="invocation-recursion-monitoring"></a>

当 Lambda 停止递归循环时，您会通过 [Health Dashboard](https://aws.amazon.com/premiumsupport/technology/aws-health-dashboard/) 或电子邮件收到通知。您还可以使用 CloudWatch 指标来监控 Lambda 已停止的递归的调用次数。

### Health Dashboard 通知
<a name="invocation-recursion-phd"></a>

当 Lambda 停止递归调用时，Health Dashboard 会在**账户运行状况**页面的[未决问题和近期问题](https://health.aws.amazon.com/health/home#/account/dashboard/open-issues)下显示一条通知。请注意，在 Lambda 停止递归调用后，最多可能需要 3.5 小时才能显示此通知。有关在 Health Dashboard 中查看账户事件的更多信息，请参阅《AWS 运行状况用户指南》**中的 [Getting started with your AWS Health Dashboard – Your account health](https://docs.aws.amazon.com/health/latest/ug/getting-started-health-dashboard.html)。

### 电子邮件警报
<a name="invocation-recursion-email"></a>

在首次停止函数的递归调用时，Lambda 会向您发送电子邮件警报。Lambda 每 24 小时最多为您 AWS 账户 中的每个函数发送一封电子邮件。在 Lambda 发送电子邮件通知后，即使 Lambda 停止对该函数的进一步递归调用，您也不会在接下来的 24 小时内再收到该函数的更多电子邮件。请注意，在 Lambda 停止递归调用后，最多可能需要 3.5 小时您才会收到此通知。

Lambda 会向您 AWS 账户 的主要账户联系人和备用运营联系人发送递归循环电子邮件警报。有关查看或更新账户中电子邮件地址的信息，请参阅《AWS 一般参考》**中的 [Updating contact information](https://docs.aws.amazon.com/accounts/latest/reference/manage-acct-update-contact.html)。

### Amazon CloudWatch 指标
<a name="invocation-recursion-cloudwatch"></a>

[CloudWatch 指标](monitoring-metrics-types.md) `RecursiveInvocationsDropped` 会记录 Lambda 因您的函数在单个请求链中被调用次数超过约 16 次而停止的函数调用次数。Lambda 会在停止递归调用后立即发出此指标。要查看此指标，请按照[在 CloudWatch 控制台上查看指标](https://docs.aws.amazon.com/lambda/latest/dg/monitoring-metrics.html#monitoring-metrics-console)的说明进行操作，然后选择指标 `RecursiveInvocationsDropped`。

## 响应递归循环检测通知
<a name="invocation-recursion-responding"></a>

当同一触发事件调用您的函数超过约 16 次时，Lambda 会停止该事件的下一次函数调用，以便中断递归循环。为防止 Lambda 中断的递归循环再次出现，请执行以下操作：
+ 将函数的可用[并发](lambda-concurrency.md)减少到零，即可限制未来发生的所有调用。
+ 移除或禁用会调用函数的触发器或事件源映射。
+ 识别并修复会将事件写回会调用函数的 AWS 资源的代码缺陷。在使用变量定义函数的事件源和目标时，就会出现常见的缺陷来源。请检查并确认您为两个变量使用的是不同值。

此外，如果您的 Lambda 函数的事件源是 Amazon SQS 队列，则可以考虑在源队列上[配置死信队列](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-configure-dead-letter-queue.html)。

**注意**  
确保在源队列上配置死信队列，而不是在 Lambda 函数上配置。您在函数上配置的死信队列用于函数的[异步调用队列](invocation-async.md)，而不是用于事件源队列。

如果事件源是 Amazon SNS 主题，请考虑为您的函数添加[失败时的目标](invocation-async-retain-records.md#invocation-async-destinations)。

**将函数的可用并发减少到零（控制台）**

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

1. 选择函数的名称。

1. 选择**限制**。

1. 在**限制函数**对话框中，选择**确认**。

**删除函数的触发器或事件源映射（控制台）**

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

1. 选择函数的名称。

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

1. 在**触发器**下，选择要删除的触发器或事件源映射，然后选择**删除**。

1. 在**删除触发器**对话框中，选择**删除**。

**禁用函数的事件源映射（AWS CLI）**

1. 要找到要禁用的事件源映射的 UUID，请运行 AWS Command Line Interface（AWS CLI）[list-event-source-mappings](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/list-event-source-mappings.html) 命令。

   ```
   aws lambda list-event-source-mappings
   ```

1. 要禁用事件源映射，请运行以下 AWS CLI [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 --function-name MyFunction \
   --uuid a1b2c3d4-5678-90ab-cdef-EXAMPLE11111 --no-enabled
   ```

## 允许 Lambda 函数在递归循环中运行
<a name="invocation-recursion-disable"></a>

如果您的设计有意使用递归循环，则可以配置 Lambda 函数以允许递归调用该函数。我们建议避免在设计中使用递归循环。实施错误可能导致递归调用使用您 AWS 账户 的所有可用并发量，并向您的账户收取意外费用。

**重要**  
如果您使用递归循环，请谨慎对待。实施最佳实践防护轨道，以最大限度地降低实施错误的风险。要了解使用递归模式的最佳实践的更多信息，请参阅 Serverless Land 中的 [Recursive patterns that cause run-away Lambda functions](https://serverlessland.com/content/service/lambda/guides/aws-lambda-operator-guide/recursive-runaway)。

您可以使用 Lambda 控制台、AWS Command Line Interface（AWS CLI）和 [PutFunctionRecursionConfig](https://docs.aws.amazon.com//lambda/latest/api/API_PutFunctionRecursionConfig.html) API 将函数配置为允许递归循环。您也可以在 AWS SAM 和 CloudFormation 中配置函数的递归循环检测设置。

默认情况下，Lambda 会检测并终止递归循环。除非您的设计有意使用递归循环，否则我们建议您不要更改函数的默认配置。

请注意，当您将函数配置为允许递归循环时，不会发出 [CloudWatch 指标](monitoring-metrics-types.md#invocation-metrics) `RecursiveInvocationsDropped`。

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

**允许函数在递归循环中运行（控制台）**

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

1. 选择函数的名称，打开函数详细信息页面。

1. 选择**配置**选项卡，然后选择**并发和递归检测**。

1. 在**递归循环检测**旁边，选择**编辑**。

1. 选择**允许递归循环**。

1. 选择**保存**。

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

您可以使用 [PutFunctionRecursionConfig](https://docs.aws.amazon.com/lambda/latest/api/API_PutFunctionRecursionConfig.html) API 来允许在递归循环中调用您的函数。为递归循环参数指定 `Allow`。例如，您可以使用 `put-function-recursion-config` AWS CLI 命令调用此 API：

```
aws lambda put-function-recursion-config --function-name yourFunctionName --recursive-loop Allow
```

------

您可以将函数的配置更改回默认设置，以便 Lambda 在检测到递归循环时将其终止。使用 Lambda 控制台或 AWS CLI 编辑函数的配置。

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

**配置函数以终止递归循环（控制台）**

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

1. 选择函数的名称，打开函数详细信息页面。

1. 选择**配置**选项卡，然后选择**并发和递归检测**。

1. 在**递归循环检测**旁边，选择**编辑**。

1. 选择**终止递归循环**。

1. 选择**保存**。

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

您可以使用 [PutFunctionRecursionConfig](https://docs.aws.amazon.com/lambda/latest/api/API_PutFunctionRecursionConfig.html) API 来配置您的函数，这样 Lambda 在检测到递归循环时将其终止。为递归循环参数指定 `Terminate`。例如，您可以使用 `put-function-recursion-config` AWS CLI 命令调用此 API：

```
aws lambda put-function-recursion-config --function-name yourFunctionName --recursive-loop Terminate
```

------

## 支持 Lambda 递归循环检测的区域
<a name="invocation-recursion-regions"></a>

Lambda 递归循环检测支持在除墨西哥（中部）和亚太地区（新西兰）以外的[所有商业区域](https://docs.aws.amazon.com/general/latest/gr/glos-chap.html#region)使用。