关于node.js:如何在AWS Lambda中等待异步操作?

How to wait for async actions inside AWS Lambda?

我正在尝试处理S3中上载的文件。因为getobject是异步的,所以主函数在处理完成之前结束,而aws在3-4秒内杀死lambda。

更糟糕的是,处理方法中还包含异步操作——它进行HTTP调用。

从高层来看,我的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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中有async,但它不包含在Amazon中;两者都要求("async")或要求("sleep")会导致错误。

lambda超时配置为60秒,但在3-4秒后退出。


开发人员的生活在不断变化,我们现在在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 Async基础:https://developer.mozilla.org/en-us/docs/web/javascript/reference/statements/async_函数

更多的AWS SDK示例:https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/using-commandes.html

有关wtf.promise()方法的详细信息,请参见第一个链接:https://docs.aws.amazon.com/awsjavascriptssdk/latest/aws/request.html promise属性

下面是我的一个基本示例(尝试粘贴到您自己的lambda中):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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'
};

上述结果:

enter image description here

如你所见,它等待了12秒,却没有破坏我的功能:)

TODO每个wait有多个内容,请使用promise.all([])语法,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
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)切换到另一种语言。

在一天结束的时候,它开始工作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
    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


使用异步/等待

1
2
3
4
5
6
7
8
9
10
11
12
13
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;
};


不包括async,但这并不意味着您不能自己添加它。只需在本地添加包(npm install async),并在上载lambda函数之前在zip中包含node_modules文件夹。

如果要单独处理dev依赖项(如test、aws-sdk以在本地执行函数等),可以将它们添加到devDependencies下的package.json中。此外,如果您希望自动化代码的开发、测试、部署和升级过程,那么这两个repo将变得非常方便。

用于测试、打包和部署lambda的grunt例程

运行和部署lambda函数的命令行工具


把lambda简单地看作是一个可以在一定时间内运行的程序。您进行异步调用的事实很好,因为(虚拟)处理器可能会交错这些调用。但是,如果lambda程序的任何部分的完成时间比分配的时间长,那么,执行将失败。这是你所做的妥协,也是亚马逊如何赚钱;通过卖给你更多的时间或记忆。

为了解决这个问题,你可以增加内存,你的lambda函数被分配了。这不仅提高了RAM,而且还提高了虚拟处理器的速度。您可以做的另一件事是增加超时。AWS lambda现在允许您最多512 MB的RAM和最多5分钟的处理时间。从这篇文章开始,这些数字可能已经改变了,所以请检查这里的最新限制。要更改此设置,请转到您的功能,然后转到配置,最后转到高级。


我认为lambda函数应该以context.done()调用结束。例如,尝试通过以下方式添加:

1
2
3
4
5
6
7
8
9
10
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");
    }
});


如果您还想使用require('async');包或require('sleep');包,则需要将您的功能作为zip文件上载,如下所示:

创建部署包(node.js)

zip我在这个问题中也解释了文件夹的所有内容:

用于Alexa javascript的AWS lambda函数中的mqtt

关于同步处理,您可以正常使用require('async');,只是使用async.series这样的功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
    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功能将同步工作。

我希望能帮助你。


您可能需要进行一个synchronous调用,因为您似乎在用相同的lambda函数处理您的文件。

如果出于某种原因,您希望得到一个回调;您可以通过直接调用lambda或通过一些可以生成lambda事件的方法来实现这一点。注意lambda函数应该是无状态的;所以您应该传递所有需要的信息。