[英]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,但是由於您根本沒有等待,因此您的函數實際上是同步結束的。
有兩種方法可以解決此問題,我們將介紹所有方法。
async
庫是為使用而設計的。 async
庫或回調,而應使用async
/ await
。 resolve
/ reject
它。 在此解決方案中,您將刪除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 ──┘
});
};
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 };
};
在這種方法中,我們仍然使用基於回調的異步庫,但是基於外部函數的承諾。 很好,但是在這種情況下,我們需要手動做出自己的承諾,並在瀑布處理程序中解決或拒絕它。
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 });
});
});
};
除了您遇到的回調與承諾的主要問題之外,還有一些我注意到的小問題:
你應該使用const
,而不是let
大部分的時間。 唯一應該使用let
是,如果您打算重新分配變量,並且大多數時候您不應該這樣做。 我會用一種不需要用let
編寫代碼的方式來挑戰您,它將總體上幫助您改善代碼。
您在瀑布式步驟之一中遇到問題,在該步驟中返回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的null
或undefined
,則將其解釋為錯誤,並跳過其余瀑布,直接進入完成處理程序,並傳入該錯誤。
您需要了解的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.