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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/31633912/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 19:02:06  来源:igfitidea点击:

How to wait for async actions inside AWS Lambda?

node.jsaws-lambda

提问by st78

I am trying to process uploaded file in S3. Since getObjectis asyncronous main function ends before processing is done, and AWS kills lambda in 3-4 seconds.

我正在尝试在 S3 中处理上传的文件。由于getObject是异步的,main 函数在处理完成之前就结束了,AWS 在 3-4 秒内杀死了 lambda。

Even worse, processing method also has async operations in it - it makes http calls.

更糟糕的是,处理方法中也有异步操作——它进行 http 调用。

On high level, my code looks like:

在高层次上,我的代码如下所示:

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);
  });
}

I find out that there is async in nodejs but it is not included by amazon; Both require('async') or require('sleep) causes errors.

我发现 nodejs 中有 async,但它不包含在 amazon 中;require('async') 或 require('sleep) 都会导致错误。

Lambda timeout configured to 60 seconds, but it exits in 3-4 seconds.

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

回答by Neo

The life of a dev is constantly changing and we now have NodeJS 8 on lambda. For anyone looking at this now check out:

开发人员的生活在不断变化,我们现在在 lambda 上拥有 NodeJS 8。对于现在正在查看此内容的任何人,请查看:

Lambda node 8.10 vs node 6.10 comparison: https://aws.amazon.com/blogs/compute/node-js-8-10-runtime-now-available-in-aws-lambda/

Lambda 节点 8.10 与节点 6.10 比较:https: //aws.amazon.com/blogs/compute/node-js-8-10-runtime-now-available-in-aws-lambda/

Basics of JS async: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function

JS 异步基础:https: //developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function

Even more aws sdk examples: https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/using-promises.html

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

Details on wtf the .promise() method is in the first link: https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Request.html#promise-property

有关 wtf .promise() 方法的详细信息在第一个链接中:https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Request.html#promise-property

Here is my take at a basic example (try pasting into your own lambda):

这是我对一个基本示例的看法(尝试粘贴到您自己的 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'
};

The above yields:

以上产生:

enter image description here

在此处输入图片说明

As you can see it waited 12 seconds without killing my function :)

如您所见,它等待了 12 秒而没有终止我的功能:)

TODO more than one thing per await, use Promise.all([]) syntax like this:

TODO 每个等待不止一件事,使用 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'
}; 

Orignal Answer Below

下面的原始答案

I had the exact same issue on my hands.

我手上有完全相同的问题。

The problem is the javascript event loop is empty so Lambda thinks it's done.

问题是 javascript 事件循环是空的,所以 Lambda 认为它已经完成了。

This is how I solved this problem. I realize this is not ideal, and I wish there was a better way, but I didn't want to a) add libraries, b) coordinate lambda invocations, or c) switch to another language.

这就是我解决这个问题的方法。我意识到这并不理想,我希望有更好的方法,但我不想 a) 添加库,b) 协调 lambda 调用,或 c) 切换到另一种语言。

At the end of the day it works.

在一天结束时它会起作用。

    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

http://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-context.html

回答by honkskillet

Using async/await

使用异步/等待

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;
};

回答by Jose L Ugia

asyncis not included but that does not mean you cannot add it yourself. Simply add the package locally (npm install async), and include the node_modulesfolder in your ZIP before uploading your Lambda function.

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

If you want to handle dev dependencies separately (e.g.: test, aws-sdkto execute your function locally, etc), you can add them under devDependenciesin your package.json. Also, if you want to automate the process of developing, testing, deploying and promoting your code, these two repos will turn out to be very handy.

如果您想单独处理开发依赖项(例如:测试、aws-sdk在本地执行您的函数等),您可以将它们添加到devDependencies您的package.json. 此外,如果您想自动化开发、测试、部署和推广代码的过程,这两个存储库将非常方便。

Grunt routine to test, package and deploy your lambdas

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

Command line tool for running and deploying your lambda functions

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

回答by SilentDirge

Think of Lambda simply as a program that you can run in a certain amount of time. The fact that you make asynchronous calls is nice since the (virtual) processor can possibly interleave those calls. However, if any part of your Lambda program takes longer to complete than the time allotted, well, execution will fail. That's the compromise you make and is how Amazon makes money; by selling you more time or memory.

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

To fix that on your end you can increase the memory your Lambda function is allotted. This not only increases your RAM but also the speed of your virtual processor. Another thing you can do is increase the timeout. AWS Lambda now allows you up to 512 MB of RAM and up to 5 minutes of processing time. As of this post those numbers may have changed so check herefor the latest limits. To change this setting, go to your function, then configuration and finally advanced.

为了解决这个问题,您可以增加分配给您的 Lambda 函数的内存。这不仅会增加您的 RAM,还会增加您的虚拟处理器的速度。您可以做的另一件事是增加超时。AWS Lambda 现在允许您使用高达 512 MB 的 RAM 和长达 5 分钟的处理时间。截至本文发布时,这些数字可能已更改,因此请在此处查看最新限制。要更改此设置,请转到您的功能,然后是配置,最后是高级。

回答by rk2

I think your lambda function should end with a context.done()call. For instance, try adding it this way:

我认为您的 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");
    }
});

回答by pedro.olimpio

If you want to use require('async');pack or require('sleep');pack as well you need to upload your function as a zipfile like this:

如果您还想使用require('async');pack 或require('sleep');pack ,您需要将您的函数上传为这样的zip文件:

Creating a Deployment Package (Node.js)

创建部署包 (Node.js)

Zipall the content of the folder as I explain in this question as well:

Zip正如我在这个问题中所解释的,文件夹的所有内容也是:

MQTT in AWS Lambda function for Alexa Javascript

用于 Alexa Javascript 的 AWS Lambda 函数中的 MQTT

About the synchronous processing, you can use require('async');normally, is just to use the async.seriesfunction like this:

关于同步处理,可以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) {
        //...
    }
    //....
});

This way the lambdafunction will works synchronously.

这样,该lambda函数将同步工作。

I hope help you.

我希望能帮到你。

回答by Neil

You might want to make a synchronouscall instead; since you seem to be processing your file in the same lambda function.

您可能想synchronous拨打电话;因为您似乎在同一个 lambda 函数中处理您的文件。

If for some reason you want to get a callback; you can do that by either directly invoking lambda or via something that would produce a lambda event. Note that lambda functions are supposed be stateless; so you should pass in all of the needed information.

如果由于某种原因您想获得回调;你可以通过直接调用 lambda 或通过一些会产生 lambda 事件的东西来做到这一点。请注意,lambda 函数应该是无状态的;所以你应该传递所有需要的信息。