我正在处理一些要在AWS Lambda服务上托管的脚本。我之所以选择Node.js,是因为我对JS没用,但我还没学过Python或Java。然而,它被证明是一场噩梦,因为我需要查询一个MySQL数据库,而我却无法弄清楚如何正确地从函数中获取结果。
所以基本上我有这样的东西(我已经删除了一些东西,但你应该得到这个想法),这就是我想做的。我希望能够查询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;
};
但是那些精通Node.js的人显然这不起作用,因为对 connection.query 的调用是异步的,所以我的 databaseCommand 函数总是返回null (并且不会抛出)并导致我的主要功能出错。
请帮助我理解如何执行这样的基本同步请求。
修改
我见过使用异步方法的“解决方案”,这些方法显示喜欢(我可能有错误)以下更改,但我不知道这有什么不同。
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();
}
答案 0 :(得分:8)
看起来您缺少JavaScript中回调的基本概念。
您必须使用callback
函数中提供的main
参数。这就是你告诉Lambda你有一些结果要回来的原因。
以下是您的代码的简化版本,可以为您提供一个想法:
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);
};
注意callback
作为调用databaseCommand
的最后一个参数。这意味着当connection.query
从DB获取某些行时,它将调用Lambda提供的相同回调。
另一个重点是不要在每个Lambda执行上创建数据库连接。它的价格昂贵。您可以初始化变量一次,并在函数冻结时再次初始化它。在此处详细了解此信息 - https://aws.amazon.com/blogs/compute/container-reuse-in-lambda/
让我知道它是否适合您,因为我在线编辑了这段代码而没有任何检查。但希望你明白这一点。您可以在此处阅读更多如何编写处理函数的信息:http://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-handler.html?shortFooter=true
答案 1 :(得分:3)
您似乎误解了异步执行的工作原理以及AWS Lambda架构。这是代码示例的修改版本,带有内联注释:
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);
});
};