【问题标题】:AWS Lambda Node Function Silently FailsAWS Lambda 节点功能静默失败
【发布时间】:2018-11-13 10:29:50
【问题描述】:

我有一个通过 AWS API 网关提供的 AWS Lambda 函数。在大多数情况下,一切似乎都运行良好。我们的 API 有一项功能无法正常工作。它是一个 webhook,用于接收传入的传真和电子邮件,让我们知道它已收到。

我们已经对所有内容进行了测试,这在我们的本地机器上完美运行,甚至在像 linode 或数字海洋这样的供应商上也是如此。我们的问题仅出现在使用 AWS Lambda 部署时。

  • 我已尝试将内存和执行限制增加到超出要求的范围。
  • 我尝试了多种电子邮件方法和库。
  • 我尝试过各种函数格式。

lambda 的 cloudwatch 日志显示 console.log(req.body.MediaUrl);,但 stdout 或 stderr 上没有其他输出。即使我把明显的缺陷放进去,也不会有错误。几乎就好像该功能在没有解释原因的情况下默默地失败了。

如果我在大约 1-2 秒内发送 2-3 个帖子请求,我就能让它工作。 1 可能会发送 2 封电子邮件,但绝不会全部发送。如果按照正常使用方式发送单个发布请求,您将永远不会收到电子邮件。

// Define a handler for when the fax is initially sent
const nodemailer = require('nodemailer');
const aws = require('aws-sdk');

// Start Email Logic
// AWS access keys are built into Lambda, modify permissions of the executor role
aws.config.update({region: 'us-west-2'});

let transporter = nodemailer.createTransport({
  SES: new aws.SES({
    apiVersion: '2010-12-01',
  }),
 });

exports.received = function(req, res) {

  transporter.sendMail({
  from: '"Fax" <fax@domain.com>',
  to: process.env.FAX_ADDRESS,
  subject: 'A new fax from ' + req.body.From,
  text: 'You can view the fax at this url:\n\n' + req.body.MediaUrl,
}, (err, info) => {
  if (err) {
    console.log(err);
  } else {
    console.log(info.envelope);
    console.log(info.messageId);
    console.log('Email Sent');
  }
});

// log the URL of the PDF received in the fax just in case email fails
console.log(req.body.MediaUrl);

  res.status(200);
  res.send();
};

顺便说一句,上面的代码有另一个文件,其中包含所有正确的快递项目来正确地提供这个服务。同样,它在本地、其他服务器甚至有时在泛洪 lambda 时也能完美运行;但它从来没有在 Lambda 上始终如一地工作。

我的猜测是要么我是一个需要睡觉的白痴,要么我在这里错过了一些关于 Lambda 的东西。任何人都可以阐明我所缺少的东西吗?

编辑:

多亏了 Michael 和 Hen,事情才顺利进行。使用 Lambda 时,lambda 函数将在它通过主流程逻辑的那一刻关闭。它不会等待待处理的异步项目完成。以下更改解决了我在使用 Express 和 Lambda 时遇到的问题。

请注意,我已将邮件功能移至响应的末尾,然后将响应状态和发送移至邮件功能的内部。这是可以解决此问题的众多方法之一,但对我们的需求而言既简单又甜蜜。

exports.received = function(req, res) {

  // log the URL of the PDF received in the fax just in case email fails
  console.log(req.body.MediaUrl);

  transporter.sendMail({
  from: '"Fax" <fax@domain.com>',
  to: process.env.FAX_ADDRESS,
  subject: 'A new fax from ' + req.body.From,
  text: 'You can view the fax at this url:\n\n' + req.body.MediaUrl,
}, (err, info) => {
  if (err) {
    console.log(err);
    res.status(500);
    res.send();
  } else {
    console.log(info.envelope);
    console.log(info.messageId);
    console.log('Email Sent');
    res.status(200);
    res.send();
  }
});
};

【问题讨论】:

    标签: node.js express aws-lambda amazon-ses


    【解决方案1】:

    它不是静默失败,它只是进入假死状态。连续短时间再次尝试实际上是允许早期尝试有足够的运行时间来完成。

    问题似乎出在这里:

    res.status(200);
    res.send();
    

    这些应该在来自sendMail() 的回调中。在成功之前您不应该返回成功,但您会在此之前返回它,因为sendMail() 是异步的。

    你不能指望 Lambda 在你告诉它你完成后继续为你做事。 Lambda 是请求/响应。¹启动函数、执行操作、返回响应、停止。该过程被冻结,运行时计费停止。如果事件循环中还有一些东西,那么它们就会挂起,具体取决于 context.callbackWaitsForEmptyEventLoop 的值,我怀疑应该/可以用 express 安全地更改它。

    如果下一个函数调用重用同一个容器(由基础架构做出决定),那么当容器解冻时,您留下运行的东西仍在运行,因此如果调用在时间上足够接近,它们可能随后完成套接字没有超时,或者没有发生任何其他与挂钟时间相关的潜在故障。

    任何时候只有一个调用在任何一个容器中运行,但在这里你让事情继续运行,因此之前调用的工作实际上是在后面的调用中完成的。

    如果您在回调中复制 console.log(req.body.MediaUrl); 行并将其保留在现在的位置,假设每个请求的值都不同,那么您实际上应该在此处捕获一些证据来确认我所断言的内容。


    ¹Lambda 是请求/响应。 Lambda 函数本身也可以作为“事件”调用异步调用,因此对外部调用者的“响应”仅表明 Lambda 基础设施已同意为您执行该函数,以防您不希望或需要等待实际响应,但这与此上下文无关。 Lambda 函数仍然是请求/响应,即使在这种替代模型下,来自函数的响应被丢弃,并且如果抛出异常,调用会重试两次。

    【讨论】:

    • 感谢您的回复。你是绝对正确的。我没有考虑到 lambda 会在 express 发送响应的那一刻关闭。
    【解决方案2】:

    我遇到了同样的问题并解决了这个问题,现在使用 await 语法。对于那些需要更好解决方案的人(这就是我稍后要做的),创建一个特定的 lambda 函数将优化工作流程并为项目带来更好的方法。

    【讨论】:

    • 最好为您的解决方案添加一些代码。
    【解决方案3】:

    在本地运行代码或在 lambda 中运行代码的区别在于,您需要在结束 lambda 函数之前等待异步操作完成。

    您所经历的是在 SendMail 结束之前终止 lambda,如果您一起发送几个请求,让它发送 1 封电子邮件只是一个时间游戏。

    我建议您使用*await transporter.sendMail* 并且还能够报告真正的成功/失败,而不是总是返回 200。

    【讨论】:

      猜你喜欢
      • 2019-05-18
      • 2019-07-13
      • 2022-11-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-28
      • 1970-01-01
      • 2011-09-20
      相关资源
      最近更新 更多