AWS Lambda节点功能无法正常运行

时间:2018-06-04 06:36:17

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

我通过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();
};

BTW上面的代码有另一个文件,其中包含所有正确的快递项目,以便正确地为此服务。同样,它可以在本地,其他服务器上完美运行,有时甚至在充斥lambda时;但它从未在Lambda上持续工作。

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

编辑:

感谢Michael和Hen,事情已经到位。使用Lambda时,lambda函数将在它通过主进程逻辑时关闭。它不会等待挂起的异步项完成。以下更改修复了使用Express with 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();
  }
});
};

3 个答案:

答案 0 :(得分:1)

它没有默默地失败,它只是进入假死。在短时间内再次尝试它实际上允许早期尝试有足够的运行时间来完成。

问题似乎在这里:

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

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

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

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

任何时候只有一个调用在任何一个容器中运行,但是在这里你要保持运行,所以之前调用的工作实际上是在后面的调用中完成的。

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

¹ Lambda是请求/响应。 Lambda函数本身也可以作为“事件”调用异步调用,因此对外部调用者的“响应”仅表示Lambda基础结构已同意为您执行该函数,如果您不想要或者需要等待实际响应,但这与此相关。 Lambda函数仍然是请求/响应,即使在此备用模型下,函数的响应也会被丢弃,如果抛出异常,则会重试调用两次。

答案 1 :(得分:1)

我遇到了同样的问题,并通过暂时将 await 语法解决了该问题。对于那些需要更好解决方案的人(这是我稍后会做的事情),创建特定的lambda函数将优化工作流程并为项目带来更好的方法。

答案 2 :(得分:0)

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

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

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