AWS Lambda Invoke 不执行 lambda 函数

Posted

技术标签:

【中文标题】AWS Lambda Invoke 不执行 lambda 函数【英文标题】:AWS Lambda Invoke does not execute the lambda function 【发布时间】:2016-10-16 04:37:45 【问题描述】:

我创建了 4 个 Lambda 函数来处理将写入 mysql 表的信息。前三个函数只是分别选择、插入和更新 MYSQL 表记录。

然后我创建了第 4 个函数来接受记录详细信息作为事件参数的一部分。此函数将首先尝试通过调用第一个 lambda 函数来选择记录,如果找到它,将使用 update lambda 函数更新表上的记录。如果没有找到,它将调用插入函数来添加记录。我在操作 MySQL 表的 3 个函数上使用 pool.query。我还使用 lambda.invoke 从第四个函数调用这三个函数。

通过将记录详细信息作为参数传递,我能够在本地成功测试第 4 个函数,并且它能够成功调用三个 Lambda 函数并更新 mySQL 表记录。我遇到的问题是,当我在 AWS Lambda 中上传函数时,它不会调用这三个函数中的任何一个。我在日志中没有看到任何错误,所以我不知道如何检查问题出在哪里。这是调用其他函数的代码:

exports.handler = (event, context, callback) => 

var err = null;
var payload = 
        qryString : event.qryString,
        record: event.updaterecord,
        dbConfigPool : event.dbConfigPool
           

var params = 
  FunctionName: 'getInventory', 
  Payload: JSON.stringify(payload) 

console.log(' before invoke ' + JSON.stringify(params) )
lambda.invoke(params, function(err, data) 
console.log(' aftr invoke ' + JSON.stringify(params) )
  if (err) 
    console.log('err ' + err, err.stack); // an error occurred
    event.message = err + ' query error';
  
  else     
      console.log('success' + JSON.stringify(data));   
      console.log(' status code ' + JSON.stringify(data.StatusCode));
      console.log(' Payload ' + JSON.stringify(JSON.parse(data.Payload)));
      var rowsTemp = JSON.parse(data.Payload);
      var rows = rowsTemp.data;
      if (!rowsTemp.recordExist) 
          console.log('insert')
            // Update inventory record only if quantity is not negative
          var newQuantity = 0
      newQuantity = parseFloat(event.updaterecord.quantity);
      if (Math.sign(newQuantity) === 1) 
        var payload = 
                    record: event.updaterecord,
                    dbConfigPool : event.dbConfigPool
                       

        console.log('insert' + JSON.stringify(payload));
        var params = 
          FunctionName: 'insertInventory', 
          Payload: JSON.stringify(payload) 
        
            lambda.invoke(params, function(err, data) 
              if (err) console.log(err, err.stack); // an error occurred
              else     console.log(data);           // successful response
            );
        

  
  else 
      newQuantity = 0
      newQuantity = parseFloat(event.updaterecord.quantity) + parseFloat(rows[0].quantity);
      if (Math.sign(newQuantity) === 1) 
      event.updaterecord.quantity = newQuantity;
       else 
        // Set to zero if the result is negative
      event.updaterecord.quantity = 0;
      
      console.log('value ' + JSON.stringify(newQuantity) + ' updaterecord' + JSON.stringify(event.updaterecord.quantity) );
      var payload = 
                    qryString : event.qryString,
                    record: event.updaterecord,
                    dbConfigPool : event.dbConfigPool
                       
      console.log('update' + JSON.stringify(payload));
        var params = 
          FunctionName: 'updateInventory', 
          Payload: JSON.stringify(payload) 
        
        console.log(' before invoke ' + JSON.stringify(params) )
        lambda.invoke(params, function(err, data) 
        console.log(' after invoke ' + JSON.stringify(params) )
          if (err) 
            console.log('err ' + err, err.stack); // an error occurred
            event.message = err + ' query error';
           else 
              console.log(data);
           // else
        ); // lambda invoke
  
          // successful response
);

