You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何我的AWS Lambda函数会创建两个Spot Instance Requests而非一个?

问题描述

我编写了如下Lambda函数,本应根据传入参数创建一个Spot实例请求,但实际却总是并行创建两个各含一个实例的Spot请求。CloudWatch显示函数仅触发一次,且成功率为100%,日志中无报错信息,我已将超时时间设置为20分钟,排除了超时导致的重复执行可能。请问为何会出现这种情况?我仅需要创建一个请求而非两个,恳请提供帮助。

我的Lambda代码:

var AWS = require('aws-sdk');
var ec2 = new AWS.EC2({ region: "eu-west-1" });
var userData = `#!/bin/bash echo "hello there" `;
var userDataEncoded = new Buffer.from(userData).toString('base64');
var params = {
 InstanceCount: 1,
 LaunchSpecification: {
 ImageId: "ami-xxxxxxxxx",
 InstanceType: "c4.2xlarge",
 KeyName: "xxxxxxx",
 SubnetId: "subnet-xxxxxxxxxx",
 Placement: { AvailabilityZone: "eu-west-1a" },
 SecurityGroupIds: [ "sg-xxxxxxxxxx" ],
 UserData: userDataEncoded
 },
 SpotPrice: "0.8",
 BlockDurationMinutes: 180,
 Type: "one-time"
};
exports.handler = async (event, context) => {
 await ec2.requestSpotInstances(params, function (err, data) {
 if (err) {
 console.log("error");
 } else {
 console.log("starting instance");
 context.succeed('Completed');
 return {
 statusCode: 200,
 body: JSON.stringify('success!'),
 };
 }
 }).promise();
};
解决方案

问题的核心是你同时混用了AWS SDK的回调函数模式和Promise/async-await模式,这直接导致了两次独立的Spot实例请求被发起。

具体原因分析

AWS SDK的方法支持两种调用方式:

  1. 回调模式:传入回调函数作为参数,方法会立即发起请求,完成后调用回调。
  2. Promise模式:调用.promise()方法,返回一个Promise对象,可配合await使用。

你的代码中,既给ec2.requestSpotInstances传入了回调函数,又链式调用了.promise()——这会触发两次API调用:一次是回调模式的请求,一次是Promise模式的请求。这就是为什么你会看到两个Spot实例请求被创建。

另外,在Lambda的async handler中,调用context.succeed是多余的(甚至会导致执行流程混乱),因为async函数的返回值会自动作为Lambda的响应,无需手动调用上下文方法。

修正后的代码

下面是修复后的代码,只使用Promise/async-await模式,确保只发起一次请求:

var AWS = require('aws-sdk');
var ec2 = new AWS.EC2({ region: "eu-west-1" });
// 修复userData的格式:#!/bin/bash需要单独一行,否则脚本无法正确执行
var userData = `#!/bin/bash
echo "hello there"`;
var userDataEncoded = new Buffer.from(userData).toString('base64');
var params = {
 InstanceCount: 1,
 LaunchSpecification: {
 ImageId: "ami-xxxxxxxxx",
 InstanceType: "c4.2xlarge",
 KeyName: "xxxxxxx",
 SubnetId: "subnet-xxxxxxxxxx",
 Placement: { AvailabilityZone: "eu-west-1a" },
 SecurityGroupIds: [ "sg-xxxxxxxxxx" ],
 UserData: userDataEncoded
 },
 SpotPrice: "0.8",
 BlockDurationMinutes: 180,
 Type: "one-time"
};

exports.handler = async (event, context) => {
  try {
    // 仅使用Promise模式,移除回调函数
    const data = await ec2.requestSpotInstances(params).promise();
    console.log("Starting spot instance request:", data);
    return {
      statusCode: 200,
      body: JSON.stringify('Success!'),
    };
  } catch (err) {
    console.error("Error requesting spot instance:", err);
    return {
      statusCode: 500,
      body: JSON.stringify('Failed to create spot instance request'),
    };
  }
};

额外优化点

  1. 修复了userData格式:原代码中#!/bin/bash和echo命令在同一行,这会导致脚本执行失败,修正后将shebang单独放在第一行。
  2. 使用try/catch处理错误:替代回调模式的错误处理,更符合async函数的编码习惯,也能更清晰地捕获和记录错误。
  3. 移除了多余的context.succeed:Lambda的async handler会自动处理函数的完成状态,返回响应对象即可。

内容的提问来源于stack exchange,提问作者Jan v

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 15:47:31