簡體   English   中英

AWS Lambda函數流程

[英]AWS Lambda function flow

我的函數在lambda中的流動方式存在一些問題。 我試圖獲取存儲在S3中的值,將其增加,然后放回去。 但是,我的程序並沒有達到我的預期。 我正在使用異步瀑布來運行功能流程。

這是我的代碼:

let AWS = require('aws-sdk');
let async = require('async');

let bucket = "MY_BUCKET";
let key = "MY_FILE.txt";

exports.handler = async (event) => {
    let s3 = new AWS.S3();
    async.waterfall([
        download,
        increment,
        upload
        ], function (err) {
            if (err) {
                console.error(err);
            } else {
                console.log("Increment successful");
            }

            console.log("test4");
            return null;
        }
    );
    console.log("test5");

    function download(next) {
        console.log("test");
        s3.getObject({
            Bucket: bucket,
            Key: key
        },
        next);
    }
    function increment(response, next) {
        console.log("test2");
        console.log(response.Body);
        let newID = parseInt(response.Body, 10) + 1;
        next(response.ContentType, newID);
    }
    function upload(contentType, data, next) {
        console.log("test3");
        s3.putObject({
            Bucket: bucket,
            Key: key,
            Body: data,
            ContentType: contentType
        },
        next);
    }
};

我只在日志上得到test和test5。 我的印象是,在下載功能之后,如果可以的話,增量應該運行,或者在出現錯誤的情況下,瀑布末尾的回調函數應該運行。 該程序不會在執行時給出錯誤,並且似乎不會執行任何一個功能。

有人可以指導我了解我所缺少的嗎?

編輯:所以看來我的問題與我的函數聲明有關。 默認模板將其聲明為async(event)。 我認為這很奇怪,因為通常將它們聲明為(事件,上下文,回調)。 切換到更高版本(甚至沒有異步的(事件))可以解決此問題。 看來我的問題是將函數作為異步調用。 這阻止了瀑布異步調用?? 有人可以詳細說明嗎?

您的問題是您的處理程序被聲明為async函數,該函數將自動為您創建一個Promise,但是由於您根本沒有等待,因此您的函數實際上是同步結束的。

有兩種方法可以解決此問題,我們將介紹所有方法。

  1. 不要使用promise,請使用回調,因為async庫是為使用而設計的。
  2. 不要使用async庫或回調,而應使用async / await
  3. 將兩者混合在一起,做出自己的承諾,然后手動resolve / reject它。

1.不要使用諾言

在此解決方案中,您將刪除async關鍵字,並添加lambda傳遞給您的回調參數。 簡單地調用它將結束lambda,向其傳遞錯誤將表明該函數失敗。

// Include the callback parameter ────┐
exports.handler = (event, context, callback) => {
    const params =[
      download,
      increment,
      upload
    ]
    async.waterfall(params, (err) => {
      // To end the lambda call the callback here ──────┐
      if (err) return callback(err);  //   error case ──┤
      callback({ ok: true });         // success case ──┘
    });
};

2.使用async / await

這里的想法是不使用回調樣式,而是使用基於Promise的async / await關鍵字。 如果返回承諾,lambda將使用該承諾來處理lambda完成而不是回調。

如果您具有帶有async關鍵字的函數,它將自動返回對您的代碼透明的promise。

為此,我們需要修改您的代碼以不再使用async庫,並使其他函數也異步。

const AWS = require('aws-sdk');
const s3 = new AWS.S3();
const Bucket = "MY_BUCKET";
const Key = "MY_FILE.txt";

async function download() {
  const params = {
    Bucket,
    Key
  }
  return s3.getObject(params).promise(); // You can await or return a promise
}

function increment(response) {
  // This function is synchronous, no need for promises or callbacks
  const { ContentType: contentType, Body } = response;
  const newId = parseInt(Body, 10) + 1;
  return { contentType, newId };
}

async function upload({ contentType: ContentType, newId: Body }) {
  const params = {
    Bucket,
    Key,
    Body,
    ContentType
  };
  return s3.putObject(params).promise();
}

exports.handler = async (event) => {
    const obj = await download(); // await the promise completion
    const data = increment(obj);  // call synchronously without await
    await upload(data)

    // The handlers promise will be resolved after the above are
    // all completed, the return result will be the lambdas return value.
    return { ok: true };
};

