【问题标题】:Node.js MySQL Needing Persistent ConnectionNode.js MySQL 需要持久连接
【发布时间】:2013-06-05 14:51:57
【问题描述】:

我的 Node Web 应用需要一个持久的 MySQL 连接。问题是这种情况每天大约会发生几次:

Error: Connection lost: The server closed the connection.
at Protocol.end (/var/www/n/node_modules/mysql/lib/protocol/Protocol.js:73:13)
at Socket.onend (stream.js:79:10)
at Socket.EventEmitter.emit (events.js:117:20)
at _stream_readable.js:895:16
at process._tickCallback (node.js:415:13)
error: Forever detected script exited with code: 8
error: Forever restarting script for 2 time
info: socket.io started

这是我的连接代码:

// Yes I know multipleStatements can be dangerous in the wrong hands.
var sql = mysql.createConnection({
    host: 'localhost',
    user: 'my_username',
    password: 'my_password',
    database: 'my_database',
    multipleStatements: true
});

sql.connect();

function handleDisconnect(connection) {
    connection.on('error', function(err) {
        if (!err.fatal) {
            return;
        }
        if (err.code !== 'PROTOCOL_CONNECTION_LOST') {
            throw err;
        }
        console.log('Re-connecting lost connection: ' + err.stack);
        sql = mysql.createConnection(connection.config);
        handleDisconnect(sql);
        sql.connect();
    });
}

handleDisconnect(sql);

如您所见,handleDisconnect 代码不起作用..

【问题讨论】:

  • 了解您在 Node 中使用的 MySQL 模块会有所帮助。
  • 我使用的是node-mysql,由标签给出。
  • 啊,对不起。没在标签里看到。

标签: mysql node.js node-mysql


【解决方案1】:

使用mysql连接池。当连接断开时它将重新连接,并且您可以获得能够同时进行多个 sql 查询的额外好处。如果您不使用数据库池,您的应用将在等待当前正在运行的数据库请求完成时阻止数据库请求。

我通常定义一个数据库模块,将查询与路由分开。它看起来像这样......

var mysql = require('mysql');
var pool  = mysql.createPool({
  host     : 'example.org',
  user     : 'bob',
  password : 'secret'
});

exports.getUsers = function(callback) {
  pool.getConnection(function(err, connection) {
    if(err) { 
      console.log(err); 
      callback(true); 
      return; 
    }
    var sql = "SELECT id,name FROM users";
    connection.query(sql, [], function(err, results) {
      connection.release(); // always put connection back in pool after last query
      if(err) { 
        console.log(err); 
        callback(true); 
        return; 
      }
      callback(false, results);
    });
  });
});

【讨论】:

  • 有没有办法在不重构所有内容的情况下使用池?我在应用中有几十个 SQL 查询。
  • 顺便说一句,我们如何知道mysql服务器一次最大连接数?
  • 检查您的 MySQL 配置中的“max_connections”。 dev.mysql.com/doc/refman/5.0/en/server-system-variables.html
  • 注意:connection.end() 当前会发出警告,表示该函数已被弃用(在下一个版本或其他内容中会更改行为),建议改为使用 connection.release()。
  • 使用池的缺点是您不能暂停池上的连接。这在队列处理项目时会派上用场。
【解决方案2】:

我知道这是超级延迟,但我已经为此编写了一个解决方案,我认为它可能更通用和可用。我编写了一个完全依赖于connection.query() 的应用程序并切换到池中断了这些调用。

这是我的解决方案:

var mysql = require('mysql');

var pool = mysql.createPool({
    host     : 'localhost',
    user     : 'user',
    password : 'secret',
    database : 'test',
    port     : 3306
});

module.exports = {
    query: function(){
        var sql_args = [];
        var args = [];
        for(var i=0; i<arguments.length; i++){
            args.push(arguments[i]);
        }
        var callback = args[args.length-1]; //last arg is callback
        pool.getConnection(function(err, connection) {
        if(err) {
                console.log(err);
                return callback(err);
            }
            if(args.length > 2){
                sql_args = args[1];
            }
        connection.query(args[0], sql_args, function(err, results) {
          connection.release(); // always put connection back in pool after last query
          if(err){
                    console.log(err);
                    return callback(err);
                }
          callback(null, results);
        });
      });
    }
};

这会实例化池一次,然后导出一个名为 query 的方法。现在,当在任何地方调用connection.query() 时,它会调用此方法,该方法首先从池中获取一个连接,然后将参数传递给该连接。它具有先抓取回调的附加效果,因此它可以回调从池中抓取连接的任何错误。

要使用它,只需将其作为模块代替 mysql。示例:

var connection = require('../middleware/db');

function get_active_sessions(){
  connection.query('Select * from `sessions` where `Active`=1 and Expires>?;', [~~(new Date()/1000)], function(err, results){
    if(err){
      console.log(err);
    }
    else{
      console.log(results);
    }
  });
}

这看起来就像普通的查询,但实际上是在后台打开一个池并从池中抓取一个连接。

【讨论】:

  • 一个用例会有所帮助:)
  • 很好的解决方案,尤其是当您需要暂停特定连接时
  • 帮助了我。我正在使用这个解决方案。谢谢。
【解决方案3】:

回复@gladsocc问题:

有没有办法在不重构所有内容的情况下使用池?我有 应用中有几十个 SQL 查询。

这就是我最终构建的。它是查询函数的包装器。它会抓取连接,进行查询,然后释放连接。

var pool = mysql.createPool(config.db);

exports.connection = {
    query: function () {
        var queryArgs = Array.prototype.slice.call(arguments),
            events = [],
            eventNameIndex = {};

        pool.getConnection(function (err, conn) {
            if (err) {
                if (eventNameIndex.error) {
                    eventNameIndex.error();
                }
            }
            if (conn) { 
                var q = conn.query.apply(conn, queryArgs);
                q.on('end', function () {
                    conn.release();
                });

                events.forEach(function (args) {
                    q.on.apply(q, args);
                });
            }
        });

        return {
            on: function (eventName, callback) {
                events.push(Array.prototype.slice.call(arguments));
                eventNameIndex[eventName] = callback;
                return this;
            }
        };
    }
};

我像往常一样使用它。

db.connection.query("SELECT * FROM `table` WHERE `id` = ? ", row_id)
          .on('result', function (row) {
            setData(row);
          })
          .on('error', function (err) {
            callback({error: true, err: err});
          });

【讨论】:

  • 我刚试过这样做,但是我得到了ReferenceError: db is not defined
  • @BrandonStewart 在第二个代码示例中您位于另一个文件中,因此您需要像往常一样获取连接,var db = require('./db.js'); 顶部某处
  • 效果很好,非常感谢您的帮助。
猜你喜欢
  • 2013-10-01
  • 2011-01-16
  • 2016-09-09
  • 1970-01-01
  • 2012-07-30
  • 2012-08-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多