console.log(' end of function');
var completed = true;
context.callbackWaitsForEmptyEventLoop = false; 
callback(null, completed);


如果代码很长,请见谅。但我想表明我确实放了一些 console.logs 来监控它的经过。 cloudwatch 日志仅显示第一个 lambda.invoke 之前的第一条消息,然后显示函数结束的最后一条消息。

我也没有在 cloudwatch 中看到已调用的三个函数的任何日志条目。

06/17 好的,由于我仍然无法完成这项工作,我将代码简化为以下内容:

exports.handler = (event, context, callback) => 

var err = null;
var updatedRecord = false;
var responseDetail = ;
var payload = 
        qryString : event.qryString,
        record: event.updaterecord,
        dbConfigPool : event.dbConfigPool
           

var params = 
  FunctionName: 'getInventory', 
  Payload: JSON.stringify(payload) 

console.log(' before invoke ' + JSON.stringify(params));
lambda.invoke(params, function(err, data) 
  if (err) 
  event.message = err + ' query error';
  callback(err,event.message);
  
  else     

  console.log('success' + JSON.stringify(data));   
  console.log(' status code ' + JSON.stringify(data.StatusCode));
  console.log(' Payload ' + JSON.stringify(JSON.parse(data.Payload)));
  callback(null, data);

          // successful response
);

console.log(' end of function');
//    var completed = true;
//    context.callbackWaitsForEmptyEventLoop = false; 
//    callback(null, completed);


但是,当我进行测试时,该功能超时。我还为附加到函数的角色提供了完整的 Lambda 和 RDS 访问权限。

【问题讨论】:

【参考方案1】:

想想这是做什么的:

