在主程序之前在 lambda 中加载 S3 文件

Load S3 file in lambda before main procedure

我正在编写一个需要加载存储在 S3 中的密钥的 lambda 函数。它不会经常更改,所以我不想在每次调用 lambda 函数时都获取它,所以我想在容器启动时加载它一次,然后在 lambda 容器的生命周期内保留该值。

但是,由于异步方法 getObject 这导致了一个问题,因为当主要 module.export 代码获取 运行 时文件可能尚未加载(特别是如果这是第一个 运行 一会儿,正在创建容器)。

我已经使用 setTimeout 实现了一个解决方法,但我想看看推荐的方法是什么 运行d 这是什么,我的方法是否有任何问题,因为它感觉不对!

示例代码:

var AWS = require('aws-sdk')
var s3 = new AWS.S3();

var fileLoaded = false;

var params = {
  Bucket: 'bucket-name',
  Key: 'file-name'
};

s3.getObject(params, function(err, data) {
  if (err) console.log(err, err.stack); // an error occurred
  else {
      console.log('File loaded from S3');
      fileLoaded = true;
  }
});

exports.handler = (event, context, callback) => {

    console.log('I am in the main procedure, but i might not have the file yet')
    waitForFileLoadBeforeDoingSomething(event, context, callback)

};

function waitForFileLoadBeforeDoingSomething(event, context, callback){
    if(!fileLoaded){
        console.log('No file available to me yet, we will sleep')
        setTimeout(function(){
            waitForFileLoadBeforeDoingSomething(event, context, callback)
        }, 300)
    } else {
        console.log('I have the file!')
        doStuff(event, context, callback)
    }
}

function doStuff(event, context, callback){
    console.log('Now I can do the rest of the code')
    //Do the actual code here

    callback(null, 'success')
}

嘿嘿,异步代码和模块很快就会失控,不是吗。

您想在异步 s3.getObject() 调用准备就绪后立即调用您的回调。正确的位置是将工作回调放在 s3.getObject() 回调中。像这样,也去除其他绒毛

var AWS = require('aws-sdk')
var s3 = new AWS.S3();

var params = {
    Bucket: 'bucket-name',
    Key: 'file-name'
};

exports.handler = (event, context, callback) => {

    s3.getObject(params, function (err, data) {
        if (err) console.log(err, err.stack);
        else {
            callback(null, 'success')  // do after we have completed s3.getObject()
        }
    });
};

如果 exports.handler 可以多次调用,代码应该稍微调整一下(缓存结果)。而且我似乎没有意识到实际文件数据传递到哪里,但你明白了它的要点。

根据 pspi 的回答和更多研究,我认为更正确的解决方案是:

var AWS = require('aws-sdk')
var s3 = new AWS.S3();

var params = {
    Bucket: 'bucket-name',
    Key: 'file-name'
};

var fileLoaded = false;

exports.handler = (event, context, callback) => {

    if(!fileLoaded){
        s3.getObject(params, function (err, data) {
            if (err) console.log(err, err.stack);
            else {
                fileLoaded = true;
                doSomething(event, context, callback)
            }
        });
    } else {
        doSomething(event, context, callback)
    }

};

function doSomething(event, context, callback){

    //Do the actual work here

    callback(null, "success") //Then end
}

这似乎给出了预期的结果,即只从 S3 加载文件一次,但不允许在加载完成之前执行代码。如果它被快速多次冷调用,它将允许对 S3 的多个请求,但这不太可能,但仍然比每次调用该函数都要好。

这里有一个带有 promise 的更简洁的版本:

var AWS = require('aws-sdk')
var s3 = new AWS.S3();

var params = {
    Bucket: 'bucket-name',
    Key: 'file-name'
};

var fileData = null;

exports.handler = (event, context, callback) => {
    if(!fileData) 
        s3.getObject(params).promise().then(data) => {
             fileData = data;
             doSomething(event, context, callback);
         }).catch((err) => {
             callback.done(err);
         });
     else 
        doSomething(event, context, callback);
};

function doSomething(event, context, callback){
    // Do the actual work here
    // you can use fileData variable now to use your downloaded file!!
    callback(null, "success") // Then end
}

我喜欢预加载资源并使用承诺来控制流程。 "cold" 容器必须去获取文件,但是温暖的容器已经有了它。在模块设置中加载它,在 lambda 实际调用您的端点时,它只需要一点点领先。

var AWS = require('aws-sdk')
var s3 = new AWS.S3();

var fileDataPromise = getFileDataPromise();

exports.handler = (event, context, callback) => {
    fileDataPromise.then((fileData) => {
        doSomething(event, context, callback, fileData);
    })
};

function getFileDataPromise() {
    var params = {
        Bucket: 'bucket-name',
        Key: 'file-name'
    };

    return new Promise((resolve, reject) => {
        s3.getObject(params, (err, data) => {
            if (err) console.log(err, err.stack);
            else {
                resolve(data)
            }
        })
    })
}

function doSomething(event, context, callback, fileData) {
    // Do the actual work here
    callback(null, "success")
}