Nodejs - 从另一个 lambda 函数中调用一个 AWS.Lambda 函数
Nodejs - Invoke an AWS.Lambda function from within another lambda function
我有以下函数,可用于从我的代码中调用 Lambda 函数。
但是,当我尝试在 Lambda 函数中使用它时,出现以下错误:
AWS lambda undefined 0.27s 3 retries] invoke({ FunctionName: 'my-function-name',
InvocationType: 'RequestResponse',
LogType: 'Tail',
Payload: <Buffer > })
如何从 Lambda 函数中调用 Lambda 函数?
我的函数:
'use strict';
var AWS = require("aws-sdk");
var lambda = new AWS.Lambda({
apiVersion: '2015-03-31',
endpoint: 'https://lambda.' + process.env.DYNAMODB_REGION + '.amazonaws.com',
logger: console
});
var lambdaHandler = {};
// @var payload - type:string
// @var functionName - type:string
lambdaHandler.invokeFunction = function (payload, functionName, callback) {
var params = {
FunctionName: functionName, /* required */
InvocationType: "RequestResponse",
LogType: "Tail",
Payload: new Buffer(payload, 'utf8')
};
var lambdaRequestObj = lambda.invoke(params);
lambdaRequestObj.on('success', function(response) {
console.log(response.data);
});
lambdaRequestObj.on('error', function(response) {
console.log(response.error.message);
});
lambdaRequestObj.on('complete', function(response) {
console.log('Complete');
});
lambdaRequestObj.send();
callback();
};
module.exports = lambdaHandler;
使用每个 Lambda 中可用的 aws-sdk
从另一个 Lambda 函数中调用一个 Lambda 函数非常简单。
I suggest starting with something simple first.
This is the "Hello World" of intra-lambda invocation:
Lambda_A
调用 Lambda_B
Payload
包含单个参数 name:'Alex'
.
Lambda_B
响应负载:"Hello Alex"
.
首先创建 Lambda_B
需要 name
属性
在 event
参数上
并用 "Hello "+event.name
:
响应请求
Lambda_B
exports.handler = function(event, context) {
console.log('Lambda B Received event:', JSON.stringify(event, null, 2));
context.succeed('Hello ' + event.name);
};
确保您赋予 Lambda_B
和 Lambda_A
相同的角色。
例如:创建一个名为 lambdaexecute
的角色,它具有 AWSLambdaRole
、AWSLambdaExecute
和
AWSLambdaBasicExecutionRole
(全部必填):
Lambda_A
var AWS = require('aws-sdk');
AWS.config.region = 'eu-west-1';
var lambda = new AWS.Lambda();
exports.handler = function(event, context) {
var params = {
FunctionName: 'Lambda_B', // the lambda function we are going to invoke
InvocationType: 'RequestResponse',
LogType: 'Tail',
Payload: '{ "name" : "Alex" }'
};
lambda.invoke(params, function(err, data) {
if (err) {
context.fail(err);
} else {
context.succeed('Lambda_B said '+ data.Payload);
}
})
};
保存这两个 Lambda 函数后,测试 运行 Lambda_A
:
一旦 basic intra-lambdda 调用正常工作,您就可以轻松扩展它以调用更复杂的 Lambda 函数。
The main thing you have to remember is to set the appropriate ARN Role
for all functions.
自 2016 年 12 月 3 日起,您可以简单地使用 AWS Step 函数将 Lambda 函数 Lambda_B 作为 [ 的顺序步骤=31=]。
With AWS Step Functions, you define your application as a state
machine, a series of steps that together capture the behavior of the
app. States in the state machine may be tasks, sequential steps,
parallel steps, branching paths (choice), and/or timers (wait). Tasks
are units of work, and this work may be performed by AWS Lambda
functions, Amazon EC2 instances of any type, containers, or on
premises servers—anything that can communicate with the Step Functions
API may be assigned a task.
所以下面的状态机应该可以满足你的需要。
这里是状态机对应的代码。
{
"Comment": "A simple example of the Amazon States Language using an AWS Lambda Function",
"StartAt": "Lambda_A",
"States": {
"Lambda_A": {
"Type": "Task",
"Resource": "arn:aws:lambda:REGION:ACCOUNT_ID:function:FUNCTION_NAME",
"Next": "Lambda_B"
},
"Lambda_B":{
"Type": "Task",
"Resource": "arn:aws:lambda:REGION:ACCOUNT_ID:function:FUNCTION_NAME",
"End": true
}
}
}
此外,您可以在状态机中添加更复杂的逻辑,例如并行步骤和捕获失败。它甚至记录了每一次执行的细节,这使得调试体验更好,尤其是对于 lambda 函数。
@nelsonic 提到的所有内容都是正确的,除了角色。
我试过选择他上面提到的角色:
- AWSLambdaExecute
- AWSLambdaBasicExecutionRole
但它不允许我调用我的其他 lambda 函数,所以我将角色更改为以下:
- AWSLambdaRole
- AWSLambdaBasicExecutionRole
背后的原因是 AWSLambdaExecute 仅提供对 S3 的 Put、Get 访问权限和对 CloudWatch Logs 的完全访问权限。
但 AWSLambdaRole 提供 AWS Lambda 服务角色的默认策略。
如果你遵守它的许可政策,它会谈论 invokeFunction
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"lambda:InvokeFunction"
],
"Resource": [
"*"
]
}
]
}
注意:可以在没有 AWSLambdaBasicExecutionRole 策略的情况下继续,因为它只启用云中的日志记录手表。但是 AWSLambdaRole 是绝对必要的。
使用 aws-sdk
中的 AWS.Lambda promises 接口调用 lambda 比使用回调更容易。
此示例函数允许您从另一个 lambda 同步调用一个 lambda(它使用 'RequestResponse'
作为 InvocationType
,因此您可以获得调用的 lambda 返回的值)。
如果使用'Event'
(异步调用),则无法获取被调用lambda的返回值,只能检测lambda是否调用成功。它适用于不需要从调用的 lambda 获取返回值的情况。
//
// Full example of a lambda that calls another lambda
//
// (create a lambda in AWS with this code)
//
'use strict';
//
// Put here the name of the function you want to call
//
const g_LambdaFunctionName = 'PUT_HERE_THE_INVOKED_LAMBDA_NAME'; // <======= PUT THE DESIRED VALUE
const AWS = require('aws-sdk');
const lambda = new AWS.Lambda;
//
// Expected use:
//
// // (payload can be an object or a JSON string, for example)
// let var = await invokeLambda(lambdaFunctionName, payload);
//
const invokeLambda = async (lambdaFunctionName, payload) => {
console.log('>>> Entering invokeLambda');
// If the payload isn't a JSON string, we convert it to JSON
let payloadStr;
if (typeof payload === 'string')
{
console.log('invokeLambda: payload parameter is already a string: ', payload);
payloadStr = payload;
}
else
{
payloadStr = JSON.stringify(payload, null, 2);
console.log('invokeLambda: converting payload parameter to a string: ', payloadStr);
}
let params = {
FunctionName : lambdaFunctionName, /* string type, required */
// ClientContext : '', /* 'STRING_VALUE' */
InvocationType : 'RequestResponse', /* string type: 'Event' (async)| 'RequestResponse' (sync) | 'DryRun' (validate parameters y permissions) */
// InvocationType : 'Event',
LogType : 'None', /* string type: 'None' | 'Tail' */
// LogType : 'Tail',
Payload : payloadStr, /* Buffer.from('...') || 'JSON_STRING' */ /* Strings will be Base-64 encoded on your behalf */
// Qualifier : '', /* STRING_VALUE' */
};
//
// TODO/FIXME: add try/catch to protect this code from failures (non-existent lambda, execution errors in lambda)
//
const lambdaResult = await lambda.invoke(params).promise();
console.log('Results from invoking lambda ' + lambdaFunctionName + ': ' , JSON.stringify(lambdaResult, null, 2) );
// If you use LogType = 'Tail', you'll obtain the logs in lambdaResult.LogResult.
// If you use 'None', there will not exist that field in the response.
if (lambdaResult.LogResult)
{
console.log('Logs of lambda execution: ', Buffer.from(lambdaResult.LogResult, 'base64').toString());
}
console.log('invokeLambdaSync::lambdaResult: ', lambdaResult);
console.log('<<< Returning from invokeLambda, with lambdaResult: ', JSON.stringify(lambdaResult, null, 2));
// The actual value returned by the lambda it is lambdaResult.Payload
// There are other fields (some of them are optional)
return lambdaResult;
};
//
// We'll assign this as the calling lambda handler.
//
const callingFunc = async (event) => {
//
// in this example We obtain the lambda name from a global variable
//
const lambdaFunctionName = g_LambdaFunctionName;
// const payload = '{"param1" : "value1"}';
const payload = event;
//
// invokeLambda has to be called from a async function
// (to be able to use await)
//
const result = await invokeLambda(lambdaFunctionName, payload);
console.log('result: ', result);
};
// Assing handler function
exports.handler = callingFunc;
请注意,您应该在 invokeLambda 之前使用 await
:
...
//
// Called from another async function
//
const result = await invokeLambda(lambdaFunctionName, payload);
...
一些包含附加信息的相关链接:
- 关于
invoke
调用的 AWS 参考:https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Lambda.html#invoke-property
- 关于调用 lambda 的 AWS 文档:https://docs.aws.amazon.com/lambda/latest/dg/API_Invoke.html
- AWS 中的 Promises 接口:
https://aws.amazon.com/es/blogs/compute/node-js-8-10-runtime-now-available-in-aws-lambda/
- 示例(西班牙语解释):https://www.it-swarm.dev/es/node.js/invocar-aws-lambda-desde-otra-lambda-de-forma-asincronica/826852446/
- 处理错误,避免 lambda 之间的耦合:https://www.rehanvdm.com/serverless/13-aws-lambda-design-considerations-you-need-to-know-about-part-2/index.html
调用 Lambda AWS SDK Typescript
我自己编写了 class 来执行此操作,解析响应并检查错误。我把它贴在这里是为了拯救任何想要努力的人:)
这需要 aws-sdk
和 ts-log
.
import { AWSError, Lambda } from 'aws-sdk'
import { Logger } from 'tslog';
export class LambdaClient {
awsLambda: Lambda;
logger: Logger;
constructor(region: string) {
this.awsLambda = new Lambda({ region })
this.logger = new Logger({ name: "LambdaClient" })
}
trigger({ functionName, payload }): Promise<any> {
return new Promise(
(resolve, reject) => {
const params = {
FunctionName: functionName,
InvocationType: 'RequestResponse',
LogType: 'Tail',
Payload: JSON.stringify(payload)
};
this.awsLambda.invoke(params, (err: AWSError, data: Lambda.InvocationResponse) => {
if (err) {
this.logger.error({ message: "error while triggering lambda", errorMessage: err.message })
return reject(err)
}
if (data.StatusCode !== 200 && data.StatusCode !== 201) {
this.logger.error({ message: "expected status code 200 or 201", statusCode: data.StatusCode, logs: base64ToString(data.LogResult) })
return reject(data)
}
const responsePayload = data.Payload
return resolve(JSON.parse(responsePayload.toString()))
})
}
)
}
}
function base64ToString(logs: string) {
try {
return Buffer.from(logs, 'base64').toString('ascii');
} catch {
return "Could not convert."
}
}
我有以下函数,可用于从我的代码中调用 Lambda 函数。
但是,当我尝试在 Lambda 函数中使用它时,出现以下错误:
AWS lambda undefined 0.27s 3 retries] invoke({ FunctionName: 'my-function-name',
InvocationType: 'RequestResponse',
LogType: 'Tail',
Payload: <Buffer > })
如何从 Lambda 函数中调用 Lambda 函数?
我的函数:
'use strict';
var AWS = require("aws-sdk");
var lambda = new AWS.Lambda({
apiVersion: '2015-03-31',
endpoint: 'https://lambda.' + process.env.DYNAMODB_REGION + '.amazonaws.com',
logger: console
});
var lambdaHandler = {};
// @var payload - type:string
// @var functionName - type:string
lambdaHandler.invokeFunction = function (payload, functionName, callback) {
var params = {
FunctionName: functionName, /* required */
InvocationType: "RequestResponse",
LogType: "Tail",
Payload: new Buffer(payload, 'utf8')
};
var lambdaRequestObj = lambda.invoke(params);
lambdaRequestObj.on('success', function(response) {
console.log(response.data);
});
lambdaRequestObj.on('error', function(response) {
console.log(response.error.message);
});
lambdaRequestObj.on('complete', function(response) {
console.log('Complete');
});
lambdaRequestObj.send();
callback();
};
module.exports = lambdaHandler;
使用每个 Lambda 中可用的 aws-sdk
从另一个 Lambda 函数中调用一个 Lambda 函数非常简单。
I suggest starting with something simple first.
This is the "Hello World" of intra-lambda invocation:
Lambda_A
调用 Lambda_B
Payload
包含单个参数 name:'Alex'
.
Lambda_B
响应负载:"Hello Alex"
.
首先创建 Lambda_B
需要 name
属性
在 event
参数上
并用 "Hello "+event.name
:
Lambda_B
exports.handler = function(event, context) {
console.log('Lambda B Received event:', JSON.stringify(event, null, 2));
context.succeed('Hello ' + event.name);
};
确保您赋予 Lambda_B
和 Lambda_A
相同的角色。
例如:创建一个名为 lambdaexecute
的角色,它具有 AWSLambdaRole
、AWSLambdaExecute
和
AWSLambdaBasicExecutionRole
(全部必填):
Lambda_A
var AWS = require('aws-sdk');
AWS.config.region = 'eu-west-1';
var lambda = new AWS.Lambda();
exports.handler = function(event, context) {
var params = {
FunctionName: 'Lambda_B', // the lambda function we are going to invoke
InvocationType: 'RequestResponse',
LogType: 'Tail',
Payload: '{ "name" : "Alex" }'
};
lambda.invoke(params, function(err, data) {
if (err) {
context.fail(err);
} else {
context.succeed('Lambda_B said '+ data.Payload);
}
})
};
保存这两个 Lambda 函数后,测试 运行 Lambda_A
:
一旦 basic intra-lambdda 调用正常工作,您就可以轻松扩展它以调用更复杂的 Lambda 函数。
The main thing you have to remember is to set the appropriate
ARN Role
for all functions.
自 2016 年 12 月 3 日起,您可以简单地使用 AWS Step 函数将 Lambda 函数 Lambda_B 作为 [ 的顺序步骤=31=]。
With AWS Step Functions, you define your application as a state machine, a series of steps that together capture the behavior of the app. States in the state machine may be tasks, sequential steps, parallel steps, branching paths (choice), and/or timers (wait). Tasks are units of work, and this work may be performed by AWS Lambda functions, Amazon EC2 instances of any type, containers, or on premises servers—anything that can communicate with the Step Functions API may be assigned a task.
所以下面的状态机应该可以满足你的需要。
这里是状态机对应的代码。
{
"Comment": "A simple example of the Amazon States Language using an AWS Lambda Function",
"StartAt": "Lambda_A",
"States": {
"Lambda_A": {
"Type": "Task",
"Resource": "arn:aws:lambda:REGION:ACCOUNT_ID:function:FUNCTION_NAME",
"Next": "Lambda_B"
},
"Lambda_B":{
"Type": "Task",
"Resource": "arn:aws:lambda:REGION:ACCOUNT_ID:function:FUNCTION_NAME",
"End": true
}
}
}
此外,您可以在状态机中添加更复杂的逻辑,例如并行步骤和捕获失败。它甚至记录了每一次执行的细节,这使得调试体验更好,尤其是对于 lambda 函数。
@nelsonic 提到的所有内容都是正确的,除了角色。
我试过选择他上面提到的角色:
- AWSLambdaExecute
- AWSLambdaBasicExecutionRole
但它不允许我调用我的其他 lambda 函数,所以我将角色更改为以下:
- AWSLambdaRole
- AWSLambdaBasicExecutionRole
背后的原因是 AWSLambdaExecute 仅提供对 S3 的 Put、Get 访问权限和对 CloudWatch Logs 的完全访问权限。 但 AWSLambdaRole 提供 AWS Lambda 服务角色的默认策略。 如果你遵守它的许可政策,它会谈论 invokeFunction
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"lambda:InvokeFunction"
],
"Resource": [
"*"
]
}
]
}
注意:可以在没有 AWSLambdaBasicExecutionRole 策略的情况下继续,因为它只启用云中的日志记录手表。但是 AWSLambdaRole 是绝对必要的。
使用 aws-sdk
中的 AWS.Lambda promises 接口调用 lambda 比使用回调更容易。
此示例函数允许您从另一个 lambda 同步调用一个 lambda(它使用 'RequestResponse'
作为 InvocationType
,因此您可以获得调用的 lambda 返回的值)。
如果使用'Event'
(异步调用),则无法获取被调用lambda的返回值,只能检测lambda是否调用成功。它适用于不需要从调用的 lambda 获取返回值的情况。
//
// Full example of a lambda that calls another lambda
//
// (create a lambda in AWS with this code)
//
'use strict';
//
// Put here the name of the function you want to call
//
const g_LambdaFunctionName = 'PUT_HERE_THE_INVOKED_LAMBDA_NAME'; // <======= PUT THE DESIRED VALUE
const AWS = require('aws-sdk');
const lambda = new AWS.Lambda;
//
// Expected use:
//
// // (payload can be an object or a JSON string, for example)
// let var = await invokeLambda(lambdaFunctionName, payload);
//
const invokeLambda = async (lambdaFunctionName, payload) => {
console.log('>>> Entering invokeLambda');
// If the payload isn't a JSON string, we convert it to JSON
let payloadStr;
if (typeof payload === 'string')
{
console.log('invokeLambda: payload parameter is already a string: ', payload);
payloadStr = payload;
}
else
{
payloadStr = JSON.stringify(payload, null, 2);
console.log('invokeLambda: converting payload parameter to a string: ', payloadStr);
}
let params = {
FunctionName : lambdaFunctionName, /* string type, required */
// ClientContext : '', /* 'STRING_VALUE' */
InvocationType : 'RequestResponse', /* string type: 'Event' (async)| 'RequestResponse' (sync) | 'DryRun' (validate parameters y permissions) */
// InvocationType : 'Event',
LogType : 'None', /* string type: 'None' | 'Tail' */
// LogType : 'Tail',
Payload : payloadStr, /* Buffer.from('...') || 'JSON_STRING' */ /* Strings will be Base-64 encoded on your behalf */
// Qualifier : '', /* STRING_VALUE' */
};
//
// TODO/FIXME: add try/catch to protect this code from failures (non-existent lambda, execution errors in lambda)
//
const lambdaResult = await lambda.invoke(params).promise();
console.log('Results from invoking lambda ' + lambdaFunctionName + ': ' , JSON.stringify(lambdaResult, null, 2) );
// If you use LogType = 'Tail', you'll obtain the logs in lambdaResult.LogResult.
// If you use 'None', there will not exist that field in the response.
if (lambdaResult.LogResult)
{
console.log('Logs of lambda execution: ', Buffer.from(lambdaResult.LogResult, 'base64').toString());
}
console.log('invokeLambdaSync::lambdaResult: ', lambdaResult);
console.log('<<< Returning from invokeLambda, with lambdaResult: ', JSON.stringify(lambdaResult, null, 2));
// The actual value returned by the lambda it is lambdaResult.Payload
// There are other fields (some of them are optional)
return lambdaResult;
};
//
// We'll assign this as the calling lambda handler.
//
const callingFunc = async (event) => {
//
// in this example We obtain the lambda name from a global variable
//
const lambdaFunctionName = g_LambdaFunctionName;
// const payload = '{"param1" : "value1"}';
const payload = event;
//
// invokeLambda has to be called from a async function
// (to be able to use await)
//
const result = await invokeLambda(lambdaFunctionName, payload);
console.log('result: ', result);
};
// Assing handler function
exports.handler = callingFunc;
请注意,您应该在 invokeLambda 之前使用 await
:
...
//
// Called from another async function
//
const result = await invokeLambda(lambdaFunctionName, payload);
...
一些包含附加信息的相关链接:
- 关于
invoke
调用的 AWS 参考:https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Lambda.html#invoke-property - 关于调用 lambda 的 AWS 文档:https://docs.aws.amazon.com/lambda/latest/dg/API_Invoke.html
- AWS 中的 Promises 接口: https://aws.amazon.com/es/blogs/compute/node-js-8-10-runtime-now-available-in-aws-lambda/
- 示例(西班牙语解释):https://www.it-swarm.dev/es/node.js/invocar-aws-lambda-desde-otra-lambda-de-forma-asincronica/826852446/
- 处理错误,避免 lambda 之间的耦合:https://www.rehanvdm.com/serverless/13-aws-lambda-design-considerations-you-need-to-know-about-part-2/index.html
调用 Lambda AWS SDK Typescript
我自己编写了 class 来执行此操作,解析响应并检查错误。我把它贴在这里是为了拯救任何想要努力的人:)
这需要 aws-sdk
和 ts-log
.
import { AWSError, Lambda } from 'aws-sdk'
import { Logger } from 'tslog';
export class LambdaClient {
awsLambda: Lambda;
logger: Logger;
constructor(region: string) {
this.awsLambda = new Lambda({ region })
this.logger = new Logger({ name: "LambdaClient" })
}
trigger({ functionName, payload }): Promise<any> {
return new Promise(
(resolve, reject) => {
const params = {
FunctionName: functionName,
InvocationType: 'RequestResponse',
LogType: 'Tail',
Payload: JSON.stringify(payload)
};
this.awsLambda.invoke(params, (err: AWSError, data: Lambda.InvocationResponse) => {
if (err) {
this.logger.error({ message: "error while triggering lambda", errorMessage: err.message })
return reject(err)
}
if (data.StatusCode !== 200 && data.StatusCode !== 201) {
this.logger.error({ message: "expected status code 200 or 201", statusCode: data.StatusCode, logs: base64ToString(data.LogResult) })
return reject(data)
}
const responsePayload = data.Payload
return resolve(JSON.parse(responsePayload.toString()))
})
}
)
}
}
function base64ToString(logs: string) {
try {
return Buffer.from(logs, 'base64').toString('ascii');
} catch {
return "Could not convert."
}
}