如何在 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 服务调用?

如何在返回集合的 lambda 中使用异步

AWS SDK (PHP):异步调用 lambda 函数,检索状态

如何使用SAM通过API网关配置异步lambda调用?

AWS Lambda 异步并发限制

如何使用无服务器框架从另一个 lambda 异步调用 lambda