lambda 触发器回调与 context.done

Posted

技术标签:

【中文标题】lambda 触发器回调与 context.done【英文标题】:lambda trigger callback vs context.done 【发布时间】:2019-07-17 16:40:24 【问题描述】:

我按照指南 here 设置了预注册触发器。

但是,当我使用 callback(null, event) 时,我的 lambda 函数永远不会真正返回,我最终会得到一个错误

代码:'UnexpectedLambdaException', 名称:'UnexpectedLambdaException', 消息:'arn:aws:lambda:us-east-2:642684845958:function:proj-dev-confirm-1OP5DB3KK5WTA 在调用 Lambda 函数时因错误套接字超时而失败。'

我发现了一个类似的link here,它说要使用context.done()

切换后效果很好。

有什么区别?

exports.confirm = (event, context, callback) => 
    event.response.autoConfirmUser = true;
    context.done(null, event);
    //callback(null, event); does not work

【问题讨论】:

context.done() 在 6.10 之前的 Node.js 运行时中使用并且已弃用。将其改回callback(),我希望它会再次起作用,因为您遇到的错误似乎是您不应该导致的错误,即使是偶然的。它可能已被“修复”,因为您更改了代码中的 任何内容,促使 Lambda 重新部署它。 所以这实际上并没有解决它。但是,我确实添加了context.callbackWaitsForEmptyEventLoop = false;,它现在可以工作了。你有没有机会解释这实际上是做什么的? @Michael-sqlbot 我也有这个问题。 context.callbackWaitsForEmptyEventLoop = false;解决了 【参考方案1】:

回到 Node.js 0.10 的原始 Lambda 运行时环境,Lambda 在 context 对象中提供了帮助函数:context.done(err, res) context.succeed(res)context.fail(err)

这曾被记录在案,但已被删除。

Using the Earlier Node.js Runtime v0.10.42 是 Lambda 文档中不再存在的页面的存档副本,它解释了如何使用这些方法。

当 Lambda 的 Node.js 4.3 运行时启动时,这些仍然是为了向后兼容(并且仍然可用但未记录),并引入了 callback(err, res)

这是您问题的性质,以及为什么您找到的两个解决方案实际上似乎可以解决问题。

但是,Context.succeed、context.done 和 context.fail 不仅仅是记账——它们会在当前任务完成后导致请求返回并立即冻结进程,即使其他任务仍保留在 Node.js 中事件循环。如果这些任务代表不完整的回调,通常这不是您想要的。

https://aws.amazon.com/blogs/compute/node-js-4-3-2-runtime-now-available-on-lambda/

因此,使用callback,Lambda 函数现在以更典型的正确方式运行,但如果您打算在调用之间发生冻结期间某些对象保留在事件循环中,这将是一个问题 - 不像旧的 (已弃用)donefailsucceed 方法,使用回调不会立即暂停事情。相反,它等待事件循环为空。

context.callbackWaitsForEmptyEventLoop -- 默认true -- 被引入,以便您可以将其设置为false,以便在您希望 Lambda 函数在您调用回调后立即返回的情况下,无论在事件循环。默认值为true,因为false 可以掩盖函数中的错误,并且如果您未能考虑容器重用的影响,可能会导致非常不稳定/意外的行为——因此您不应将其设置为false,除非且直到你明白为什么需要它。

需要false 的一个常见原因是您的函数建立了数据库连接。如果您在全局变量中创建数据库连接对象,它将有一个打开的套接字,并且可能还有其他东西,如计时器,位于事件循环中。这可以防止回调导致 Lambda 返回响应,直到这些操作也完成或调用超时计时器触发。

确定为什么需要将其设置为false,如果这是一个正当的理由,那么使用它是正确的。

否则,在调用回调时,您的代码可能存在需要了解和修复的错误,例如使请求处于未完成状态或其他工作未完成。

那么,我们如何解析 Cognito 错误?起初,这似乎很不寻常,但现在显然不是。

执行函数时,Lambda 会在配置的秒数后抛出任务超时的错误。当您在 Lambda 控制台中测试您的函数时,您应该会发现这就是发生的情况。

不幸的是,Cognito 在调用 Lambda 函数时似乎采用了内部设计捷径,而不是等待 Lambda 使调用超时(这可能会占用 Cognito 内部的资源)或将其自己的显式计时器强加于 Cognito 的最长持续时间将等待 Lambda 响应,它依赖于较低层的套接字计时器来限制这种等待......因此在调用超时时会引发“意外”错误。

错误消息的解释更加复杂,错误中缺少引号,其中插入了下层异常。

对我来说,如果错误如下所示,问题会更清楚:

'arn:aws:lambda:...' failed with error 'Socket timeout' while invoking Lambda function

这种格式会更清楚地表明,当 Cognito 调用该函数时,它会引发内部 Socket timeout 错误(与 Lambda 遇到意外的内部错误相反,这是我最初的——也是不正确的——假设)。

对于 Cognito 来说,对 Lambda 函数施加某种响应时间限制是非常合理的,但我没有看到这有记录。我怀疑您的 Lambda 函数本身的短暂超时(使其更快地失败)会导致 Cognito 抛出一个更有用的错误,但在我看来,Cognito 应该被设计为包含逻辑以使其成为预期的、定义的错误,而不是将其归类为“意外”。

【讨论】:

这篇文章写得很好,文档也很好,谢谢。我确实有一个全局数据库连接,这导致了这种情况。这个函数虽然不使用连接,所以我相信使用context.callbackWaitsForEmptyEventLoop = false;是安全的 @TemporaryFix 或者您可以在完成后关闭连接,而如果 lambda 有点复杂,您可能会忘记一些未完成的调用。我建议仅将 context.callbackWaitsForEmptyEventLoop = false 用于超小的 lambda,尽管 idk 你的 lambda :)【参考方案2】:

作为更新,Runtime Node.js 10.x 处理程序支持async function,它利用returnthrow 语句分别返回成功或错误响应。此外,如果您的函数执行异步任务,那么您可以返回一个Promise,然后您将使用resolvereject 分别返回成功或错误。这两种方法都不需要 contextcallback 向调用者发出完成信号,从而简化了事情,因此您的 lambda 函数可能如下所示:

exports.handler = async (event) => 
  // perform tasking...
  const data = doStuffWith(event)

  // later encounter an error situation
  throw new Error('tell invoker you encountered an error')

  // finished tasking with no errors
  return  data 

当然,您仍然可以使用context,但它不需要发出完成信号。

【讨论】:

现在也支持 Node 12!

以上是关于lambda 触发器回调与 context.done的主要内容,如果未能解决你的问题,请参考以下文章

API Gateway 方法与 lambda 函数集成,但 lambda 函数声称它没有触发器

AWS SNS子脚本与lambda函数的触发器?

c++回调函数详解及实现(lambda)

如何创建一个填充了 C 风格函数指针和 lambda 的向量(有和没有捕获)

数据库触发器与 Rails ActiveRecord 回调的优缺点?

如何使用lambda表达式捕获局部变量?