简体   繁体   English

如何从AWS Lambda node.js异步函数返回数据?

[英]How to return data from AWS Lambda node.js asynchronous function?

I'm working on some scripts to be hosted on the AWS Lambda service. 我正在研究要在AWS Lambda服务上托管的一些脚本。 I chose Node.js because I'm okay with JS but I've not learned Python or Java. 我之所以选择Node.js是因为我对JS没问题,但我还没有学习过Python或Java。 However it's proving to be a nightmare because I need to query a MySQL database and I just cannot figure out how to get the result out of the function correctly. 但是事实证明这是一场噩梦,因为我需要查询一个MySQL数据库,而我却无法弄清楚如何正确地从函数中获取结果。

So basically I have something like this (I've cut some stuff out but you should get the idea), and this is all I want to do. 所以基本上我有这样的事情(我已经删掉了一些东西,但是你应该明白了),这就是我要做的。 I want to be able to query the MySQL database and when it has an answer simply return it or throw if there was an error. 我希望能够查询MySQL数据库,当它有答案时,只需将其返回或在出现错误时抛出。

var mysql = require("mysql");     //npm installed module "mysql"
var err = require("./errors.js"); //internally requires npm installed module "errors"

var main = function(event, context, callback){
  try{
    //Confidential
    var data = null;
    //Confidential
    try{
      data = databaseCommand("SELECT * FROM `<table>` WHERE <field> = <value>");
    }
    catch(e){
      if(e instanceof err.Database)
        //Return state that indicates "Internal server error".
      else
        throw e;
      return
    }
    //Do maths on data
    //Return "OK" and result
  }
  catch(e){
    //Return "Unkown error"
  }
};
var databaseCommand = function(cmdString){
  if(typeof cmdString !== "string") throw new err.InputInvalidType({explanation: "'cmdString' is of type '" + typeof cmdString + "', expected type 'string'"});

  var connection = mysql.createConnection({
    host: process.env.db_host,
    port: process.env.db_port || 3306,
    user: process.env.db_username,
    password: process.env.db_password,
    database: process.env.db_database
  });

  var ret = {
    error: null,
    result: null
  };

  //I cut out the connection.connect() because it can be implied and I'm confused enough
  connection.query(cmdString, function(error, rows){
    if(error)
      ret.error = error;
    else
      ret.result = rows;
  });
  connection.end();

  if(ret.error)
    throw new err.Database();
  return ret.result;
};

But for those who are versed in Node.js obviously this doesn't work because the call to connection.query is asynchronous, so my databaseCommand function always returns null (and doesn't throw) and causes errors in my main function. 但是对于那些精通Node.js的人来说,这显然是行不通的,因为对connection.query的调用是异步的,因此我的databaseCommand函数始终返回null(并且不会抛出),并在我的main函数中引起错误。

Please help me understand how I can perform a basic synchronous request like this. 请帮助我了解如何执行这样的基本同步请求。

EDIT 编辑

I've seen "solutions" on using async methods that show something like (I probably have this wrong) the following changes, but I don't see how this is any different. 我已经看到了使用异步方法,显示类似的“解决方案”(我可能都有这个毛病)以下的变化,但我看不出这是任何不同。

var mysql = require("mysql");     //npm installed module "mysql"
var err = require("./errors.js"); //internally requires npm installed module "errors"

var main = function(event, context, callback){
  try{
    //Confidential
    var data = null;
    //Confidential
    try{
      databaseCommand("SELECT * FROM `<table>` WHERE <field> = <value>", function(err, result){
        if(err)
          throw err;
        data = result;
      });
      //This function will still return before data is set
      //Maths will still be performed before data is set
    }
    catch(e){
      if(e instanceof err.Database)
        //Return state that indicates "Internal server error".
      else
        throw e;
      return
    }
    //Do maths on data
    //Return result
  }
  catch(e){
    //Return "Unkown error"
  }
}
var databaseCommand = function(cmdString, callback){
  if(typeof cmdString !== "string") throw new err.InputInvalidType({explanation: "'cmdString' is of type '" + typeof cmdString + "', expected type 'string'"});

  var connection = mysql.createConnection({
    host: process.env.db_host,
    port: process.env.db_port || 3306,
    user: process.env.db_username,
    password: process.env.db_password,
    database: process.env.db_database
  });

  var ret = {
    error: null,
    result: null
  };

  //I cut out the connection.connect() because it can be implied and I'm confused enough
  connection.query(cmdString, function(error, rows){
    if(error)
      callback(err, null);
    else
      callback(null, rows);
  });
  connection.end();
}

