【问题标题】:Node.js / AWS Lambda ends before SNS Push has been sentNode.js / AWS Lambda 在发送 SNS 推送之前结束
【发布时间】:2016-05-07 03:44:09
【问题描述】:

也许擅长 Node.js 的人可以解决这个问题。

在 Amazon Web Service 上编写和测试我的代码时,我注意到 Lambda 有时会在 SNS 发布之前结束函数。我认为问题可能正在发生,因为“context.done”被提前调用。但从逻辑上讲,这种情况不应该发生。重要信息:代码正常工作! (除了我之前提到的事情)。

代码的基本思想是:一旦 DynamoDB 中的条目发生变化,Lambda 就会做出反应并通过 SNS 推送通知“所有者”。

感谢您的帮助!


此代码在 Lambda 实例上运行。

console.log('Loading function');
var aws = require('aws-sdk');
var doc = require('dynamodb-doc');
var dynamo = new doc.DynamoDB();
exports.handler = function(event, context){

    var target = null;
    var status = null;

    event.Records.forEach(function(record){

            console.log("Function Invoked");
            console.log("Function Event: "+record.eventName);

            if(record.eventName === "MODIFY")
            {
                db_record_exists(record, function(r) {
                    if (r) {

                        if(r.handle == "true"){
                            creator = r.creator;
                            participant = r.participant;

                            creator = JSON.stringify(creator);
                            status = JSON.stringify(record.dynamodb.NewImage.status.S);
                            creator = creator.replace(/"/g, "");
                            status = status.replace(/"/g, "");

                            participant = JSON.stringify(record.dynamodb.NewImage.participant.S);
                            participant = participant.replace(/"/g, "");

                            console.log("creator: "+ creator);
                            console.log("status: "+ status);
                            console.log("participant is: "+ participant);

                            getCredentialsMovement(creator,participant,function(response){
                                console.log("RESPONSE: "+ response);

                                sendSNSmovement(response, status, context);

                            });

                        }else{
                            context.done();
                        }
                    }
                });
            }else if(record.eventName == "INSERT"){
                db_record_exists(record, function(r) {
                    if (r) {

                        if(r.handle == "true"){
                            creator = r.creator;
                            participant = r.participant;

                            creator = JSON.stringify(creator);
                            status = JSON.stringify(record.dynamodb.NewImage.status.S);
                            creator = creator.replace(/"/g, "");
                            status = status.replace(/"/g, "");

                            participant = JSON.stringify(record.dynamodb.NewImage.participant.S);
                            participant = participant.replace(/"/g, "");

                            console.log("creator: "+ creator);
                            console.log("status: "+ status);
                            console.log("participant is: "+ participant);

                            getCredentialsInvitation(creator,participant,function(response){
                                console.log("RESPONSE: "+ response);

                                sendSNSinvitation(response, context);

                            });

                        }else{
                            context.done();
                        }
                    } else context.done();
                });
            }else{
                context.done();
            }
        }
    );
};



function db_record_exists(record, callback){

    var users = {};
    users.creator = record.dynamodb.NewImage.creator.S;
    users.participant = record.dynamodb.NewImage.participant.S;

    if(users.creator != users.participant){
        users.handle = "true";
    }else{
        users.handle = "false";
    }

    callback(users);
}

function shutdown(context){
    context.done();
}


function sendSNSmovement(creator_information, status, context){

    var sns = new aws.SNS();
    var payload_negative = creator_information.name + " has declined your event!";
    var payload_positive = creator_information.name + " has accepted your event!";
    //var payload_positive = "Positive";

    var payload_accepted = {
        "GCM": "{ \"data\": { \"message\": \"Your event has been accepted!!\"} }"
    };

    var payload_declined = { "GCM": "{ \"data\":  { \"message\": \"Your event has been declined!\" } }" };

    //   console.log("Standart Message as JSON: "+ payload_declined.GCM);

    payload_declined.GCM = "{ \"data\": { \"message\": \""+payload_negative+"\" } }";
    payload_accepted.GCM = "{ \"data\": { \"message\": \""+payload_positive+"\" } }";


    //   console.log("Standart Message as JSON: "+ payload_declined.GCM);
    //  console.log("Standart Message as String: "+ JSON.stringify(payload_declined));


    payload_accepted = JSON.stringify(payload_accepted);
    payload_declined = JSON.stringify(payload_declined);

    var payload;

    if(status == "true"){
        payload = payload_accepted;
    }else{
        payload = payload_declined;
    }


    var params = {
        TargetArn: creator_information.arn,
        MessageStructure: 'json',
        Message: payload
    };

    sns.publish(
        params, function(err, data) {
            if (err) {
                console.log(err.stack);
// Notify Lambda that we are finished, but with errors

                context.done(err, 'Brians Function Finished with Errors!');

            }else{
                console.log('push sent');
                console.log(data);
// Notify Lambda that we are finished

                context.done();
            }

        });
}

function sendSNSinvitation(creator_information,context){

    var sns = new aws.SNS();
    var payload_message = creator_information.name + " has invited you to an event!";

    var payload = {
        "GCM": "{ \"data\": { \"message\": \"Your event has been accepted!!\"} }"
    };


    payload.GCM = "{ \"data\": { \"message\": \""+payload_message+"\" } }";



    payload = JSON.stringify(payload);


    var params = {
        TargetArn: creator_information.arn,
        MessageStructure: 'json',
        Message: payload
    };

    sns.publish(
        params, function(err, data) {
            if (err) {
                console.log(err.stack);

// Notify Lambda that we are finished, but with errors

                context.done(err, 'Brians Function Finished with Errors!');

            }else{
                console.log('push sent');
                console.log(data);
// Notify Lambda that we are finished

                context.done();
            }
        });
}

function getCredentialsMovement(creator,participant,callback) {

    var params_name = {

        TableName : "users",
        Key : {
            "id" : participant
        },

        ProjectionExpression: 'person_name'

    };
    var params_arn = {

        TableName : "users",
        Key : {
            "id" : creator
        },

        ProjectionExpression: 'arn'

    };

    dynamo.getItem(params_name, function(err, data) {

        var response = {};

        if (err) {
            console.log(err);
            return err;
        }
        else {
            console.log("creators name is: "+data.Item.person_name);
            response.name = data.Item.person_name;
            dynamo.getItem(params_arn, function(err, data) {


                if (err) {
                    console.log(err);
                    return err;
                }
                else {
                    console.log(data.Item.arn);

                    response.arn = data.Item.arn;

                    return callback(response);
                }
            });


        }
    });

}
function getCredentialsInvitation(creator,participant,callback) {

    var params_name = {

        TableName : "users",
        Key : {
            "id" : creator
        },

        ProjectionExpression: 'person_name'

    };
    var params_arn = {

        TableName : "users",
        Key : {
            "id" : participant
        },

        ProjectionExpression: 'arn'

    };

    dynamo.getItem(params_name, function(err, data) {

        var response = {};

        if (err) {
            console.log(err);
            return err;
        }
        else {
            console.log("creators name is: "+data.Item.person_name);
            response.name = data.Item.person_name;
            dynamo.getItem(params_arn, function(err, data) {


                if (err) {
                    console.log(err);
                    return err;
                }
                else {
                    console.log(data.Item.arn);

                    response.arn = data.Item.arn;

                    return callback(response);
                }
            });


        }
    });

}

【问题讨论】:

    标签: javascript node.js amazon-web-services aws-lambda


    【解决方案1】:

    我不知道你的代码到底哪里出错了,但我有一些调试它的建议:

    1. 为您的 Lambda 函数启用 CloudWatch Logs。
    2. 确保通过代码的每条唯一路径都有独特的日志消息,这样您就可以区分哪些场景有效,哪些无效。
    3. 我统计了对context.done() 的9 次调用,分布在多个函数中。您应该将其减少到一个或两个(成功/错误)。不要让每个函数都调用context.done(),而是让它们使用回调将状态返回给您的顶级处理程序。
    4. 您可以考虑使用 Promises 或 async 之类的库来使回调链更易于理解。

    【讨论】:

    • 感谢您的回答。我已经尝试了所有这些事情。亚马逊的一位工程师告诉我,我是否使用多个“context.done()”调用并不重要,只要它们在逻辑上是分开的。您还有什么建议吗?
    • 如果只有一个语句,逻辑更容易验证。尝试使用 MODIFY 运行 100 次,使用 INSERT 事件运行 100 次,并计算每条路径的成功率。这可能会帮助您了解它是什么类型的问题,以及您应该查看代码的哪个位置。
    【解决方案2】:

    似乎 contex.done() 调用在逻辑上不是独立的。您有一个 for 循环,其中包含异步调用。如果事件中有多条记录,它们有可能同时被处理,其中一条可能会调用 context.done() 而另一条仍未完成。

    假设您有两个 MODIFY 事件进入此代码,第一个 db_record_exist 函数返回 r.handle = "true",第二个返回 false。第一个将继续异步调用 getCredentials(dynamo.getItem)。同时将处理第二条记录并调用 context.done() 从而结束 lambda 函数而不等待来自 dynamo 的响应

    【讨论】:

      猜你喜欢
      • 2021-08-18
      • 2017-12-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-01-12
      • 1970-01-01
      • 2016-03-16
      • 2015-09-28
      相关资源
      最近更新 更多