【问题标题】:AWS Lambda function times out but works on local instanceAWS Lambda 函数超时但适用于本地实例
【发布时间】:2019-03-12 18:43:41
【问题描述】:

我正在 AWS 上编写一个 NodeJS 应用程序,它执行以下操作:

  • 从远程 URL 读取 XML
  • 解析数据并将其作为 JSON 存储在 DynamoDB 上
  • 上传 8 张图片到 s3 存储桶

代码在我的本地节点实例上运行良好。整个过程通常需要不到 3 秒的时间来完成。我可以通过检查 AWS s3 存储桶和 DynamoDB 来确认这一点。

但是,当我将其打包为由 Lambda 运行时,即使超时设置为 5 分钟并最大化内存,操作也会过期。检查日志时,我注意到 axios 的承诺在 Lambda 上运行时从未返回。有人能发现我的代码有什么问题吗?谢谢。

仅供参考,我是 Lambda 开发的新手。我将代码部署到 Lambda 的方式是将封闭的 export.handler 方法添加到我的本地代码中,并将其与模块和 package.json 一起压缩。我当然可以使用一些指导来简化这个过程。我提到这一点是因为我不确定这是否与问题有关。

谢谢。

exports.handler = function(event, context, callback) {
async function main() {
    await getData();
    updateDB();
    updateAssets();
}

let dataStore = {};

let s3Assets = [
    'photo1',
    'photo2',
    'photo3',
    'photo4',
    'photo5',
    'logoLarge',
    'logoSmall',
    'logoCompany'
];
let s3Status = {};

s3Assets.map( (i) => {
    // Initialize the status of all assets with 0 (false)
    s3Status[i] = 0;
})

let s3Ready = () => {
    let count = 0;
    s3Assets.map( (i) => {
        count += s3Status[i];    
    })
    console.log(s3Status);
    console.log(`Upload Count: ${count}`);
    if (count === s3Assets.length) {
        console.log(`[SUCCESS] All assets are updated`);
        console.log('< END: updateAssets');
    }
}

let getData = () => {
    ... // code omitted
}

let handleAsset = (asset) => {
    let src = dataStore[asset];
    let destination = dataStore.prefix + asset + '.jpg';

    axios({
        method:'get',
        url: src,
        responseType:'stream'
    }).then( (response) => {
        let body = response.data.pipe(zlib.createGzip());
        let fileType = 'multipart/form-data';

        let s3Promise = s3.upload({
            Bucket: 'someBucket',
            Key: destination,
            Body: body,
            ContentType: fileType,
            ContentEncoding: 'gzip'
        }).promise();

        s3Promise.then( (data) => {
            s3Status[asset] = 1;
            console.log('\n');
            console.log(`[SUCCESS] s3 Upload: ${data.Location}`);
        }).then( () => {
            s3Ready();
        }).catch( (error) => {
            console.log(`[ERROR] s3 Upload: ${error}`);
        });
    }).catch( (error) => {
        console.log(`[ERROR] axios: ${error}`);
    });
}

let updateAssets = () => {
    console.log('> START: updateAssets');
    s3Assets.map( (i) => {
        handleAsset(i)
    });
}

let updateDB = () => {
    ... // code omitted
}

main(); 

}

【问题讨论】:

  • 我在我的 handleAssets 函数中的 axios 请求配置中添加了超时选项。正如预期的那样,axios 正在超时。
  • 混合 Promise 链和 async/await 会使调试变得有点困难。我建议重构 Promise 链以使用 async/await 和 try/catch。但是我看到的主要问题是您从不表示成功或失败。你永远不会调用callback 或返回响应对象
  • 您是如何配置 Lambda 函数的?是否配置为连接到 VPC 和子网?你能测试它是否有互联网连接(例如尝试从某个地方检索网页)吗?
  • @JohnRotenstein 谢谢。我没有使用 VPC。该函数能够更新 DynamoDB,所以我认为问题与处理 S3 上传的部分有关。
  • @bwest。谢谢。为了更清晰,我将在我的承诺链中添加返回值。

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


【解决方案1】:
  1. 我的猜测,既然你说这个程序在本地运行良好,是你错误地配置了security group associated with the Lambda。确保您拥有所有出站端口并且正确的入站端口已打开。要确认这是问题所在,或者至少获得更有针对性的反馈,请尝试向您添加更详细的日志记录 axios 请求。这是来自axios docs的一个非常详细的示例...


}).catch( (error) => {
    console.log(`[ERROR] axios: ${error}`);
    if (error.response) {
        // The request was made and the server responded with a status code
        // that falls out of the range of 2xx
        console.log('error.response.data'error.response.data);
        console.log('error.response.status',error.response.status);
        console.log('error.response.headers',error.response.headers);
    } else if (error.request) {
        // The request was made but no response was received
        // `error.request` is an instance of XMLHttpRequest in the browser and an instance of
        // http.ClientRequest in node.js
        console.log('Error request:',error.request);
    } else {
        // Something happened in setting up the request that triggered an Error
        console.log('Error message', error.message);
    }
    console.log(error.config);
});

您应该会收到关于 error.request 对象的错误,该错误可以帮助您进一步排除故障。

  1. 不太可能是您的问题的原因,但值得一提的是您链接到s3Promise.then() 方法将异步执行,因此s3Status[asset] = 1 不一定会在s3Ready(); 达到条件@ 之前完成987654330@ 所以你可以在这里有一个竞争条件。这不太可能,但无论如何我都会纠正,因为它可能会在未来引入错误。运行此示例以了解我的意思...


var test = ()=>new Promise(resolve=> resolve());

test()
  .then(()=> { setTimeout(()=>{console.log('one')},3000)})
  .then(()=>{console.log('two')})

这个分配s3Status[asset] = 1 是同步的,所以你应该尝试将你的s3Ready() 移动到同一个.then() 块中......

s3Promise.then( (data) => {
    s3Status[asset] = 1;
    console.log('\n');
    console.log(`[SUCCESS] s3 Upload: ${data.Location}`);
    s3Ready();
}).catch( (error) => {
    console.log(`[ERROR] s3 Upload: ${error}`);
});

【讨论】:

  • 谢谢。我在您的两个步骤中进行了详细的更改。 cloudwatch 日志显示多行 Error request: Writable 错误。我怀疑这是由于 lambda 角色没有足够的权限写入 s3 存储桶。但是,它具有分配给它的 AmazonS3FullAccess 策略。
  • 但是,s3 存储桶经常在 s3 列表页面上显示其状态为 Error,我觉得这很不寻常。
  • 我添加了错误作为答案。它不会让我在回复中发布错误,因为它超过了字符限制。谢谢。
猜你喜欢
  • 2022-01-17
  • 2019-04-03
  • 2020-10-05
  • 1970-01-01
  • 2016-07-08
  • 2019-09-05
  • 1970-01-01
  • 1970-01-01
  • 2017-07-22
相关资源
最近更新 更多