如何在 AWS Lambda 中等待异步操作?
Posted
技术标签:
【中文标题】如何在 AWS Lambda 中等待异步操作?【英文标题】:How to wait for async actions inside AWS Lambda? 【发布时间】:2015-10-16 12:09:19 【问题描述】:我正在尝试在 S3 中处理上传的文件。由于 getObject 是异步主函数在处理完成之前结束,AWS 在 3-4 秒内杀死 lambda。
更糟糕的是,处理方法中还有异步操作——它会进行 http 调用。
在高层次上,我的代码如下所示:
exports.handler = function(event, context)
// Get the object from the event and show its content type
var bucket = event.Records[0].s3.bucket.name;
var key = event.Records[0].s3.object.key;
var params =
Bucket: bucket,
Key: key
;
s3.getObject(params, function(err, data)
if (err)
...
else
processFile(data.Body.toString(), 0);
console.log("ok");
);
//need to wait here till processFile is done
;
processFile = function(content, start)
... build url to call
http.get(url, function(res)
console.log("Got response: " + res.statusCode + ");
processFile(content, start + 1);
);
我发现nodejs中有异步但亚马逊不包含它; require('async') 或 require('sleep) 都会导致错误。
Lambda 超时配置为 60 秒,但在 3-4 秒后退出。
【问题讨论】:
我可以确认看到与 AWS lambda 类似的问题,并且超时设置没有增加执行窗口的逻辑效果...增加内存分配确实似乎增加了可用执行时间,但仍然观察到奇怪的效果。 你还记得你是如何解决这个问题的吗? 【参考方案1】:开发人员的生活在不断变化,现在我们在 lambda 上使用了 NodeJS 8。对于正在查看此内容的任何人,请查看:
Lambda 节点 8.10 与节点 6.10 比较: https://aws.amazon.com/blogs/compute/node-js-8-10-runtime-now-available-in-aws-lambda/
JS 异步的基础知识: https://developer.mozilla.org/en-US/docs/Web/javascript/Reference/Statements/async_function
更多 aws sdk 示例:https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/using-promises.html
.promise() 方法的详细信息在第一个链接中:https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Request.html#promise-property
这是我对一个基本示例的看法(尝试粘贴到您自己的 lambda 中):
exports.handler = async (event) =>
function wait()
return new Promise((resolve, reject) =>
setTimeout(() => resolve("hello"), 2000)
);
console.log(await wait());
console.log(await wait());
console.log(await wait());
console.log(await wait());
console.log(await wait());
console.log(await wait());
return 'exiting'
;
以上产出:
如您所见,它等待了 12 秒而没有杀死我的功能 :)
每次等待要多做一件事,使用 Promise.all([]) 语法如下:
exports.handler = async (event) =>
var uploadPromises = [];
folder.files.forEach(file =>
uploadPromises.push( s3.putObject(
Bucket: "mybucket",
Key: file.name,
Body: file.data
).promise());
);
await Promise.all(uploadPromises);
return 'exiting'
;
下面是原始答案
我手上也有同样的问题。
问题是 javascript 事件循环是空的,所以 Lambda 认为它已经完成了。
这就是我解决这个问题的方法。我意识到这并不理想,我希望有更好的方法,但我不想 a) 添加库,b) 协调 lambda 调用,或 c) 切换到另一种语言。
最终它会起作用。
exports.handler = (event, context, callback) =>
var response;
var callBackCount;
/*
Ensures the javascript event loop is never empty.
This is the key to keeping lambda from exiting early
*/
setInterval(function(), 1000);
/*
Tell lambda to stop when I issue the callback.
This is super important or the lambda funciton will always go until it hits the timeout limit you set.
*/
context.callbackWaitsForEmptyEventLoop = false;
//My way of determining when I'm done with all calls
callBackCount = 0;
//My info to return
response = "";
//Various functions that make rest calls and wait for a response
asyncFunction1();
asyncFunction2();
asyncFunction3();
//Same for asyncFunction 2 and 3
function asyncFunction1()
response += callBackResponseForThisMethod;
returnResponse();
function returnReponse()
callBackCount++;
if(callBackCount == 3)
//Lambda will stop after this as long as context.callbackWaitsForEmptyEventLoop was set to false
callback(null, JSON.stringify(response));
;
http://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-context.html
【讨论】:
“开发者的生活”完美地总结了它! 您的代码行节省了几乎一整天的修补时间!你先生是我今天的英雄! 【数字高五!!!】 天啊...非常感谢你,哈哈。 6年后,这仍然是我需要看到的【参考方案2】:使用异步/等待
let AWS = require('aws-sdk');
let lambda = new AWS.Lambda();
let data;
exports.handler = async (event) =>
try
data = await lambda.getAccountSettings().promise();
catch (err)
console.log(err);
return err;
return data;
;
【讨论】:
这在 Node 10+ 中起作用的原因是,如果你的处理函数被标记为异步,它应该返回一个未解决的 Promise 才能正常工作。如果处理程序返回一个承诺,则 lambda 将在完成之前自动等待承诺解决。 docs.aws.amazon.com/lambda/latest/dg/…【参考方案3】:async
不包括在内,但这并不意味着您不能自己添加它。
只需在本地添加包 (npm install async
),并在上传 Lambda 函数之前将 node_modules
文件夹包含在 ZIP 中。
如果您想单独处理开发依赖项(例如:测试、aws-sdk
在本地执行您的函数等),您可以将它们添加到您的package.json
中的devDependencies
下。此外,如果您想自动化开发、测试、部署和推广代码的过程,这两个存储库将非常方便。
Grunt routine to test, package and deploy your lambdas
Command line tool for running and deploying your lambda functions
【讨论】:
【参考方案4】:将 Lambda 简单地视为可以在一定时间内运行的程序。您进行异步调用的事实很好,因为(虚拟)处理器可能会交错这些调用。但是,如果您的 Lambda 程序的任何部分的完成时间超过了分配的时间,那么执行将失败。这就是你做出的妥协,也是亚马逊赚钱的方式;卖给你更多的时间或记忆。
要解决这个问题,您可以增加分配给您的 Lambda 函数的内存。这不仅增加了您的 RAM,还增加了虚拟处理器的速度。您可以做的另一件事是增加超时。 AWS Lambda 现在允许您使用高达 512 MB 的 RAM 和长达 5 分钟的处理时间。在这篇文章中,这些数字可能已经改变,因此请查看here 了解最新限制。要更改此设置,请转到您的功能,然后是配置,最后是高级。
【讨论】:
【参考方案5】:我认为你的 lambda 函数应该以 context.done() 调用结束。例如,尝试以这种方式添加它:
s3.getObject(params, function(err, data)
if (err)
...
context.done("Error: " + err.stack);
else
processFile(data.Body.toString(), 0);
console.log("ok");
context.done(null, "success");
);
【讨论】:
我在最后一个处理程序中有 context.done,但是 lambda 在到达它之前就被终止了 在你的 s3.getObject 回调之后你有 context.done 吗?您是否尝试过从最后一个处理程序中删除 context.done 并将其仅放在 s3.getObject 回调中,就像我上面展示的方式一样?甚至在 s3.getObject 回调完成之前,可能会调用回调之外的 context.done 。【参考方案6】:如果您还想使用require('async');
pack 或require('sleep');
pack,您需要将您的函数上传为zip
文件,如下所示:
Creating a Deployment Package (Node.js)
Zip
我也在这个问题中解释的文件夹的所有内容:
MQTT in AWS Lambda function for Alexa Javascript
关于同步处理,可以正常使用require('async');
,就是这样使用async.series
函数:
async.series([
function(callback)
// to do the function 1
callback();
,
function(callback)
// to do the function 2
callback();
,
function(callback)
// to do the function 3
callback();
], function(err)
// to do the function if any error happens...
if (err)
//...
//....
);
这样lambda
函数将同步工作。
希望对你有帮助。
【讨论】:
【参考方案7】:您可能想要拨打synchronous
来代替;因为您似乎在同一个 lambda 函数中处理您的文件。
如果由于某种原因您想获得回调;您可以通过直接调用 lambda 或通过会产生 lambda 事件的方法来做到这一点。请注意,lambda 函数应该是无状态的;所以你应该传递所有需要的信息。
【讨论】:
对了,我只是没找到在node.js下同步s3.getObject的方法,改写成java。 对不起;我对 node.js 的熟悉程度非常有限以上是关于如何在 AWS Lambda 中等待异步操作?的主要内容,如果未能解决你的问题,请参考以下文章
鉴于所有调用都是异步的,您如何在 lambda 中构建顺序 AWS 服务调用?