

# Lambda 中的执行问题疑难解答
<a name="troubleshooting-execution"></a>

当 Lambda 运行时运行函数代码时，可能会在已经处理事件一段时间的函数实例上处理事件，或者可能需要初始化一个新实例。在函数初始化期间、处理程序代码处理事件时或者当函数返回（或无法返回）响应，可能会发生错误。

函数执行错误可能是由您的代码、函数配置、下游资源或权限中的问题引起。如果您直接调用函数，则会在 Lambda 的响应中看到函数错误。如果您使用事件源映射或通过其他服务异步调用函数，则可能会在日志、死信队列或失败时的目标中找到错误。错误处理选项和重试行为因调用函数的方式和错误类型而异。

当您的函数代码或 Lambda 运行时返回错误时，来自 Lambda 的响应中的状态代码为“200 OK”。响应中是否存在错误由名为 `X-Amz-Function-Error` 的标头指示。400 和 500 系列状态代码保留用于[调用错误](troubleshooting-invocation.md)。

**Topics**
+ [Lambda：使用 Visual Studio Code 进行远程调试](#troubleshooting-execution-remote-debugging)
+ [Lambda：执行时间过长](#troubleshooting-execution-toolong)
+ [Lambda：意外的事件有效载荷](#troubleshooting-execution-unexpected-payload)
+ [Lambda：意外的大型有效载荷大小](#troubleshooting-execution-large-payload)
+ [Lambda：JSON 编码和解码错误](#troubleshooting-execution-json-encoding)
+ [Lambda：未显示日志或跟踪](#troubleshooting-execution-logstraces)
+ [Lambda：并非所有我的函数的日志都会出现](#troubleshooting-execution-missinglogs)
+ [Lambda：函数在执行完成之前返回](#troubleshooting-execution-unfinished)
+ [Lambda：运行意外函数版本或别名](#unintended-function)
+ [Lambda：检测无限循环](#infinite-loops)
+ [常规：下游服务不可用](#downstream-unavailability)
+ [AWS 开发工具包：版本和更新](#troubleshooting-execution-versions)
+ [Python：库未正确加载](#troubleshooting-execution-libraries)
+ [Java：从 Java 11 更新到 Java 17 后，函数处理事件所需的时间更长](#troubleshooting-execution-java-perf)
+ [Kafka: 错误处理和重试配置问题](#troubleshooting-kafka-error-handling)

## Lambda：使用 Visual Studio Code 进行远程调试
<a name="troubleshooting-execution-remote-debugging"></a>

**问题：***对实际 AWS 环境中难以复杂的 Lambda 函数行为进行故障排除*

Lambda 通过 AWS Toolkit for Visual Studio Code 提供远程调试功能。有关设置和一般说明，请参阅[使用 Visual Studio Code 远程调试 Lambda 函数](debugging.md)。

有关故障排除、高级使用案例和区域可用性的详细说明，请参阅《AWS Toolkit for Visual Studio Code 用户指南》中的[远程调试 Lambda 函数](https://docs.aws.amazon.com/toolkit-for-vscode/latest/userguide/lambda-remote-debug.html)。

## Lambda：执行时间过长
<a name="troubleshooting-execution-toolong"></a>

**问题：***函数执行时间太长。*

如果您的代码在 Lambda 中运行所花费的时间长于在本地计算机上运行所花费的时间，则可能受到对该函数可用的内存或处理能力的限制。[使用额外内存配置函数](configuration-memory.md)以增加内存和 CPU。

## Lambda：意外的事件有效载荷
<a name="troubleshooting-execution-unexpected-payload"></a>

**问题：***与 JSON 格式错误或数据验证不足相关的函数错误。*

所有 Lambda 函数都会在处理程序的第一个参数中接收事件有效载荷。事件有效载荷是一个 JSON 结构，可能包含数组和嵌套元素。

当上游服务未使用强大的流程检查 JSON 结构时，可能会出现格式错误的 JSON。当服务连接文本字符串或嵌入未经清理的用户输入时，就会发生这种情况。也会经常序列化 JSON，以便在服务之间传递。始终以 JSON 的生产者和使用者的身份解析 JSON 结构，以确保结构有效。

同样，未能检查事件有效载荷中的值范围也可能导致错误。此示例显示了计算预扣税的函数：

```
exports.handler = async (event) => {
    let pct = event.taxPct
    let salary = event.salary

    // Calculate % of paycheck for taxes
    return (salary * pct)
}
```

此函数使用事件有效载荷中的工资和税率来执行计算。但是，该代码未能检查属性是否存在。也未能检查数据类型或确保界限，例如确保税收百分比介于 0 与 1 之间。因此，这些界限以外的值会产生毫无意义的结果。类型不正确或属性缺少会导致运行时错误。

创建测试，以确保您的函数可以处理更大的有效载荷。Lambda 事件有效载荷的最大大小为 1 MB。根据内容，有效载荷越大，可能意味着传递给函数的项目越多，或者 JSON 属性中嵌入的二进制数据越多。在这两种情况下，此项都可能导致对 Lambda 函数进行更多处理。

较大的有效载荷也可能导致超时。例如，Lambda 函数每 100 毫秒处理一条记录，且超时时间为 3 秒。成功处理有效载荷中的 0-29 个项目。但是，一旦有效载荷包含超过 30 个项目，该函数就会超时并引发错误。为避免这种情况，请确保设置超时，以处理预期最大数量的项目所需的额外处理时间。

## Lambda：意外的大型有效载荷大小
<a name="troubleshooting-execution-large-payload"></a>

**问题：***由于有效载荷较大，函数超时或导致错误。*

较大的有效载荷也可能导致超时和错误。我们建议创建测试以确保您的函数能够处理最大预期有效载荷，并确保正确设置函数超时。

此外，某些事件有效载荷可能包含指向其他资源的指针。例如，内存为 128 MB 的 Lambda 函数可以针对作为对象存储在 S3 中的 JPG 文件执行图像处理。对于较小的图像文件，该函数可以按预期运行。但是，当提供较大的 JPG 文件作为输入时，Lambda 函数会因内存不足而引发错误。为避免这种情况，测试案例应包括预期数据大小上限的示例。该代码还应验证有效载荷大小。

## Lambda：JSON 编码和解码错误
<a name="troubleshooting-execution-json-encoding"></a>

**问题：***解析 JSON 输入时出现 NoSuchKey 异常。*

进行检查，确保在正确处理 JSON 属性。例如，对于 S3 生成的事件，`s3.object.key` 属性包含了 URL 编码的对象密钥名称。许多函数将此属性处理为文本，以加载引用的 S3 对象：

**Example**  

```
const originalText = await s3.getObject({
  Bucket: event.Records[0].s3.bucket.name,
  Key: event.Records[0].s3.object.key
}).promise()
```

此代码适用于密钥名称 `james.jpg`，但是当名称为 `james beswick.jpg` 时会引发 `NoSuchKey` 错误。由于 URL 编码会转换密钥名称中的空格和其他字符，因此，您必须确保函数在使用以下数据之前解码了密钥：

**Example**  

```
const originalText = await s3.getObject({
  Bucket: event.Records[0].s3.bucket.name,
  Key: decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, " "))
}).promise()
```

## Lambda：未显示日志或跟踪
<a name="troubleshooting-execution-logstraces"></a>

**问题：***日志未显示在 CloudWatch Logs 中。*

**问题：***跟踪未显示在 AWS X-Ray 中。*

您的函数需要权限才能调用 CloudWatch Logs 和 X-Ray。更新函数的[执行角色](lambda-intro-execution-role.md)以向其授予权限。添加以下托管策略以启用日志和跟踪。
+ **AWSLambdaBasicExecutionRole**
+ **AWSXRayDaemonWriteAccess**

向函数添加权限时，请同时简单更新其代码或配置。这会强制停止并替换函数的具有过时凭证的运行实例。

**注意**  
函数调用后，日志可能需要 5 到 10 分钟才能显示。

## Lambda：并非所有我的函数的日志都会出现
<a name="troubleshooting-execution-missinglogs"></a>

**问题：***尽管我拥有正确权限，但 CloudWatch Logs 中缺少函数日志*

如果您的 AWS 账户 达到了其 [CloudWatch Logs 限额限制](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/cloudwatch_limits_cwl.html)，则 CloudWatch 就会对函数日志记录进行节流。发生这种情况时，您函数输出的某些日志可能不会出现在 CloudWatch Logs 中。

如果函数输出日志的速度过快，Lambda 无法对其进行处理，也会导致日志输出无法显示在 CloudWatch Logs 中。当 Lambda 无法按照函数生成日志的速度向 CloudWatch 发送日志时，它会丢弃日志以防止函数的执行速度减慢。单个日志流的日志吞吐量超过 2 MB/s 时，预计会持续观察到删除的日志。

如果函数配置为使用 [JSON 格式的日志](monitoring-cloudwatchlogs-logformat.md)，则 Lambda 会在丢弃日志时尝试向 CloudWatch Logs 发送 [`logsDropped`](telemetry-schema-reference.md#platform-logsDropped) 事件。但是，当 CloudWatch 对函数的日志记录进行节流时，此事件可能无法到达 CloudWatch Logs，因此 Lambda 丢弃日志时您并不始终会看到记录。

要检查 AWS 账户 是否已达到其 CloudWatch Logs 限额限制，请执行以下操作：

1. 打开[服务限额控制台](https://console.aws.amazon.com/servicequotas)。

1. 在导航窗格中，选择 **AWS 服务**。

1. 从 **AWS 服务**列表中，搜索 Amazon CloudWatch Logs。

1. 在**服务限额**列表中，选择 `CreateLogGroup throttle limit in transactions per second`、`CreateLogStream throttle limit in transactions per second` 和 `PutLogEvents throttle limit in transactions per second` 限额以查看利用率。

您还可以设置 CloudWatch 警报，以便在账户利用率超过您为这些限额指定的限制时提醒您。请参阅[根据静态阈值创建 CloudWatch 告警](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/ConsoleAlarms.html)以了解更多信息。

如果 CloudWatch Logs 的默认限额限制不足以满足您的用例，则可以[请求增加限额](https://docs.aws.amazon.com/servicequotas/latest/userguide/request-quota-increase.html)。

## Lambda：函数在执行完成之前返回
<a name="troubleshooting-execution-unfinished"></a>

**问题：(Node.js)** *函数在代码完成执行之前返回*

许多库（包括 AWS 开发工具包）都是异步操作。当您进行网络调用或执行其他需要等待响应的操作时，库返回一个名为 promise 的对象，该对象在后台跟踪操作的进度。

要等待 promise 解析为响应，请使用 `await` 关键字。这会阻止您的处理程序代码执行，直到将 promise 解析为包含响应的对象。如果您不需要在代码中使用来自响应的数据，则可以直接将 promise 返回到运行时。

一些库不返回 promise，但可以包装到返回 promise 的代码中。有关更多信息，请参阅 [定义采用 Node.js 的 Lambda 函数处理程序](nodejs-handler.md)。

## Lambda：运行意外函数版本或别名
<a name="unintended-function"></a>

**问题：***未调用函数版本或别名*

当您在控制台中或使用 AWS SAM 发布新的 Lambda 函数时，最新的代码版本由 `$LATEST` 表示。默认情况下，未指定版本或别名的调用会自动将函数代码的 `$LATEST` 版本作为目标。

如果您使用特定的函数版本或别名，则这些版本或别名是除 `$LATEST` 之外函数的不可变已发布版本。对这些函数进行问题排查时，首先确定调用方是否调用了预期的版本或别名。可以通过检查函数日志来做到这一点。被调用的函数的版本始终显示在 START 日志行中：

![\[调试操作图 1\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/debugging-ops-figure-1.png)


## Lambda：检测无限循环
<a name="infinite-loops"></a>

**问题：***与 Lambda 函数相关的无限循环模式*

Lambda 函数中有两种类型的无限循环。第一个发生于函数自身内部，是由一个永不退出的循环所致。只有函数超时，调用才会结束。您可以通过监控超时然后修复循环行为来发现这些问题。

第二种循环是 Lambda 函数与其他 AWS 资源之间的循环。当来自 S3 存储桶等资源的事件调用 Lambda 函数时，就会发生此情况，然后该函数与相同的源资源交互以触发另一个事件。这会再次调用该函数，进而创建与同一 S3 存储桶的另一个交互，依此类推。这些类型的循环可能由多种不同的 AWS 事件源引起，包括 Amazon SQS 队列和 DynamoDB 表。可以使用[递归循环检测](invocation-recursion.md)来识别这些模式。

![\[调试操作图 2\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/debugging-ops-figure-2.png)


您可以确保 Lambda 函数写入与使用资源不同的资源，从而避免这些循环。如果您必须将数据发布回使用的资源，则请确保新数据不会触发相同的事件。或者，使用[事件筛选](invocation-eventfiltering.md)。例如，以下是针对使用 S3 和 DynamoDB 资源的无限循环提出的两个解决方案：
+ 如果您回写到同一 S3 存储桶，请使用与事件触发器不同的前缀或后缀。
+ 如果您将项目写入同一 DynamoDB 表，则请包含使用的 Lambda 函数可以筛选的属性。如果 Lambda 找到该属性，则不会导致再次调用。

## 常规：下游服务不可用
<a name="downstream-unavailability"></a>

**问题：***您的 Lambda 函数所依赖的下游服务不可用*

对于调用第三方端点或其他下游资源的 Lambda 函数，请确保它们能够处理服务错误和超时。这些下游资源可能具有可变的响应时间，或者可能由于服务中断而变得不可用。根据实施，如果服务的错误响应未在函数代码中处理，则这些下游错误可能会显示为 Lambda 函数超时或异常。

每当函数依赖于下游服务（例如 API 调用）时，应实施适当的错误处理和重试逻辑。对于关键服务，Lambda 函数应将指标或日志发布到 CloudWatch。例如，如果第三方支付 API 不可用，则 Lambda 函数可以记录此信息。然后，您可以设置 CloudWatch 警报来发送与这些错误相关的通知。

由于 Lambda 可以快速扩展，非无服务器下游服务可能难以应对流量激增。有三种常见的方法可处理此问题：
+  **缓存** – 如果第三方服务返回的值不经常更改，可以考虑缓存这些值的结果。您可以将这些值存储在函数或其他服务的全局变量中。例如，可以将来自 Amazon RDS 实例的产品列表查询的结果保存在函数内一段时间，以防止冗余查询。
+  **加入队列** – 保存或更新数据时，在 Lambda 函数与资源之间添加 Amazon SQS 队列。在下游服务处理消息时，队列可以持久地保存数据。
+  **代理** – 若通常使用长期连接（例如对于 Amazon RDS 实例），应使用代理层来池化和重复使用这些连接。对于关系数据库，[Amazon RDS 代理](https://github.com/aws-samples/s3-to-lambda-patterns/tree/master/docrepository)是一项旨在帮助提高基于 Lambda 的应用程序的可扩展性和弹性的服务。

## AWS 开发工具包：版本和更新
<a name="troubleshooting-execution-versions"></a>

**问题：***运行时包含的 AWS 开发工具包不是最新版本*

**问题：***运行时中包含的 AWS 开发工具包自动更新*

解释性语言的运行时包括 AWS SDK 的一个版本。Lambda 会定期更新这些运行时，以使用最新的 SDK 版本。要查找运行时中包含的 SDK 版本，请参阅以下部分：
+ [包含运行时的 SDK 版本（Node.js）](lambda-nodejs.md#nodejs-sdk-included)
+ [包含运行时的 SDK 版本（Python）](lambda-python.md#python-sdk-included)
+ [包含运行时的 SDK 版本（Ruby）](lambda-ruby.md#ruby-sdk-included)

要使用较新版本的AWS开发工具包，或将函数锁定为特定版本，您可以将库与函数代码捆绑起来，或[创建 Lambda 层](chapter-layers.md)。有关创建具有依赖项的部署程序包的详细信息，请参阅以下主题：

------
#### [ Node.js ]

[使用 .zip 文件归档部署 Node.js Lambda 函数](nodejs-package.md) 

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

 [将 .zip 文件归档用于 Python Lambda 函数](python-package.md) 

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

 [使用 .zip 文件归档部署 Ruby Lambda 函数](ruby-package.md) 

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

 [使用 .zip 或 JAR 文件归档部署 Java Lambda 函数](java-package.md) 

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

 [使用 .zip 文件归档部署 Go Lambda 函数](golang-package.md) 

------
#### [ C\$1 ]

 [使用 .zip 文件归档构建和部署 C\$1 Lambda 函数](csharp-package.md) 

------
#### [ PowerShell ]

 [使用 .zip 文件归档部署 PowerShell Lambda 函数](powershell-package.md) 

------

## Python：库未正确加载
<a name="troubleshooting-execution-libraries"></a>

**问题：**(Python) *无法从部署程序包中正确加载某些库*

具有使用 C 或 C \$1\$1 编写的扩展模块的库，必须在与 Lambda (Amazon Linux) 具有相同处理器架构的环境中进行编译。有关更多信息，请参阅 [将 .zip 文件归档用于 Python Lambda 函数](python-package.md)。

## Java：从 Java 11 更新到 Java 17 后，函数处理事件所需的时间更长
<a name="troubleshooting-execution-java-perf"></a>

**问题：**（Java）*从 Java 11 更新到 Java 17 后，函数处理事件所需的时间更长*

使用 `JAVA_TOOL_OPTIONS` 参数调整编译器。Java 17 及更高 Java 版本的 Lambda 运行时会更改默认的编译器选项。虽然此更改缩短了寿命较短的函数的冷启动时间，但以前的行为更适合计算密集型、运行时间较长的函数。将 `JAVA_TOOL_OPTIONS` 设置为 `-XX:-TieredCompilation` 可恢复到 Java 11 的行为。有关 `JAVA_TOOL_OPTIONS` 参数的更多信息，请参阅 [了解 `JAVA_TOOL_OPTIONS` 环境变量](java-customization.md#java-tool-options)。

## Kafka: 错误处理和重试配置问题
<a name="troubleshooting-kafka-error-handling"></a>

**问题：***Kafka 事件源映射无法配置重试设置或失败时的目标*

Kafka 重试配置和失败时的目标仅适用于启用了预置模式的事件源映射。在尝试设置重试配置之前，请确保已在 `ProvisionedPollerConfig` 中配置了 `MinimumPollers`。

常见配置错误：
+ **采用分批处理的无限次重试**：当 `MaximumRetryAttempts` 被设置为 -1（无限）时，无法启用 `BisectBatchOnFunctionError`。设置有限重试限制或禁用分批处理。
+ **相同的主题递归**：Kafka 失败时的目标主题不能与您的任何源主题相同。为您的死信主题选择不同的主题名称。
+ **无效的 Kafka 目标格式**：在指定 Kafka 主题作为失败时的目标时，请使用 `kafka://<topic-name>` 格式。
+ **kafka:WriteData 权限问题**：确保您的执行角色具有目标主题的 `kafka-cluster:WriteData` 权限。如果出现主题不存在的超时错误或者写入 API 的节流问题，可能需要提高账户的限制。