重现 MySQL 错误:服务器关闭连接(node.js)

Posted

技术标签:

【中文标题】重现 MySQL 错误:服务器关闭连接(node.js)【英文标题】:Reproduce MySQL error: The server closed the connection (node.js) 【发布时间】:2012-10-12 15:56:06 【问题描述】:

我正在尝试使用node mysql library 重现我在 EC2 上的 node.js 应用程序中看到的 MySQL 错误:

连接丢失:服务器关闭了连接。

我无法在本地重现该错误 - 我的代码可以很好地处理杀死数据库 - 它只是每隔几秒钟重新检查一次,并在重新启动后重新连接到数据库。在 EC2 上,它发生在太平洋时间凌晨 4 点左右,但数据库仍然正常运行。

我愿意

    用我的本地 mysql 重现崩溃 在我的 mysql 帮助模块中添加我需要的任何逻辑来处理这个

这是我的 node.js 应用程序中的错误:

2012-10-22T08:45:40.518Z - 错误:uncaughtException date=Mon Oct 22 2012 08:45:40 GMT+0000 (UTC), pid=14184, uid=0, gid=0, cwd=/home/ec2-user/my-app, execPath=/usr/bin/nodejs, 版本=v0.6.18, argv=[/usr/local/bin/node, /home/ec2-user/my-app/app.js, --my-app], rss=15310848, heapTotal=6311392, heapUsed=5123292, loadavg=[0.0029296875, 0.0146484375,0.04541015625],正常运行时间=3238343.511107486,跟踪=[列=13, 文件=/home/ec2-user/my-app/node_modules/mysql/lib/protocol/Protocol.js, 函数=Protocol.end, line=63, method=end, native=false, column=10, 文件=stream.js,函数=Socket.onend,行=80,方法=onend, native=false,column=20,file=events.js,function=Socket.emit, 行=88,方法=发射,本机=假,列=51,文件=net.js, 功能=TCP.onread,行=388,方法=onread,本机=false], stack=[错误:连接丢失:服务器关闭了连接。, 在 Protocol.end (/home/ec2-user/my-app/node_modules/mysql/lib/protocol/Protocol.js:63:13),在 Socket.onend (stream.js:80:10),在 Socket.emit (events.js:88:20),在 TCP.onread (net.js:388:51)]

这是我的代码(mysql 辅助模块):

module.exports = function (conf,logger) 
  var mysql = require('mysql');

  var connectionState = false;
  var connection = mysql.createConnection(
    host: conf.db.hostname,
    user: conf.db.user,
    password: conf.db.pass,
    database: conf.db.schema,
    insecureAuth: true
  );

  function attemptConnection(connection) 
    if(!connectionState)
      connection = mysql.createConnection(connection.config);
      connection.connect(function (err) 
        // connected! (unless `err` is set)
        if (err) 
          logger.error('mysql db unable to connect: ' + err);
          connectionState = false;
         else 
          logger.info('mysql connect!');
          connectionState = true;
        
      );
      connection.on('close', function (err) 
        logger.error('mysqldb conn close');
        connectionState = false;
      );
      connection.on('error', function (err) 
        logger.error('mysqldb error: ' + err);
        connectionState = false;

        /*
        if (!err.fatal) 
          return;
        
        if (err.code !== 'PROTOCOL_CONNECTION_LOST') 
          throw err;
        
        */
      );
    
  
  attemptConnection(connection);

  var dbConnChecker = setInterval(function()
    if(!connectionState)
      logger.info('not connected, attempting reconnect');
      attemptConnection(connection);
    
  , conf.db.checkInterval);

  return connection;
;

【问题讨论】:

nodejs mysql Error: Connection lost The server closed the connection的可能重复 【参考方案1】:

解决方案是使用池连接!

您可以编写代码来手动处理连接,它可以工作。 但是池是为此设计的,使用池连接解决了连接丢失错误。

var mysql = require('mysql');
var pool  = mysql.createPool(
    connectionLimit : 10,
     host            : 'example.org',
    user            : 'bob',
     password        : 'secret',
      database        : 'my_db'
    );

   pool.query('SELECT 1 + 1 AS solution', function (error, results, fields) 
            if (error) throw error;
            console.log('The solution is: ', results[0].solution);
         );

