Lambda 中的执行问题疑难解答
当 Lambda 运行时运行函数代码时,可能会在已经处理事件一段时间的函数实例上处理事件,或者可能需要初始化一个新实例。在函数初始化期间、处理程序代码处理事件时或者当函数返回(或无法返回)响应,可能会发生错误。
函数执行错误可能是由您的代码、函数配置、下游资源或权限中的问题引起。如果您直接调用函数,则会在 Lambda 的响应中看到函数错误。如果您使用事件源映射或通过其他服务异步调用函数,则可能会在日志、死信队列或失败时的目标中找到错误。错误处理选项和重试行为因调用函数的方式和错误类型而异。
当您的函数代码或 Lambda 运行时返回错误时,来自 Lambda 的响应中的状态代码为“200 OK”。响应中是否存在错误由名为 X-Amz-Function-Error 的标头指示。400 和 500 系列状态代码保留用于调用错误。
主题
Lambda:使用 Visual Studio Code 进行远程调试
问题:对实际 Amazon 环境中难以复杂的 Lambda 函数行为进行故障排除
Lambda 通过 Amazon Toolkit for Visual Studio Code 提供远程调试功能。有关设置和一般说明,请参阅使用 Visual Studio Code 远程调试 Lambda 函数。
有关故障排除、高级使用案例和区域可用性的详细说明,请参阅《Amazon Toolkit for Visual Studio Code 用户指南》中的远程调试 Lambda 函数。
Lambda:执行时间过长
问题:函数执行时间太长。
如果您的代码在 Lambda 中运行所花费的时间长于在本地计算机上运行所花费的时间,则可能受到对该函数可用的内存或处理能力的限制。使用额外内存配置函数以增加内存和 CPU。
Lambda:意外的事件有效载荷
问题:与 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 事件有效载荷的最大大小为 256 KB。根据内容,有效载荷越大,可能意味着传递给函数的项目越多,或者 JSON 属性中嵌入的二进制数据越多。在这两种情况下,此项都可能导致对 Lambda 函数进行更多处理。
较大的有效载荷也可能导致超时。例如,Lambda 函数每 100 毫秒处理一条记录,且超时时间为 3 秒。成功处理有效载荷中的 0-29 个项目。但是,一旦有效载荷包含超过 30 个项目,该函数就会超时并引发错误。为避免这种情况,请确保设置超时,以处理预期最大数量的项目所需的额外处理时间。
Lambda:意外的大型有效载荷大小
问题:由于有效载荷较大,函数超时或导致错误。
较大的有效载荷也可能导致超时和错误。我们建议创建测试以确保您的函数能够处理最大预期有效载荷,并确保正确设置函数超时。
此外,某些事件有效载荷可能包含指向其他资源的指针。例如,内存为 128 MB 的 Lambda 函数可以针对作为对象存储在 S3 中的 JPG 文件执行图像处理。对于较小的图像文件,该函数可以按预期运行。但是,当提供较大的 JPG 文件作为输入时,Lambda 函数会因内存不足而引发错误。为避免这种情况,测试案例应包括预期数据大小上限的示例。该代码还应验证有效载荷大小。
Lambda:JSON 编码和解码错误
问题:解析 JSON 输入时出现 NoSuchKey 异常。
进行检查,确保在正确处理 JSON 属性。例如,对于 S3 生成的事件,s3.object.key 属性包含了 URL 编码的对象密钥名称。许多函数将此属性处理为文本,以加载引用的 S3 对象:
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 编码会转换密钥名称中的空格和其他字符,因此,您必须确保函数在使用以下数据之前解码了密钥:
const originalText = await s3.getObject({ Bucket: event.Records[0].s3.bucket.name, Key: decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, " ")) }).promise()
Lambda:未显示日志或跟踪
问题:日志未显示在 CloudWatch Logs 中。
问题:跟踪未显示在 Amazon X-Ray 中。
您的函数需要权限才能调用 CloudWatch Logs 和 X-Ray。更新函数的执行角色以向其授予权限。添加以下托管策略以启用日志和跟踪。
-
AWSLambdaBasicExecutionRole
-
AWSXRayDaemonWriteAccess
向函数添加权限时,请同时简单更新其代码或配置。这会强制停止并替换函数的具有过时凭证的运行实例。
注意
函数调用后,日志可能需要 5 到 10 分钟才能显示。
Lambda:并非所有我的函数的日志都会出现
问题:尽管我拥有正确权限,但 CloudWatch Logs 中缺少函数日志
如果您的 Amazon Web Services 账户 达到了其 CloudWatch Logs 限额限制,则 CloudWatch 就会对函数日志记录进行节流。发生这种情况时,您函数输出的某些日志可能不会出现在 CloudWatch Logs 中。
如果函数输出日志的速度过快,Lambda 无法对其进行处理,也会导致日志输出无法显示在 CloudWatch Logs 中。当 Lambda 无法按照函数生成日志的速度向 CloudWatch 发送日志时,它会丢弃日志以防止函数的执行速度减慢。单个日志流的日志吞吐量超过 2 MB/s 时,预计会持续观察到删除的日志。
如果函数配置为使用 JSON 格式的日志,则 Lambda 会在丢弃日志时尝试向 CloudWatch Logs 发送 logsDropped 事件。但是,当 CloudWatch 对函数的日志记录进行节流时,此事件可能无法到达 CloudWatch Logs,因此 Lambda 丢弃日志时您并不始终会看到记录。
要检查 Amazon Web Services 账户 是否已达到其 CloudWatch Logs 限额限制,请执行以下操作:
-
打开服务限额控制台
。 -
在导航窗格中,选择 Amazon 服务。
-
从 Amazon 服务列表中,搜索 Amazon CloudWatch Logs。
-
在服务限额列表中,选择
CreateLogGroup throttle limit in transactions per second、CreateLogStream throttle limit in transactions per second和PutLogEvents throttle limit in transactions per second限额以查看利用率。
您还可以设置 CloudWatch 警报,以便在账户利用率超过您为这些限额指定的限制时提醒您。请参阅根据静态阈值创建 CloudWatch 告警以了解更多信息。
如果 CloudWatch Logs 的默认限额限制不足以满足您的用例,则可以请求增加限额。
Lambda:函数在执行完成之前返回
问题:(Node.js) 函数在代码完成执行之前返回
许多库(包括 Amazon 开发工具包)都是异步操作。当您进行网络调用或执行其他需要等待响应的操作时,库返回一个名为 promise 的对象,该对象在后台跟踪操作的进度。
要等待 promise 解析为响应,请使用 await 关键字。这会阻止您的处理程序代码执行,直到将 promise 解析为包含响应的对象。如果您不需要在代码中使用来自响应的数据,则可以直接将 promise 返回到运行时。
一些库不返回 promise,但可以包装到返回 promise 的代码中。有关更多信息,请参阅 定义采用 Node.js 的 Lambda 函数处理程序。
Lambda:运行意外函数版本或别名
问题:未调用函数版本或别名
当您在控制台中或使用 Amazon SAM 发布新的 Lambda 函数时,最新的代码版本由 $LATEST 表示。默认情况下,未指定版本或别名的调用会自动将函数代码的 $LATEST 版本作为目标。
如果您使用特定的函数版本或别名,则这些版本或别名是除 $LATEST 之外函数的不可变已发布版本。对这些函数进行问题排查时,首先确定调用方是否调用了预期的版本或别名。可以通过检查函数日志来做到这一点。被调用的函数的版本始终显示在 START 日志行中:
Lambda:检测无限循环
问题:与 Lambda 函数相关的无限循环模式
Lambda 函数中有两种类型的无限循环。第一个发生于函数自身内部,是由一个永不退出的循环所致。只有函数超时,调用才会结束。您可以通过监控超时然后修复循环行为来发现这些问题。
第二种循环是 Lambda 函数与其他 Amazon 资源之间的循环。当来自 S3 存储桶等资源的事件调用 Lambda 函数时,就会发生此情况,然后该函数与相同的源资源交互以触发另一个事件。这会再次调用该函数,进而创建与同一 S3 存储桶的另一个交互,依此类推。这些类型的循环可能由多种不同的 Amazon 事件源引起,包括 Amazon SQS 队列和 DynamoDB 表。可以使用递归循环检测来识别这些模式。
您可以确保 Lambda 函数写入与使用资源不同的资源,从而避免这些循环。如果您必须将数据发布回使用的资源,则请确保新数据不会触发相同的事件。或者,使用事件筛选。例如,以下是针对使用 S3 和 DynamoDB 资源的无限循环提出的两个解决方案:
-
如果您回写到同一 S3 存储桶,请使用与事件触发器不同的前缀或后缀。
-
如果您将项目写入同一 DynamoDB 表,则请包含使用的 Lambda 函数可以筛选的属性。如果 Lambda 找到该属性,则不会导致再次调用。
常规:下游服务不可用
问题:您的 Lambda 函数所依赖的下游服务不可用
对于调用第三方端点或其他下游资源的 Lambda 函数,请确保它们能够处理服务错误和超时。这些下游资源可能具有可变的响应时间,或者可能由于服务中断而变得不可用。根据实施,如果服务的错误响应未在函数代码中处理,则这些下游错误可能会显示为 Lambda 函数超时或异常。
每当函数依赖于下游服务(例如 API 调用)时,应实施适当的错误处理和重试逻辑。对于关键服务,Lambda 函数应将指标或日志发布到 CloudWatch。例如,如果第三方支付 API 不可用,则 Lambda 函数可以记录此信息。然后,您可以设置 CloudWatch 警报来发送与这些错误相关的通知。
由于 Lambda 可以快速扩展,非无服务器下游服务可能难以应对流量激增。有三种常见的方法可处理此问题:
-
缓存 – 如果第三方服务返回的值不经常更改,可以考虑缓存这些值的结果。您可以将这些值存储在函数或其他服务的全局变量中。例如,可以将来自 Amazon RDS 实例的产品列表查询的结果保存在函数内一段时间,以防止冗余查询。
-
加入队列 – 保存或更新数据时,在 Lambda 函数与资源之间添加 Amazon SQS 队列。在下游服务处理消息时,队列可以持久地保存数据。
-
代理 – 若通常使用长期连接(例如对于 Amazon RDS 实例),应使用代理层来池化和重复使用这些连接。对于关系数据库,Amazon RDS 代理
是一项旨在帮助提高基于 Lambda 的应用程序的可扩展性和弹性的服务。
Amazon 开发工具包:版本和更新
问题:运行时包含的 Amazon 开发工具包不是最新版本
问题:运行时中包含的 Amazon 开发工具包自动更新
解释性语言的运行时包括 Amazon SDK 的一个版本。Lambda 会定期更新这些运行时,以使用最新的 SDK 版本。要查找运行时中包含的 SDK 版本,请参阅以下部分:
要使用较新版本的Amazon开发工具包,或将函数锁定为特定版本,您可以将库与函数代码捆绑起来,或创建 Lambda 层。有关创建具有依赖项的部署程序包的详细信息,请参阅以下主题:
Python:库未正确加载
问题:(Python) 无法从部署程序包中正确加载某些库
具有使用 C 或 C ++ 编写的扩展模块的库,必须在与 Lambda (Amazon Linux) 具有相同处理器架构的环境中进行编译。有关更多信息,请参阅 将 .zip 文件归档用于 Python Lambda 函数。
Java:从 Java 11 更新到 Java 17 后,函数处理事件所需的时间更长
问题:(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 环境变量。