lambda.invoke(params, function(err, data)  ...

它开始“做某事”(碰巧调用另一个 lambda 函数的事实实际上并不重要),当“某事”完成时,它调用 function(),对吗?

但它也立即返回,并执行下一条语句。

与此同时,异步事件循环正在处理“某事”。

“下一条语句”是

console.log(' end of function');

然后,您是在告诉 lambda “嘿,我可能有一些异步操作正在进行,但不要担心等待它完成”:

context.callbackWaitsForEmptyEventLoop = false; 

所以好消息是您的代码正在执行编写的任务...但事实证明这是错误的。

在任何地方你都有这两件事之一......

// an error occurred
// successful response

...那些是你应该调用callback()的地方,而不是在处理函数的末尾,你的代码应该很快到达。

如果您正确断开数据库连接并且您包含的所有模块都表现良好,则您不需要使用context.callbackWaitsForEmptyEventLoop = false;。虽然这有其目的,但似乎有很多人用它来掩盖微妙的未竟事业。

或者,对于一个更整洁的解决方案,您最后只提到回调并且您的 function function function 嵌套不会变得如此失控,请使用async-waterfall

【讨论】:

异步过程当然让这更有趣。我的主要困惑是 lambda.invoke 做了什么。我的理解是它实际上会运行我在参数中指定的 lambda 函数。有趣的是,如果我在本地机器上运行上面的代码(并注释掉回调的最后两行),我可以看到正在调用 lambda 函数。我开始怀疑这可能是访问/安全问题,但我希望日志中会有一些相关信息。 否...lambda.invoke() 将向 AWS Lambda 基础设施发送请求,以在完全不同的容器中调用第二个函数,然后在返回响应时调用您的回调函数。此调用会立即返回,当该响应或错误可用时,您的 function(err,data) ... 将被触发。本地测试与实际测试不同。 删除 context.callbackWaitsForEmptyEventLoop = false;callback(null, completed);(暂时),然后将允许的超时时间增加到 60 秒,您应该会在日志中看到更多信息。 您可能有安全/访问/权限问题,但您需要先修复您做错的部分......或者至少暂时禁用它,正如我所建议的那样。 re: lambda.invoke - 我并不是说它会在我的函数中执行,但我希望它会在 AWS 中的某个地方运行。我通常查看 getInventory 函数的 cloudwatch 日志以确定它是否被调用,在这种情况下,我没有看到任何调用。我会尝试你关于修改回调位置的建议,并让你知道结果。感谢您的帮助。 异步操作最初在某种程度上是违反直觉的。当你“开始”某事时,你实质上是在询问你的环境“嘿,尽快这样做,但不是在将控制权交还给我之前”。然后,上面的接下来的两个指令使环境无法实际执行您之前要求执行的操作。我很有信心,当你做出改变时,你会看到不同的行为。【参考方案2】:

首先 - 欢迎回调地狱!我稍后会回到这个。

这是一个调用 lambda 函数的简单代码。

var params = 
  FunctionName: 'LAMBDA_FUNCTION_NAME', /* required */
;
lambda.invoke(params, function(err, data) 
  if (err) 
   console.log(err, err.stack); // an error occurred
  
  else 
   console.log(data);           // successful response
  
);

lambda.invoke 函数有两个参数(paramsfunction(err,data)..)。第一个是一个简单的 JSON 对象。第二个是函数——回调函数。当 lambda.invoke(你可以认为是 LAMBDA_FUNCTION_NAME)的执行结束时,这个函数将被“回调”。如果发生错误,它将被“存储”在 err 变量中,否则返回的数据将存储在 data 变量中(这不是正确的解释,但我试图保留它这里很简单)。

当你想一个接一个地调用两个 lambda 函数时会发生什么?

var params1 = 
  FunctionName: 'LAMBDA_FUNCTION_1_NAME', /* required */
;
lambda.invoke(params1, function(err, data) 
  if (err) 
   console.log(err, err.stack); // an error occurred
  
  else 
   console.log('Lambda function 1 invoked!');
   console.log(data);           // successful response
  
);
var params2 = 
  FunctionName: 'LAMBDA_FUNCTION_2_NAME', /* required */
;
lambda.invoke(params2, function(err, data) 
  if (err) 
   console.log(err, err.stack); // an error occurred
  
  else 
   console.log('Lambda function 2 invoked!');
   console.log(data);           // successful response
  
);
console.log('I am done!');

根据 LAMBDA_FUNCTION_1_NAME 和 LAMBDA_FUNCTION_2_NAME 的执行时间,您可能会看到不同的输出,例如:

Lambda function 1 invoked!
I am done!

Lambda function 1 invoked!
Lambda function 2 invoked!
I am done!

甚至

Lambda function 1 invoked!
I am done!
Lambda function 2 invoked!

这是因为您正在调用 lambda.invoke,然后(无需等待)您再次调用 lambda.invoke。在那之后(当然没有等待)前面的函数结束你正在调用 console.log('I am done!');

您可以通过将每个函数放在前一个函数的回调中来解决这个问题。像这样的:

var params1 = 
  FunctionName: 'LAMBDA_FUNCTION_1_NAME', /* required */
;
lambda.invoke(params1, function(err, data) 
  if (err) 
   console.log(err, err.stack); // an error occurred
  
  else 
   console.log('Lambda function 1 invoked!');
   console.log(data);           // successful response
   var params2 = 
    FunctionName: 'LAMBDA_FUNCTION_2_NAME', /* required */
   ;
   lambda.invoke(params2, function(err, data) 
      if (err) 
       console.log(err, err.stack); // an error occurred
      
      else 
       console.log('Lambda function 2 invoked!');
       console.log(data);           // successful response
       console.log('I am done!');
      
    );
  
);

这样你的输出将是:

Lambda function 1 invoked!
Lambda function 2 invoked!
I am done!

但是如果你想一个接一个地调用 3 个或多个函数,你最终会得到嵌套代码。这是回调地狱。你可以用这种方式重写你的代码。但在我看来,检查waterfall async library 是个好主意

async.waterfall([
    function(callback) 
        callback(null, 'one', 'two');
    ,
    function(arg1, arg2, callback) 
      // arg1 now equals 'one' and arg2 now equals 'two'
        callback(null, 'three');
    ,
    function(arg1, callback) 
        // arg1 now equals 'three'
        callback(null, 'done');
    
], function (err, result) 
    // result now equals 'done'
)

伪代码应如下所示:

async.waterfall([
    function(callback1) 
        var params1 = 
            FunctionName: 'LAMBDA_FUNCTION_1_NAME', /* required */
        ;
        lambda.invoke(params1, function(err, data) 
            if (err) 
                console.log(err, err.stack); // an error occurred
            
            else 
                console.log('LAMBDA_FUNCTION_1_NAME finished!');
                callback1(null,data);
            
        );
    ,
    function(result_from_function_1, callback2) 
        console.log(result_from_function_1); // outputs result from LAMBDA_FUNCTION_1_NAME
        var params2 = 
            FunctionName: 'LAMBDA_FUNCTION_2_NAME', /* required */
        ;
        lambda.invoke(params2, function(err, data) 
            if (err) 
                console.log(err, err.stack); // an error occurred
            
            else 
                console.log('LAMBDA_FUNCTION_2_NAME finished!');
                callback2(null,data); 
            
        );
    ,
    function(result_from_function_2, callback3) 
        console.log(result_from_function_2); // outputs result from LAMBDA_FUNCTION_2_NAME
        var params3 = 
            FunctionName: 'LAMBDA_FUNCTION_3_NAME', /* required */
        ;
        lambda.invoke(params3, function(err, data) 
            if (err) 
                console.log(err, err.stack); // an error occurred
            
            else 
                console.log('LAMBDA_FUNCTION_3_NAME finished!');
                callback3(null,data);
            
        );
    
], function (err, result) 
    // result now equals LAMBDA_FUNCTION_3_NAME result
)

请注意,所有回调(callback1、callback2 和 callback3)只能使用名称“callback”。为了更好地理解,我更改了他们的名字。

【讨论】:

感谢您的推荐。我会更多地研究瀑布异步库。我认为我的代码已经使用了嵌套的 lambda 调用。我正在调用 getInventory,如果没有错误,我将调用 updateInventory 或 InsertInventory。让我感到困惑的是,这 3 个函数似乎根本没有执行(基于显示没有调用的 cloudwatch 日志。 这是因为您正在调用 getInventory ,然后无需等待即可调用 callback(null, completed);您必须调用 getInventory,然后在其回调中,如果没有发生错误,您可以进行检查并调用 updateInventory 或 InsertInventory。然后在他们的回调中,如果没有发生错误,您可以调用 context.done(null,SOME_RESULT);这样 getInventory 将被调用。然后 updateInventory/InsertInventory 将等待 getInventory 的结果。在那之后 context.done(null,SOME_RESULT);将等待 updateInventory/InsertInventory 完成,然后执行。 我尝试用 context.done 替换回调,但我得到了函数超时的相同结果。为了简化代码,我删除了嵌套调用并尝试了 getInventory,但它仍然不起作用。我在原始问题中包含了最新的代码。 替换 "exports.handler = (event, context, callback) => " 为 "exports.handler = function (event, context) " 之后替换 "callback(err,event.message );"用“context.done(err);”和“回调(空,数据);”用“context.done(null, data);”请注意,如果您使用 _testdriver.js 进行测试/调试,则应包含“var context = ; context.done = function () console.log("Lambda Function Complete"); " 以免收到运行时错误。如果您直接在 AWS 中进行测试,则不能包含此内容。 试过了,但我还是遇到了同样的问题。我决定不再在我将配置为 lambda 函数的函数中使用 lambda.invoke,而是只在函数中包含所有代码。这不是理想的解决方案,但我发现缺少有关错误的信息将使我以后难以调试问题。感谢您的宝贵时间。

以上是关于AWS Lambda Invoke 不执行 lambda 函数的主要内容,如果未能解决你的问题,请参考以下文章

MATLAB Runtime 可以在 AWS Lambda 上执行吗?

AWS lambda 调用不调用另一个 lambda 函数 - Node.js

aws lambda 上的保留并发不会阻止 lambda 进行更多扩展?

如何绕过 AWS Lambda 大小限制

为 Scala AWS Lambda 使用 Proguard

AWS Kinesis 连接器库