3.混合承諾和回調

在這種方法中,我們仍然使用基於回調的異步庫,但是基於外部函數的承諾。 很好,但是在這種情況下,我們需要手動做出自己的承諾,並在瀑布處理程序中解決或拒絕它。

exports.handler = async (event) => {
  // In an async function you can either use one or more `await`'s or
  // return a promise, or both.
  return new Promise((resolve, reject) => {
    const steps = [
      download,
      increment,
      upload
    ];
    async.waterfall(steps, function (err) {
      // Instead of a callback we are calling resolve or reject
      // given to us by the promise we are running in.
      if (err) return reject(err);
      resolve({ ok: true });
    });
  });
};

雜項

除了您遇到的回調與承諾的主要問題之外,還有一些我注意到的小問題:

其他1

你應該使用const ,而不是let大部分的時間。 唯一應該使用let是,如果您打算重新分配變量,並且大多數時候您不應該這樣做。 我會用一種不需要用let編寫代碼的方式來挑戰您,它將總體上幫助您改善代碼。

其他2

您在瀑布式步驟之一中遇到問題,在該步驟中返回response.ContentType作為next的第一個參數,這是一個錯誤,因為它將把它解釋為錯誤。 回調的簽名是next(err, result)因此您應該在增量和上傳功能中執行以下操作:

function increment(response, next) {
  const { ContentType: contentType, Body: body } = response;
  const newId = parseInt(body, 10) + 1;
  next(null, { contentType, newId }); // pass null for err
}
function upload(result, next) {
  const { contentType, newId } = result;
  s3.putObject({
      Bucket: bucket,
      Key: key,
      Body: newId,
      ContentType: contentType
  },
  next);
}

如果在調用下一個async時未傳遞err的nullundefined ,則將其解釋為錯誤,並跳過其余瀑布,直接進入完成處理程序,並傳入該錯誤。

其他3

您需要了解的context.callbackWaitsForEmptyEventLoop是,即使您正確完成了該功能,以上述方法之一,您的Lambda仍可能會掛起並最終超時而不是成功完成。 根據此處的代碼示例,您可能不必擔心這一點,但是發生這種情況的原因是,如果您碰巧沒有正確關閉某些內容,例如與數據庫或websocket的持久連接或類似的內容, 。 在lambda執行開始時將此標志設置為false會導致該進程退出,而不管使事件循環保持活動的任何狀態,並將迫使它們不合常規地關閉。

在以下情況下,您的lambda可以成功完成工作,甚至返回成功結果,但它將一直掛起直到超時,並報告為錯誤。 甚至可以反復觸發它,具體取決於觸發方式。

exports.handler = async (event) => {
  const db = await connect()
  await db.write(data)
  // await db.close() // Whoops forgot to close my connection!
  return { ok: true }
}

在那種情況下,簡單地調用db.close()可以解決問題,但有時它並不清楚在事件循環中徘徊着什么,您只需要大錘類型的解決方案來關閉lambda,這就是context.callbackWaitsForEmptyEventLoop = false是對於!

exports.handler = async (event) => {
  context.callbackWaitsForEmptyEventLoop = false
  const db = await connect()
  await db.write(data)
  return { ok: true }
}

函數返回后,上面的代碼將立即完成lambda,殺死所有連接或仍然存在於事件循環中的任何其他東西。

您的函數在解析瀑布之前終止。 也就是說,根本不執行異步調用。 這就是為什么您看不到waterfall函數中有任何console.log調用,而僅看到在調用async.waterfall之后立即被同步調用的async.waterfall

不確定AWS Lambda對async.waterfall的支持程度如何,但是由於原生支持promise並執行相同的功能(使用較少的loc),因此可以改用Promise。 您的代碼如下所示:

module.exports.handler = (event,context) =>
    s3.getObject({
        Bucket: bucket,
        Key: key
    }).promise()
        .then(response => ({
            Body: parseInt(response.Body, 10) + 1,
            ContentType: response.contentType,
        }))
        .then(modifiedResponse => s3.putObject({
            Bucket: bucket,
            Key: key,
            Body: modifiedResponse.data,
            ContentType: modifiedResponse.contentType}).promise())
        .catch(err => console.error(err));

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM