Lambda 中的执行问题疑难解答
当 Lambda 运行时运行函数代码时,可能会在已经处理事件一段时间的函数实例上处理事件,或者可能需要初始化一个新实例。在函数初始化期间、处理程序代码处理事件时或者当函数返回(或无法返回)响应,可能会发生错误。
函数执行错误可能是由您的代码、函数配置、下游资源或权限中的问题引起。如果您直接调用函数,则会在 Lambda 的响应中看到函数错误。如果您使用事件源映射或通过其他服务异步调用函数,则可能会在日志、死信队列或失败时的目标中找到错误。错误处理选项和重试行为因调用函数的方式和错误类型而异。
当您的函数代码或 Lambda 运行时返回错误时,来自 Lambda 的响应中的状态代码为“200 OK”。响应中是否存在错误由名为 X-Amz-Function-Error
的标头指示。400 和 500 系列状态代码保留用于调用错误。
主题
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 开发工具包,并定期更新到最新版本。每个运行时的当前版本在运行时页面上列出。要使用较新版本的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 环境变量。