0

承诺连接的问题

我最近将我的 Node 应用程序从在我的本地机器上运行转换为使用 Amazon EC2 来运行 Node 应用程序和使用 VPN 来提供文件服务和 MySQL。

我对 Promises 的了解足以编写以下连接片段(在响应客户端之前运行 3 个查询),使用Bluebird。连接在我的机器上工作,但使用 VPN 托管的 MySQL 设置,连接每次都崩溃,大约在应用程序启动后 30 秒,我意识到这可能是因为我忘记关闭它们。

编辑:根据评论,问题似乎不在连接关闭中。

所以我以我知道的最好的方式修改了我的脚本来关闭连接,但是对于 Promises,这很令人困惑。此版本的连接不起作用。它不会失败或导致任何错误。它只是在服务器端不返回任何结果。我认为我的问题在于我关闭连接的方式。

  • 是什么导致了这个问题?

  • 是连接关闭吗?

  • 如果是这样,我将如何正确关闭它们?

我的(简化的)MySQL 连接尝试与Bluebird Promises

var mysql = require('mysql');
var Promise = require('bluebird');
var moment = require('moment');
function createConnection() {
    var connection = mysql.createConnection({
            dateStrings : true,
            host : 'hostname', 
            user : 'username',
            password : 'password', 
            database : 'database' 
        });
    connection = Promise.promisifyAll(connection);
    return connection;
}
function sendGame(req, res, sales, settings, categories, players) {
    var game = new Object();
    game.sales = sales;
    game.players = players;
    game.settings = settings;
    game.categories = categories;
    var JSONgame = JSON.stringify(game);
    console.log("Game: " + JSON.stringify(game, undefined, 4));
}
var retrieveSales = Promise.method(function (username, connection, timeFrame) {
        console.log('User ' + username + ' retrieving sales...');
        var q = 'select * from sales_entries where date BETWEEN ? AND ?';
        return connection.queryAsync(q, timeFrame).then(function (results) {
            return results[0];
        });
    });
var retrieveSettings = Promise.method(function (username, connection) {
        console.log('User ' + username + ' retrieving settings...');
        var q = 'select * from sales_settings';
        return connection.queryAsync(q).then(function (results) {
            return results[0];
        });
    });
var retrieveCategories = Promise.method(function (username, connection) {
        console.log('User ' + username + ' retrieving categories...');
        var q = 'select * from sales_categories';
        return connection.queryAsync(q).then(function (results) {
            return results[0];
        });
    });
var retrievePlayers = Promise.method(function (username, connection) {
        console.log('User ' + username + ' retrieving players...');
        var q = 'select * from users';
        return connection.queryAsync(q).then(function (results) {
            return results[0];
        });
    });
var gameSucceed = Promise.method(function gameSucceed(req, res) {
        var username = req.body.username;
        console.log('User ' + req.body.username + ' retrieving game...');
        var timeFrame = [moment().days(0).hour(0).minute(0).second(0).format("YYYY-MM-DD HH:mm:ss"), moment().days(6).hour(0).minute(0).second(0).format("YYYY-MM-DD HH:mm:ss")];
        //var connection = Promise.promisifyAll(createConnection());
        return connection.connectAsync().then(function () {
            console.log('Connection with the MySQL database openned for Game retrieval...');
            return Promise.all([retrieveSales(username, connection, timeFrame), retrieveSettings(username, connection), retrieveCategories(username, connection), retrievePlayers(username, connection)]);
        }).then(function () {
            connection.end(),
            console.log("...Connection with the MySQL database for Game retrieval ended")
        });
    });
function getGameData(req, res) {
    gameSucceed(req, res).spread(function (sales, settings, categories, players) {
        return sendGame(req, res, sales, settings, categories, players);
    });
};
var req = new Object();
var res = new Object();
req.body = {
    "username" : "user123",
    "password" : "password"
}
getGameData(req, res);

控制台结果

User user123 retrieving game...  
Connection with the MySQL database openned for Game retrieval...
User user123 retrieving sales... 
User user123 retrieving settings... 
User user123 retrieving categories... 
User user123 retrieving players... 
...Connection with the MySQL database for Game retrieval ended 
Game: {}
4

2 回答 2

1
var gameSucceed = function gameSucceed(req, res) {
    …
    var connection = createConnection());
    return connection.connectAsync().then(function () {
        return Promise.all([…]);
    }).then(function () {
        connection.end();
    });
};

最终从此方法返回的 Promise 没有分辨率值。它是由then您不从其回调的调用创建的return- 这将导致undefined. 要解决此问题,只需通过以下方式路由结果:

.then(function(results) {
    connection.end();
    return results;
});

但是,如果您这样做,则不会在出现错误时关闭连接。最好的解决方案是使用finally()方法,它就像finally同步代码中的子句一样工作。它的回调将被调用用于解决和拒绝,并且生成的承诺将自动进行该值。

.finally(function() {
    connection.end();
})
// .then(function(results) { })
于 2014-05-16T03:20:21.203 回答
0

你的代码有一个特定的资源管理问题,就像 Bergi 所说的那样。你必须记住什么时候关闭集合,什么时候不关闭。

然而,最佳解决方案是使用Promise.using,它仅在 Bluebird 的 v2 分支中可用,因此您将不得不等待一段时间。

在那之前,您可以创建自己的包装器方法来执行更基本的范围资源管理:

function connect(fn,timeout){
    timeout = (timeout === undefined) ? 8000 : timeout; // connection timeout
    return createConnection().then(function(connection){
        // run the function, when it resolves - close the connection
        // set a 7 second timeout on the connection
        return fn(connection).timeout(timeout).finally(function(){  
             connection.end();
        });
    });
}

这会让你做:

connect(function(connection){
    return gameSucceed(req,resp,connection); // connection is injected to that fn now
}).then(function(val){
      // gameSucceed resolution value here
});

现在,gameSucceed完成后,连接将自动关闭。这将使gameSucceed自己看起来像:

var gameSucceed = Promise.method(function gameSucceed(req, res,connection) {
    var username = req.body.username;
    console.log('User ' + req.body.username + ' retrieving game...');
    var timeFrame = [moment().days(0).hour(0).minute(0).second(0).format("YYYY-MM-DD HH:mm:ss"), moment().days(6).hour(0).minute(0).second(0).format("YYYY-MM-DD HH:mm:ss")];
    return connection.connectAsync().then(function () {
        console.log('Connection with the MySQL database openned for Game retrieval...');
        return Promise.all([retrieveSales(username, connection, timeFrame), retrieveSettings(username, connection), retrieveCategories(username, connection), retrievePlayers(username, connection)]);
    }); // no longer its responsibility to handle the connection
});

通常,您可能还想为您的代码考虑一种更加 OOPish 的编码风格。

祝你好运,编码愉快。

于 2014-05-16T14:35:10.740 回答