S3でアップロードされたファイルを処理しようとしています。 getObjectは非同期メイン関数が処理が完了する前に終了するため、AWSは3〜4秒でラムダを強制終了します。
さらに悪いことに、処理メソッドには非同期操作もあります-HTTP呼び出しを行います。
高レベルでは、私のコードは次のようになります。
exports.handler = function(event, context) {
// Get the object from the event and show its content type
var bucket = event.Records[0].s3.bucket.name;
var key = event.Records[0].s3.object.key;
var params = {
Bucket: bucket,
Key: key
};
s3.getObject(params, function(err, data) {
if (err) {
...
} else {
processFile(data.Body.toString(), 0);
console.log("ok");
}
});
//need to wait here till processFile is done
};
processFile = function(content, start) {
... build url to call
http.get(url, function(res) {
console.log("Got response: " + res.statusCode + ");
processFile(content, start + 1);
});
}
Nodejsには非同期がありますが、Amazonには含まれていません。 require( 'async')またはrequire( 'sleep)の両方がエラーを引き起こします。
Lambdaタイムアウトは60秒に設定されていますが、3〜4秒で終了します。
開発者の生活は絶えず変化しており、NodeJS 8はラムダ上にあります。これを見ている人は今チェックしてください:
Lambdaノード8.10とノード6.10の比較: https://aws.Amazon.com/blogs/compute/node-js-8-10-runtime-now-available-in-aws-lambda/
JS非同期の基本: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
さらに多くのAWS SDKの例: https://docs.aws.Amazon.com/sdk-for-javascript/v2/developer-guide/using-promises.html
.promise()メソッドのwtfの詳細は最初のリンクにあります: https://docs.aws.Amazon.com/AWSJavaScriptSDK/latest/AWS/Request.html#promise-property
これが基本的な例です(独自のラムダに貼り付けてみてください):
exports.handler = async (event) => {
function wait(){
return new Promise((resolve, reject) => {
setTimeout(() => resolve("hello"), 2000)
});
}
console.log(await wait());
console.log(await wait());
console.log(await wait());
console.log(await wait());
console.log(await wait());
console.log(await wait());
return 'exiting'
};
上記の結果:
あなたが見ることができるように、それは私の機能を殺すことなく12秒待った:)
待機ごとに複数のことを行うには、次のようなPromise.all([])構文を使用します。
exports.handler = async (event) => {
var uploadPromises = [];
folder.files.forEach(file => {
uploadPromises.Push( s3.putObject({
Bucket: "mybucket",
Key: file.name,
Body: file.data
}).promise());
});
await Promise.all(uploadPromises);
return 'exiting'
};
以下のオリジナルの回答
私の手にはまったく同じ問題がありました。
問題は、javascriptイベントループが空なので、Lambdaが完了したと判断することです。
これが私がこの問題を解決した方法です。私はこれが理想的ではないことを認識しており、より良い方法があればいいのですが、a)ライブラリを追加したり、b)ラムダ呼び出しを調整したり、c)別の言語に切り替えたりしたくありませんでした。
一日の終わりに動作します。
exports.handler = (event, context, callback) => {
var response;
var callBackCount;
/*
Ensures the javascript event loop is never empty.
This is the key to keeping lambda from exiting early
*/
setInterval(function(){}, 1000);
/*
Tell lambda to stop when I issue the callback.
This is super important or the lambda funciton will always go until it hits the timeout limit you set.
*/
context.callbackWaitsForEmptyEventLoop = false;
//My way of determining when I'm done with all calls
callBackCount = 0;
//My info to return
response = "";
//Various functions that make rest calls and wait for a response
asyncFunction1();
asyncFunction2();
asyncFunction3();
//Same for asyncFunction 2 and 3
function asyncFunction1(){
response += callBackResponseForThisMethod;
returnResponse();
}
function returnReponse(){
callBackCount++;
if(callBackCount == 3){
//Lambda will stop after this as long as context.callbackWaitsForEmptyEventLoop was set to false
callback(null, JSON.stringify(response));
}
}
};
http://docs.aws.Amazon.com/lambda/latest/dg/nodejs-prog-model-context.html
Async/awaitを使用する
let AWS = require('aws-sdk');
let lambda = new AWS.Lambda();
let data;
exports.handler = async (event) => {
try {
data = await lambda.getAccountSettings().promise();
}
catch (err) {
console.log(err);
return err;
}
return data;
};
async
は含まれていませんが、自分で追加できないわけではありません。パッケージをローカルに追加し(npm install async
)、Lambda関数をアップロードする前にZipにnode_modules
フォルダーを含めるだけです。
Dev依存関係を個別に処理する場合(例:テスト、aws-sdk
関数をローカルで実行するなど)、package.json
のdevDependencies
の下に追加できます。また、コードの開発、テスト、デプロイ、およびプロモーションのプロセスを自動化する場合、これらの2つのリポジトリは非常に便利です。
Lambdaは、一定の時間内に実行できるプログラムと考えてください。 (仮想)プロセッサがこれらの呼び出しをインターリーブする可能性があるため、非同期呼び出しを行うという事実は素晴らしいです。ただし、Lambdaプログラムの一部が、割り当てられた時間よりも完了するのに時間がかかる場合、実行は失敗します。それが妥協であり、Amazonがお金を稼ぐ方法です。より多くの時間や記憶を売ることによって。
これを修正するには、Lambda関数に割り当てられるメモリを増やすことができます。これにより、RAMだけでなく、仮想プロセッサの速度も向上します。もう1つできることは、タイムアウトを増やすことです。 AWS Lambdaで、最大512 MBのRAMと最大5分の処理時間が可能になりました。この投稿の時点で、これらの数値は変更されている可能性があるため、最新の制限について here を確認してください。この設定を変更するには、機能に移動してから設定を行い、最後に詳細設定に進みます。
あなたのラムダ関数はcontext.done()で終わるべきだと思います。たとえば、次のように追加してみてください。
s3.getObject(params, function(err, data) {
if (err) {
...
context.done("Error: " + err.stack);
} else {
processFile(data.Body.toString(), 0);
console.log("ok");
context.done(null, "success");
}
});
require('async');
packまたはrequire('sleep');
packも使用する場合は、次のようにZip
ファイルとして関数をアップロードする必要があります。
Zip
この質問でも説明したように、フォルダーのすべてのコンテンツ:
Alexa JavascriptのAWS Lambda関数のMQTT
同期処理については、通常require('async');
を使用できますが、次のようにasync.series
関数を使用するだけです。
async.series([
function(callback) {
// to do the function 1
callback();
},
function(callback) {
// to do the function 2
callback();
},
function(callback) {
// to do the function 3
callback();
}
], function(err) {
// to do the function if any error happens...
if (err) {
//...
}
//....
});
このようにして、lambda
関数は同期して動作します。
お役に立てば幸いです。
代わりにsynchronous
呼び出しを行いたい場合があります。あなたは同じラムダ関数でファイルを処理しているようだからです。
何らかの理由でコールバックを取得したい場合;ラムダを直接呼び出すか、ラムダイベントを生成する何かを介して、それを行うことができます。ラムダ関数はステートレスであることに注意してください。そのため、必要なすべての情報を渡す必要があります。