【问题标题】:Nodejs - Invoke an AWS.Lambda function from within another lambda functionNodejs - 从另一个 lambda 函数中调用 AWS.Lambda 函数
【发布时间】:2016-06-15 18:01:54
【问题描述】:

我有以下函数用于从我的代码中调用 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.invoke 写成event emitter 而不是通过回调传递它?
  • 是的。这是针对网络服务器的,我被要求在将响应发送到客户端之前不要等待 Lambda 的响应。
  • 啊,好的。您可以简单地触发lambda.invoke忽略响应。

标签: node.js amazon-web-services aws-lambda aws-sdk


【解决方案1】:

使用每个 Lambda 中都可用的 aws-sdk 从另一个 Lambda 函数中调用 Lambda 函数非常简单。

我建议先从简单的东西开始。
这是内部 lambda 调用的“Hello World”:

Lambda_A 调用 Lambda_B Payload 包含单个参数 name:'Alex'
Lambda_B 以 Payload 响应:"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_BLambda_A 相同的角色。
例如:创建一个名为lambdaexecute 的角色,该角色具有AWSLambdaRoleAWSLambdaExecute 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 内部 lambdda 调用工作后,您可以轻松扩展它以调用更精细的 Lambda 函数。

您必须记住的主要事情是为所有功能设置适当的ARN Role

【讨论】:

  • @ArpitVaishnav 你的两个功能是否与AWSLambdaExecute Policy 具有相同的作用。
  • 我所做的就是为角色添加所有可能的 lambda 策略。尝试搜索文本“lambda”并添加所有那些自动建议的策略;)现在它有效......稍后将深入挖掘以找出我可以取消哪些策略......
  • @usama 这个例子“异步”...如果您达到时间限制,只需增加您的 Lambda 函数的执行时间。
  • 这对我不起作用,但是为角色添加 InvokeFunction 权限就可以了
  • 据我所知 lambda_b 不需要 AWSLambdaRole、AWSLambdaExecute 策略,只需要 AWSLambdaBasicExecutionRole
【解决方案2】:

从 2016 年 12 月 3 日起,您可以简单地使用 AWS Step 函数将 Lambda 函数 Lambda_B 作为 Lambda_A 的顺序步骤。

使用 AWS Step Functions,您可以将应用程序定义为状态 机器,一系列步骤共同捕捉机器的行为 应用程序。状态机中的状态可能是任务、顺序步骤、 并行步骤、分支路径(选择)和/或计时器(等待)。任务 是工作单元,这项工作可能由 AWS Lambda 执行 函数、任何类型的 Amazon EC2 实例、容器或 本地服务器——任何可以与 Step Functions 通信的东西 API 可能会被分配一个任务。

所以下面的状态机应该可以满足你的需要。

这里是状​​态机对应的代码。

{
  "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 函数。

【讨论】:

  • 哇,谢谢分享,我不知道有这个,以后可能会用到!看起来真的很有用!
  • 这是否意味着我们现在必须执行这个步骤功能?或者这是自动配置到 Lambda_A 中(这样 Lambda_B 会在 A 被触发时自动执行)?
  • @MichaelDu 您需要执行步骤函数而不是 lambda。触发它的一种简单方法是使用 API Gateway,就像触发 lambda 函数一样,请查看此 SO 问题stackoverflow.com/questions/41188584/… 中的详细信息
  • 那么这比简单地从另一个 lambda 函数执行一个 lambda 函数更简单吗? lambda B 的执行可能取决于 Lambda A 中发生的事情并需要来自那里的数据
  • @Zigglzworth 步进函数的主要好处是可以控制更复杂项目的整个工作流程。它为您免费提供了 lambda 和等待函数的条件执行,并且不受 lambda 函数的最大时间限制(300s)的限制。话虽如此,如果您只有两个想要链接的 lambda 函数,那么是的,不使用 step-function 可能更容易:)
【解决方案3】:

@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 是绝对必要的。

【讨论】:

    【解决方案4】:

    使用aws-sdk 中的 AWS.Lambda 承诺接口调用 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);
    ...
    

    一些带有附加信息的相关链接:

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-12-05
      • 2020-03-04
      • 1970-01-01
      • 1970-01-01
      • 2019-07-01
      • 2018-05-19
      • 1970-01-01
      • 2018-10-26
      相关资源
      最近更新 更多