AWS Lambda 似乎在完成之前退出
AWS Lambda seems exiting before completion
我有一个非常简单的 lambda 函数 (nodeJS),它将接收到的事件放入运动流中。这是源代码:
'use strict';
const AWS = require('aws-sdk');
const kinesis = new AWS.Kinesis({apiVersion: '2013-12-02'});
exports.handler = async (event, context, callback) => {
let body = JSON.parse(event.body);
let receptionDate = new Date().toISOString();
let partitionKey = "pKey-" + Math.floor(Math.random() * 10);
// Response format needed for API Gateway
const formatResponse = (status, responseBody) => {
return {
statusCode: status,
headers: { "Content-Type": "application/json" },
body: JSON.stringify(responseBody)
}
}
// body.events is an array of events. Just add the reception date in each events.
for(let e of body.events) {
e.reception_date = receptionDate;
}
console.log("put In kinesis stream");
let kinesisParams = {
Data: new Buffer(JSON.stringify(body) + "\n"),
PartitionKey: partitionKey,
StreamName: 'event_test'
};
kinesis.putRecord(kinesisParams, (err, res) => {
console.log("Kinesis.putRecord DONE");
if(err) {
console.log("putRecord Error:", JSON.stringify(err));
callback(null, formatResponse(500, "Internal Error: " + JSON.stringify(err)));
} else {
console.log("putRecord Success:", JSON.stringify(res));
callback(null, formatResponse(200));
}
});
};
执行此代码时,cloudwatch 中的日志如下:
START RequestId: 5d4d7526-1a40-401f-8417-06435f0e5408 Version: $LATEST
2019-01-11T09:39:11.925Z 5d4d7526-1a40-401f-8417-06435f0e5408 put In kinesis stream
END RequestId: 5d4d7526-1a40-401f-8417-06435f0e5408
REPORT RequestId: 5d4d7526-1a40-401f-8417-06435f0e5408 Duration: 519.65 ms Billed Duration: 600 ms Memory Size: 128 MB Max Memory Used: 28 MB
似乎 kinesis.putRecord 没有被调用...我在运动流日志中没有看到任何内容。我肯定在某个地方错了,但我不知道在哪里!
刚刚找到解决方案:删除 "async" 关键字使其生效!
exports.handler = (event, context, callback) => { ... }
kinesis.putRecord
是一个异步操作,当它完成时(无论是成功还是错误)调用回调(第二个参数)。
async
函数是 return 承诺的函数。 Lambda 将在解决此承诺时完成其执行,即使还有其他尚未完成的异步操作也是如此。
由于您的函数 return 什么都没有,因此当函数结束时承诺会立即得到解决,因此执行将立即完成 - 无需等待您的异步 kinesis.putRecord
任务。
使用 async
处理程序时,您不需要调用回调。相反,您 return 随心所欲,否则会抛出错误。 Lambda会得到它并分别响应。
所以你有两个选择:
- 由于您的代码中没有任何
await
,只需删除 async
。在这种情况下,Lambda 正在等待事件循环为空 (Unless you explicitly change context.callbackWaitsForEmptyEventLoop)
- 将
kinesis.putRecord
更改为:
let result;
try {
result = await kinesis.putRecord(kinesisParams).promise();
} catch (err) {
console.log("putRecord Error:", JSON.stringify(err));
throw Error(formatResponse(500, "Internal Error: " + JSON.stringify(err));
}
console.log("putRecord Success:", JSON.stringify(result));
return formatResponse(200);
在第二个选项中,lambda 将保持 运行 直到 kinesis.putRecord
完成。
有关这种情况下 Lambda 行为的更多信息,您可以在 lambda 容器中的 /var/runtime/node_modules/awslambda/index.js
下查看执行您的处理程序的主要代码。
@ttulka could you explain a bit more? Give advices or code samples ? –
Adagyo
这是关于 JavaScript 中异步处理的演变。
首先,一切都是通过回调完成的,这是最古老的方法。到处使用回调会导致 "Callback Hell" (http://callbackhell.com)。
然后引入了Promises。使用 Promises 看起来有点像使用 Monads,所有内容都打包到一个 "box"(Promise)中,因此您必须链接所有调用:
thisCallReturnsPromise(...)
.then(data => ...)
.then(data => ...)
.then(data => ...)
.catch(err => ...)
这对人类来说有点不自然,所以 ECMAScript 2017 在异步函数中提出了语法糖(async/await)https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
Async/await 语法允许您像使用普通同步代码一样使用异步承诺:
const data = await thisCallReturnsPromise(...)
别忘了,await
调用必须在异步函数内:
async () => {
const data = await thisCallReturnsPromise(...)
return await processDataAsynchronouslyInPromise(data)
}
AWS Lambda 支持 Node.js v8.10,它完全实现了这种语法。
我有一个非常简单的 lambda 函数 (nodeJS),它将接收到的事件放入运动流中。这是源代码:
'use strict';
const AWS = require('aws-sdk');
const kinesis = new AWS.Kinesis({apiVersion: '2013-12-02'});
exports.handler = async (event, context, callback) => {
let body = JSON.parse(event.body);
let receptionDate = new Date().toISOString();
let partitionKey = "pKey-" + Math.floor(Math.random() * 10);
// Response format needed for API Gateway
const formatResponse = (status, responseBody) => {
return {
statusCode: status,
headers: { "Content-Type": "application/json" },
body: JSON.stringify(responseBody)
}
}
// body.events is an array of events. Just add the reception date in each events.
for(let e of body.events) {
e.reception_date = receptionDate;
}
console.log("put In kinesis stream");
let kinesisParams = {
Data: new Buffer(JSON.stringify(body) + "\n"),
PartitionKey: partitionKey,
StreamName: 'event_test'
};
kinesis.putRecord(kinesisParams, (err, res) => {
console.log("Kinesis.putRecord DONE");
if(err) {
console.log("putRecord Error:", JSON.stringify(err));
callback(null, formatResponse(500, "Internal Error: " + JSON.stringify(err)));
} else {
console.log("putRecord Success:", JSON.stringify(res));
callback(null, formatResponse(200));
}
});
};
执行此代码时,cloudwatch 中的日志如下:
START RequestId: 5d4d7526-1a40-401f-8417-06435f0e5408 Version: $LATEST
2019-01-11T09:39:11.925Z 5d4d7526-1a40-401f-8417-06435f0e5408 put In kinesis stream
END RequestId: 5d4d7526-1a40-401f-8417-06435f0e5408
REPORT RequestId: 5d4d7526-1a40-401f-8417-06435f0e5408 Duration: 519.65 ms Billed Duration: 600 ms Memory Size: 128 MB Max Memory Used: 28 MB
似乎 kinesis.putRecord 没有被调用...我在运动流日志中没有看到任何内容。我肯定在某个地方错了,但我不知道在哪里!
刚刚找到解决方案:删除 "async" 关键字使其生效!
exports.handler = (event, context, callback) => { ... }
kinesis.putRecord
是一个异步操作,当它完成时(无论是成功还是错误)调用回调(第二个参数)。
async
函数是 return 承诺的函数。 Lambda 将在解决此承诺时完成其执行,即使还有其他尚未完成的异步操作也是如此。
由于您的函数 return 什么都没有,因此当函数结束时承诺会立即得到解决,因此执行将立即完成 - 无需等待您的异步 kinesis.putRecord
任务。
使用 async
处理程序时,您不需要调用回调。相反,您 return 随心所欲,否则会抛出错误。 Lambda会得到它并分别响应。
所以你有两个选择:
- 由于您的代码中没有任何
await
,只需删除async
。在这种情况下,Lambda 正在等待事件循环为空 (Unless you explicitly change context.callbackWaitsForEmptyEventLoop) - 将
kinesis.putRecord
更改为:
let result;
try {
result = await kinesis.putRecord(kinesisParams).promise();
} catch (err) {
console.log("putRecord Error:", JSON.stringify(err));
throw Error(formatResponse(500, "Internal Error: " + JSON.stringify(err));
}
console.log("putRecord Success:", JSON.stringify(result));
return formatResponse(200);
在第二个选项中,lambda 将保持 运行 直到 kinesis.putRecord
完成。
有关这种情况下 Lambda 行为的更多信息,您可以在 lambda 容器中的 /var/runtime/node_modules/awslambda/index.js
下查看执行您的处理程序的主要代码。
@ttulka could you explain a bit more? Give advices or code samples ? – Adagyo
这是关于 JavaScript 中异步处理的演变。
首先,一切都是通过回调完成的,这是最古老的方法。到处使用回调会导致 "Callback Hell" (http://callbackhell.com)。
然后引入了Promises。使用 Promises 看起来有点像使用 Monads,所有内容都打包到一个 "box"(Promise)中,因此您必须链接所有调用:
thisCallReturnsPromise(...)
.then(data => ...)
.then(data => ...)
.then(data => ...)
.catch(err => ...)
这对人类来说有点不自然,所以 ECMAScript 2017 在异步函数中提出了语法糖(async/await)https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
Async/await 语法允许您像使用普通同步代码一样使用异步承诺:
const data = await thisCallReturnsPromise(...)
别忘了,await
调用必须在异步函数内:
async () => {
const data = await thisCallReturnsPromise(...)
return await processDataAsynchronouslyInPromise(data)
}
AWS Lambda 支持 Node.js v8.10,它完全实现了这种语法。