Looks like you are missing basic concepts of callbacks in JavaScript. 似乎您缺少JavaScript回调的基本概念。

You must utilize callback argument you are provided with in main function. 您必须利用main函数中提供的callback参数。 That's how you tell Lambda you have some results to return. 这就是您告诉Lambda您要返回一些结果的方式。

Here is a bit simplified version of your code to give you an idea: 这是您的代码的简化版本,可以帮助您:

var mysql = require("mysql"); //npm installed module "mysql"
var err = require("./errors.js"); //internally requires npm installed module "errors"

var connection;

var main = function(event, context, callback) {
    databaseCommand("SELECT * FROM `<table>` WHERE <field> = <value>", (error, rows) => {
        if (error) return callback(error);

        var results = doSomeMathWithRows(rows);

        callback(null, results);
    });
};

var databaseCommand = function(cmdString, callback) {
    if (typeof cmdString !== "string") throw new err.InputInvalidType({
        explanation: "'cmdString' is of type '" + typeof cmdString + "', expected type 'string'"
    });

    // Don't init DB connection for every request
    // Lambda functions can lose it only after freezing (see docs for details when)
    // so we create connection only on demand
    if (!connection) {
        connection = mysql.createConnection({
            host: process.env.db_host,
            port: process.env.db_port || 3306,
            user: process.env.db_username,
            password: process.env.db_password,
            database: process.env.db_database
        });
    }

    connection.query(cmdString, callback);
};

Notice a callback as a last argument of invoking databaseCommand . 注意, callback是调用databaseCommand的最后一个参数。 That means when connection.query will fetch some rows from DB it will invoke same callback Lambda provides. 这意味着,当connection.query从数据库中获取某些行时,它将调用Lambda提供的相同回调。

Another important point is not to create DB connection on each Lambda execution. 另一个要点是不要在每个Lambda执行上创建数据库连接。 It's expensive. 它的价格昂贵。 You can init variable one time and init it again when function was freezed. 您可以一次初始化变量,然后在冻结函数时再次初始化。 Read more about this here - https://aws.amazon.com/blogs/compute/container-reuse-in-lambda/ 在此处了解更多信息-https: //aws.amazon.com/blogs/compute/container-reuse-in-lambda/

Let me know if it works for you, as I edited this code online without any checks. 让我知道它是否对您有用,因为我无需任何检查即可在线编辑此代码。 But hope you got the point. 但希望您明白这一点。 You can read more how to write a handler function here: http://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-handler.html?shortFooter=true 您可以在此处了解更多有关如何编写处理函数的信息: http : //docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-handler.html?shortFooter=true

You seem to be misunderstanding how asynchronous execution works as well as the AWS Lambda architecture. 您似乎误会了异步执行以及AWS Lambda架构的工作方式。 Here's a modified version of your code sample, with inline comments: 这是代码示例的修改后的版本,带有内联注释:

var mysql = require("mysql");     //npm installed module "mysql"

// This would be the exported handler function that gets invoked when your AWS Lambda function is triggered
exports.queryDbHandler = function(event, context, callback) {
  try {
    // This function executes an asynchronous operation so you need to pass in a callback that will be executed once it's done
    databaseCommand("SELECT * FROM `<table>` WHERE <field> = <value>", function onData(error, dbData) {
      // Once the callback is executed, you call the callback provided to your Lambda function. First argument is an error and the second is payload/result of the operation. First argument should be null if all went ok
      if (error) {
        callback(error);
      } else {
        let dbDataProcessed = // do something to dbData
        callback(null, dbDataProcessed);
      }
    });
  }
  catch(e) {
    // In case you had an exception in the synchronous part of the code, you still need to invoke the callback and provide an error
    callback(e);
  }
}

var databaseCommand = function(cmdString, onResultCallback){
  // Instead of throwing, it would be better to just invoke the callback and provide an error object
  if(typeof cmdString !== "string") throw new err.InputInvalidType({explanation: "'cmdString' is of type '" + typeof cmdString + "', expected type 'string'"});

  var connection = mysql.createConnection({
    host: process.env.db_host,
    port: process.env.db_port || 3306,
    user: process.env.db_username,
    password: process.env.db_password,
    database: process.env.db_database
  });

  connection.query(cmdString, function(error, rows) {
      // Once we have the data, or an error happened, invoke the callback
      onResultCallback(error, rows);
  });
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM