web-dev-qa-db-fra.com

Node.js MySQL nécessitant une connexion persistante

J'ai besoin d'une connexion MySQL persistante pour mon application Web Node. Le problème est que cela se produit environ plusieurs fois par jour:

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

Voici mon code de connexion:

// 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);

Comme vous pouvez le voir, le code handleDisconnect ne fonctionne pas.

33
apscience

Utilisez le pool de connexions mysql. Il se reconnectera lorsqu'une connexion s'éteindra et vous aurez l'avantage supplémentaire de pouvoir effectuer plusieurs requêtes SQL en même temps. Si vous n'utilisez pas le pool de bases de données, votre application bloquera les demandes de base de données en attendant la fin des demandes de base de données en cours d'exécution.

Je définis généralement un module de base de données où je garde mes requêtes séparées de mes itinéraires. Cela ressemble à ceci ...

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);
    });
  });
});
49
Daniel

Je sais que cela est super retardé, mais j'ai écrit une solution à ce problème qui, je pense, pourrait être un peu plus générique et utilisable. J'avais écrit une application entièrement dépendante de connection.query() et le passage à un pool a interrompu ces appels.

Voici ma solution:

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);
        });
      });
    }
};

Cela instancie le pool une fois, puis exporte une méthode nommée query. À présent, lorsque connection.query() est appelée n'importe où, elle appelle cette méthode, qui saisit d'abord une connexion du pool, puis transmet les arguments à la connexion. Il a pour effet supplémentaire de récupérer le rappel en premier, de sorte qu'il peut rappeler toutes les erreurs lors de la capture d'une connexion à partir du pool.

Pour l'utiliser, il suffit de l'exiger comme module à la place de mysql. Exemple:

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);
    }
  });
}

Cela ressemble à la requête normale, mais ouvre en fait un pool et récupère une connexion du pool en arrière-plan.

29
Adam Yost

En réponse à @ gladsocc question:

Existe-t-il un moyen d'utiliser les pools sans tout refactoriser? J'ai des dizaines de requêtes SQL dans l'application.

C'est ce que j'ai fini par construire. C'est un wrapper pour la fonction de requête. Il saisira la connexion, fera la requête, puis libérera la connexion.

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;
            }
        };
    }
};

Et je l'utilise comme je le ferais normalement.

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});
          });
18
oportocala