pooling mysql connection

【讨论】:

【参考方案2】:

查看node-mysql中的mysql池功能

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

pool.getConnection(function(err, connection) 
  // connected! (unless `err` is set)
  connection.end();
);

【讨论】:

是的,这似乎是最好的方法,并且您可以并行锤击您的数据库作为奖励。 最好将connection.end() 替换为connection.release(),这样它就可以在不断开连接的情况下重复用于进一步的请求。【参考方案3】:

这是我最终使用的,效果很好。在偶尔的连接丢失/重新启动时,它恢复得很好。我有一个 database.js 文件,它建立连接并定期检查它们。

提出请求:

var conn = require('./database');
var sql = 'SELECT foo FROM bar;';
conn.query(sql, [userId, plugId], function (err, rows) 
   // logic

这是我的数据库.js

var mysql = require('mysql');
var Common = require('./common');
var conf = Common.conf;
var logger = Common.logger;

var connectionState = false;
var connection = mysql.createConnection(
  host: conf.db.hostname,
  user: conf.db.user,
  password: conf.db.pass,
  database: conf.db.schema,
  insecureAuth: true
);
connection.on('close', function (err) 
  logger.error('mysqldb conn close');
  connectionState = false;
);
connection.on('error', function (err) 
  logger.error('mysqldb error: ' + err);
  connectionState = false;
);

function attemptConnection(connection) 
  if(!connectionState)
    connection = mysql.createConnection(connection.config);
    connection.connect(function (err) 
      // connected! (unless `err` is set)
      if (err) 
        logger.error('mysql db unable to connect: ' + err);
        connectionState = false;
       else 
        logger.info('mysql connect!');

        connectionState = true;
      
    );
    connection.on('close', function (err) 
      logger.error('mysqldb conn close');
      connectionState = false;
    );
    connection.on('error', function (err) 
      logger.error('mysqldb error: ' + err);

      if (!err.fatal) 
        //throw err;
      
      if (err.code !== 'PROTOCOL_CONNECTION_LOST') 
        //throw err;
       else 
        connectionState = false;
      

    );
  

attemptConnection(connection);

var dbConnChecker = setInterval(function()
  if(!connectionState)
    logger.info('not connected, attempting reconnect');
    attemptConnection(connection);
  
, conf.db.checkInterval);

// Mysql query wrapper. Gives us timeout and db conn refreshal! 
var queryTimeout = conf.db.queryTimeout;
var query = function(sql,params,callback)
  if(connectionState) 
    // 1. Set timeout
    var timedOut = false;
    var timeout = setTimeout(function () 
      timedOut = true;
      callback('MySQL timeout', null);
    , queryTimeout);

    // 2. Make query
    connection.query(sql, params, function (err, rows) 
      clearTimeout(timeout);
      if(!timedOut) callback(err,rows);
    );
   else 
    // 3. Fail if no mysql conn (obviously)
    callback('MySQL not connected', null);
  


// And we present the same interface as the node-mysql library!
// NOTE: The escape may be a trickier for other libraries to emulate because it looks synchronous
exports.query = query;
exports.escape = connection.escape;

【讨论】:

请告诉我配置文件中的queryTimeout和checkInterval要设置什么时间?【参考方案4】:

我遇到了类似的问题,并创建了一个 getConnection() 包装函数,该函数在将 mysql 连接返回给调用者之前检查其健康状况并根据需要重新建立连接。在我的测试中,它为应用程序透明地处理了致命和非致命的连接问题。如果连接只是超时,应用程序会恢复而不会出现任何错误。如果存在暂时但致命的数据库连接问题,一旦数据库连接再次可用,应用程序将自动恢复运行。

至于重现问题进行测试,将以下两行添加到[mysqld]块下的my.ini或my.cnf文件中:

interactive_timeout=30
wait_timeout=30

这是我命名为“database.js”的文件的内容:

var mysql = require("mysql");
var CONFIG = require(__dirname + "/configuration");

module.exports.getConnection = function() 
    // Test connection health before returning it to caller.
    if ((module.exports.connection) && (module.exports.connection._socket)
            && (module.exports.connection._socket.readable)
            && (module.exports.connection._socket.writable)) 
        return module.exports.connection;
    
    console.log(((module.exports.connection) ?
            "UNHEALTHY SQL CONNECTION; RE" : "") + "CONNECTING TO SQL.");
    var connection = mysql.createConnection(
        host     : CONFIG.db.host,
        user     : CONFIG.db.user,
        password : CONFIG.db.password,
        database : CONFIG.db.database,
        port     : CONFIG.db.port
    );
    connection.connect(function(err) 
        if (err) 
            console.log("SQL CONNECT ERROR: " + err);
         else 
            console.log("SQL CONNECT SUCCESSFUL.");
        
    );
    connection.on("close", function (err) 
        console.log("SQL CONNECTION CLOSED.");
    );
    connection.on("error", function (err) 
        console.log("SQL CONNECTION ERROR: " + err);
    );
    module.exports.connection = connection;
    return module.exports.connection;


// Open a connection automatically at app startup.
module.exports.getConnection();

// If you've saved this file as database.js, then get and use the
// connection as in the following example:
// var database = require(__dirname + "/database");
// var connection = database.getConnection();
// connection.query(query, function(err, results)  ....

【讨论】:

【参考方案5】:

使用generic-pool,我写了一些在本地工作的东西。我想我会测试它,看看它是否不会在服务器端以奇怪的方式崩溃。

// Test node connection pool stuff

// Create a MySQL connection pool with
// a max of 10 connections, a min of 2, and a 30 second max idle time
var poolModule = require('generic-pool');
var pool = poolModule.Pool(
    name     : 'mysql',
    create   : function(callback) 
        var Client = require('mysql').Client; // use node-mysql library in all it's dubious glory
        var c = new Client();
        c.user     = 'root';
        c.password = 'xxx';
        c.database = 'test';
        c.on('close', function (err) 
          console.log('mysqldb conn close');
        );
        c.on('error', function (err) 
          console.log('mysqldb error: ' + err);
        );

        // parameter order: err, resource
        // new in 1.0.6
        callback(null, c);
    ,
    destroy  : function(client)  client.end(); ,
    max      : 10,
    // optional. if you set this, make sure to drain() (see step 3)
    min      : 2, 
    // specifies how long a resource can stay idle in pool before being removed
    idleTimeoutMillis : 30000,
     // if true, logs via console.log - can also be a function
    log : true 
);

var http = require('http');
http.createServer(function (req, res) 

  // Get db conn
  pool.acquire(function(err, client) 
      if (err) 
          // handle error - this is generally the err from your
          // factory.create function 
          console.log('pool.acquire err: ' + err);
          res.writeHead(500, 'Content-Type': 'application/json');
          out = 
            err: err
          
          res.end(JSON.stringify(out));
      
      else 
          client.query("select * from foo", [], function(err, results) 

              if(err)
                res.writeHead(500, 'Content-Type': 'application/json');
                out = 
                  err: err
                
                res.end(JSON.stringify(out));
               else 
                res.writeHead(500, 'Content-Type': 'application/json');
                out = 
                  results: results
                
                res.end(JSON.stringify(out));
              


              // return object back to pool
              pool.release(client);
          );
      
  );
).listen(9615);

请不要无缘无故在凌晨 4 点死去!

【讨论】:

不。我基本上在每个mysql查询上都设置了一个包装器来检查是否有连接,如果没有,则异步建立连接。我会把我的代码作为答案

以上是关于重现 MySQL 错误:服务器关闭连接(node.js)的主要内容,如果未能解决你的问题,请参考以下文章

node.js ssh2 => 如何关闭连接并处理 ECONNRESET 错误

node.js mysql错误:ECONNREFUSED

节点MYSQL连接丢失:服务器关闭了连接

node-red node-red-contrib-mssql “连接错误:连接已关闭。”

Django 1.3 的随机错误“在查询期间丢失与 MySQL 服务器的连接”[关闭]

MySQL Wordpress 问题 - “太多连接重复问题”和“WordPress 数据库错误服务器正在关闭”