为什么AWS Lambda函数会针对单个事件多次调用?

时间:2015-08-13 08:02:43

标签: amazon-web-services amazon-s3 amazon-ec2 aws-sdk aws-lambda

我正在尝试创建执行以下过程的AWS Lambda函数。

  • 接收S3" Put"事件
  • 从S3获取fileA
  • 从S3调用lambda
  • 获取fileB
  • 只启动一个EC2实例
  • 为新的EC2实例创建标签

问题:意外启动了多个(5)实例。

已成功创建实例,但还会启动其他4个实例。共启动了5个实例。

日志

在此功能的Log Streams中,我为此调用找到了4个Streams。每个Stream都没有显示任何错误或异常,但似乎该函数重复执行。

试验

我猜测该功能已超时,然后重新运行。

然后,我将Timeout从5s更改为60s并将文件放在S3上。 它以某种方式影响了。仅出现2个Log Streams,第一个显示该功能仅执行一次,第二个显示该功能已执行两次。已启动实例的数量为3。

但是,我不知道为什么会启动多个(3)实例。

欢迎任何评论! 提前谢谢: - )

我的Lambda函数

我的Lambda功能如下。 (它被简化为隐藏凭证信息,但它没有失去其基本结构)

var AWS = require('aws-sdk');

function composeParams(data, config){
  var block_device_name = "/dev/xvdb";
  var security_groups = [
    "MyGroupName"
  ];
  var key_name = 'mykey';
  var security_group_ids = [
    "sg-xxxxxxx"
  ];
  var subnet_id = "subnet-xxxxxxx";

  // Configurations for a new EC2 instance
  var params = {
    ImageId: 'ami-22d27b22',      /* required */
    MaxCount: 1,                  /* required */
    MinCount: 1,                  /* required */
    KeyName: key_name,
    SecurityGroupIds: security_group_ids,
    InstanceType: data.instance_type,
    BlockDeviceMappings: [
      {
        DeviceName: block_device_name,
        Ebs: {
          DeleteOnTermination: true,
          Encrypted: true,
          VolumeSize: data.volume_size,
          VolumeType: 'gp2'
        }
      }
    ],
    Monitoring: {
      Enabled: false              /* required */
    },
    SubnetId: subnet_id,
    UserData: new Buffer(config).toString('base64'),
    DisableApiTermination: false,
    InstanceInitiatedShutdownBehavior: 'stop',
    DryRun: data.dry_run,
    EbsOptimized: false
  };

  return params;
}

exports.handler = function(event, context) {
  // Get the object from the event
  var s3 = new AWS.S3({ apiVersion: '2006-03-01' });
  var bucket = event.Records[0].s3.bucket.name;
  var key = event.Records[0].s3.object.key;

  // Get fileA
  var paramsA = {
    Bucket: bucket,
    Key: key
  };
  s3.getObject(paramsA, function(err, data) {
    if (err) {
      console.log(err);
    } else {
      var dataA = JSON.parse(String(data.Body));

      // Get fileB
      var paramsB = {
        Bucket: bucket,
        Key: 'config/config.yml'
      };
      s3.getObject(paramsB, function(err, data) {
        if (err) {
          console.log(err, err.stack);
        } else {
          var config = data.Body;
          /* Some process */

          // Launch EC2 Instance
          var ec2 = new AWS.EC2({ region: REGION, apiVersion: '2015-04-15' });
          var params = composeParams(dataA, config);
          ec2.runInstances(params, function(err, data) {
            if (err) {
              console.log(err, err.stack);
            } else {
              console.log(data);

              // Create tags for instance
              for (var i=0; i<data.Instances.length; i++){
                var instance = data.Instances[i];
                var params = {
                  Resources: [                /* required */
                    instance.InstanceId
                  ],
                  Tags: [                     /* required */
                    {
                      Key: 'Name',
                      Value: instance_id
                    },
                    {
                      Key: 'userID',
                      Value: dataA.user_id
                    }
                  ],
                  DryRun: dataA.dry_run
                };
                ec2.createTags(params, function(err, data) {
                  if (err) {
                    console.log(err, err.stack);
                  } else {
                    console.log("Tags created.");
                    console.log(data);
                  }
                });
              }
            }
          });
        }
      });
    }
  });
};

4 个答案:

答案 0 :(得分:12)

解决。

schema.xml添加到嵌套回调的最后部分可防止重复执行该函数。

context.succeed(message);

答案 1 :(得分:1)

我遇到了与较新的运行时(Node.JS v4.3)相同的问题。呼叫

context.callbackWaitsForEmptyEventLoop = false;

之前打电话

callback(...)

答案 2 :(得分:1)

在cloudwatch事件中签入每次调用的那个context.aws_request_id值。如果是

  1. 相同 ,因为aws函数引发了一些错误,因此无法重试。 使您的lambda幂等
  2. 不同 ,这是因为aws的连接超时
    lambda客户。检查aws客户端配置请求超时和
    连接超时值。

答案 3 :(得分:0)

最大事件年龄

当函数在执行前返回错误时,Lambda 会将事件返回到队列中,并在默认情况下最多 6 小时内尝试再次运行该函数。使用最长事件期限,您可以将队列中事件的生命周期配置为 60 秒到 6 小时。这允许您根据事件年龄删除任何不需要的事件。

最大重试次数

当函数在执行后返回错误时,默认情况下,Lambda 会尝试再运行两次。使用最大重试次数,您可以自定义从 0 到 2 的最大重试次数。这使您可以选择以更少或不重试的方式继续处理新事件。

配置 > 异步调用 > 重试尝试 您可以将其设置为 0-2

enter image description here

enter image description here

来源: https://aws.amazon.com/about-aws/whats-new/2019/11/aws-lambda-supports-max-retry-attempts-event-age-asynchronous-invocations/