Handshake kann nach dem Aufrufen von quit nicht in die Warteschlange gestellt werden


81

Ich habe den folgenden Code implementiert:

module.exports = {
    getDataFromUserGps: function(callback)
    {
        connection.connect();
        connection.query("SELECT * FROM usergps", 
            function(err, results, fields) {
                if (err) return callback(err, null);
                return callback(null, results);
            }
        ); 
        connection.end();
    },
    loginUser: function(login, pass, callback)
    {
        connection.connect();
        connection.query(
            "SELECT id FROM users WHERE login = ? AND pass = ?",
            [login, pass],
            function(err, results, fields) 
            {
                if (err) return callback(err, null);
                return callback(null, results);
            }
        ); 
        connection.end();
    },
    getUserDetails: function(userid, callback)
    {
        connection.connect();
        connection.query(
            "SELECT * FROM userProfilDetails LEFT JOIN tags ON userProfilDetails.userId = tags.userId WHERE userProfilDetails.userid = ?",
            [userid],
            function(err, results, fields)
            {
                if (err) return callback(err, null);
                return callback(null, results);
            }
        );
        connection.end();
    },
    addTags: function(userId, tags)
    {
        connection.connect();
        connection.query(
            "INSERT INTO tag (userId, tag) VALUES (?, ?)",
            [userId, tags],
            function(err, results, fields)
            {
                if (err) throw err;
            }
        )
        connection.end();
    }
}

Alles funktioniert nur zum ersten Mal großartig. Wenn ich die Abfrage zum zweiten Mal "verwenden" möchte, wird folgende Fehlermeldung angezeigt:

Cannot enqueue Handshake after invoking quit

Ich habe versucht, keine .end()Verbindungen herzustellen, aber es hat nicht geholfen.

Wie kann ich dieses Problem beheben?


2
Könnten Sie die Frage zumindest schließen?
Andrew Rhyne

Für mich wurde der Fehler ausgelöst, wenn ich versuche, eine Verbindung zu öffnen, während eine geöffnet ist (dh zwei connection.connect () -Aufrufe nebeneinander haben)
sqram

Aufruf connection.endinnerhalb der connection.queryRückruffunktion, da diese asynchron ausgeführt wird.
Arjun Singh

Antworten:


240

Wenn Sie das Node-MySQL-Modul verwenden, entfernen Sie einfach .connect und .end. Habe das Problem einfach selbst gelöst. Anscheinend haben sie in ihrer letzten Iteration unnötigen Code eingegeben, der ebenfalls fehlerhaft ist. Sie müssen keine Verbindung herstellen, wenn Sie den Aufruf createConnection bereits ausgeführt haben


21
Das ist die schlechteste Antwort, die ich gefunden habe! wenn Sie eine Verbindung zu erstellen, und es öffnet sich , jetzt glauben wir , eine MySQL - Knoten in einer Funktion erstellen, jedes Mal die Funktion aufgerufen, sie schaffen und offene Verbindung halten, ein nach kurzer Zeit, Sie Max Limit erreicht der MySQL - Verbindung erhalten
Ata

4
Dann machst du es falsch. Sie sollen die Verbindung wiederverwenden
Andrew Rhyne

6
@ata node-mysql implementiert einen Verbindungspool. Sie sollten das Verbindungsobjekt nicht bei jeder Anforderung zerstören, da es nicht die eigentliche Anforderung ist. Ich bin mir nicht mal sicher, warum dein Kommentar positiv bewertet wird. Offensichtlich lesen die Leute die Dokumente nicht
Andrew Rhyne

5
Wenn Sie in aws lambda arbeiten, läuft Lambda immer wieder ab, wenn Sie die Verbindung nicht schließen. all diese Vorschläge helfen also nicht. Ich habe Tage mit diesem Thema verbracht.
Joseph Bolade Caxton-Idowu

Danke, das funktioniert bei mir. Die folgende Antwort von @ XP1 enthält weitere Details zu diesem Verhalten.
KeitelDOG

54

Gemäß:

TL; DR Sie müssen eine neue Verbindung herstellen, indem Sie die createConnectionMethode nach jeder Trennung aufrufen .

und

Hinweis: Wenn Sie Webanfragen bearbeiten, sollten Sie die Verbindungen nicht bei jeder Anfrage beenden. Erstellen Sie einfach beim Serverstart eine Verbindung und verwenden Sie das Verbindungs- / Clientobjekt, um die gesamte Zeit abzufragen. Sie können das Fehlerereignis abhören, um die Servertrennung zu behandeln und die Verbindung wiederherzustellen. Vollständiger Code hier .


Von:

Es sagt:

Server trennt die Verbindung

Möglicherweise verlieren Sie die Verbindung zu einem MySQL-Server aufgrund von Netzwerkproblemen, Zeitüberschreitung beim Server oder Absturz des Servers. Alle diese Ereignisse gelten als schwerwiegende Fehler und haben die folgenden err.code = 'PROTOCOL_CONNECTION_LOST'. Weitere Informationen finden Sie im Abschnitt Fehlerbehandlung.

Der beste Weg, um mit solchen unerwarteten Unterbrechungen umzugehen, ist unten dargestellt:

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

    connection = mysql.createConnection(connection.config);
    handleDisconnect(connection);
    connection.connect();
  });
}

handleDisconnect(connection);

Wie Sie im obigen Beispiel sehen können, erfolgt das erneute Verbinden einer Verbindung durch Herstellen einer neuen Verbindung. Nach dem Beenden kann ein vorhandenes Verbindungsobjekt nicht mehr vom Design her erneut verbunden werden.

Mit Pool werden getrennte Verbindungen aus dem Pool entfernt, wodurch Speicherplatz für eine neue Verbindung beim nächsten Aufruf von getConnection frei wird.


Ich habe die Funktion so angepasst, dass jedes Mal, wenn eine Verbindung benötigt wird, eine Initialisierungsfunktion die Handler automatisch hinzufügt:

function initializeConnection(config) {
    function addDisconnectHandler(connection) {
        connection.on("error", function (error) {
            if (error instanceof Error) {
                if (error.code === "PROTOCOL_CONNECTION_LOST") {
                    console.error(error.stack);
                    console.log("Lost connection. Reconnecting...");

                    initializeConnection(connection.config);
                } else if (error.fatal) {
                    throw error;
                }
            }
        });
    }

    var connection = mysql.createConnection(config);

    // Add handlers.
    addDisconnectHandler(connection);

    connection.connect();
    return connection;
}

Verbindung initialisieren:

var connection = initializeConnection({
    host: "localhost",
    user: "user",
    password: "password"
});

Kleiner Vorschlag: Dies gilt möglicherweise nicht für alle, aber ich bin auf ein kleines Problem im Zusammenhang mit dem Umfang gestoßen. Wenn das OP der Ansicht ist, dass diese Bearbeitung nicht erforderlich war, kann er sie entfernen. Für mich musste ich eine Zeile ändern initializeConnection, die var connection = mysql.createConnection(config);einfach nur war

connection = mysql.createConnection(config);

Der Grund dafür ist, dass wenn connectiones sich um eine globale Variable in Ihrem Programm handelt, das Problem zuvor darin bestand, dass Sie connectionbeim Behandeln eines Fehlersignals eine neue Variable erstellt haben. In meinem nodejs-Code habe ich jedoch weiterhin dieselbe globale connectionVariable zum Ausführen von Abfragen verwendet, sodass die neue Variable connectionim lokalen Bereich der initalizeConnectionMethode verloren geht. Bei der Änderung wird jedoch sichergestellt, dass die globale connectionVariable zurückgesetzt wird. Dies kann relevant sein, wenn ein Problem auftritt, das als bekannt ist

Abfrage kann nach schwerwiegendem Fehler nicht in die Warteschlange gestellt werden

nach dem Versuch, eine Abfrage durchzuführen, nachdem die Verbindung unterbrochen und die Verbindung erfolgreich wiederhergestellt wurde. Dies mag ein Tippfehler des OP gewesen sein, aber ich wollte es nur klarstellen.


1
Genialer Code, aber mein Skript scheint nach 90 Sekunden immer noch zu beenden (Code 8), ohne die Routine addDisconectHandler aufzurufen. Ideen?
emc

Geniale Antwort, ich habe mich nach einem Refactor für das Pooling entschieden, aber das ist eine ausgezeichnete Option.
Pogrindis

Gute Antwort danke. Dies sollte Teil des offiziellen Dokuments sein (und von Node-MySQL, nicht vom Entwickler, verwaltet werden).
Skoua

1
Dies ist eine fantastische Antwort, aber ich habe einen Vorschlag / eine Optimierung, die ich machen musste, damit dies für mich funktioniert. Ich bin mir nicht sicher, ob dies für alle benötigt wird, aber das hat mir definitiv geholfen. Sie können die Bearbeitung entfernen, wenn Sie der Meinung sind, dass dies nicht erforderlich ist. Vielen Dank für Ihre bisherige Hilfe.
Chris Gong

22

Ich hatte das gleiche Problem und Google führte mich hierher. Ich stimme @Ata zu, dass es nicht richtig ist, nur zu entfernen end(). Nach weiterem Googeln denke ich, dass die Verwendung poolingein besserer Weg ist.

Node-MySQL-Dokument zum Pooling

Es ist so:

var mysql = require('mysql');
var pool  = mysql.createPool(...);

pool.getConnection(function(err, connection) {
    connection.query( 'bla bla', function(err, rows) {
        connection.release();
    });
});

7

Verbinden Sie () und end () nicht innerhalb der Funktion. Dies führt bei wiederholten Aufrufen der Funktion zu Problemen. Stellen Sie nur die Verbindung her

var connection = mysql.createConnection({
      host: 'localhost',
      user: 'node',
      password: 'node',
      database: 'node_project'
    })

connection.connect(function(err) {
    if (err) throw err

});

einmal und verwenden Sie diese Verbindung wieder.

Innerhalb der Funktion

function insertData(name,id) {

  connection.query('INSERT INTO members (name, id) VALUES (?, ?)', [name,id], function(err,result) {
      if(err) throw err
  });


}

5

AWS Lambda funktioniert

Verwenden Sie mysql.createPool () mit connection.destroy ()

Auf diese Weise verwenden neue Aufrufe den eingerichteten Pool, lassen die Funktion jedoch nicht laufen. Obwohl Sie nicht den vollen Nutzen aus dem Pooling ziehen können (jede neue Verbindung verwendet eine neue Verbindung anstelle einer vorhandenen), kann ein zweiter Aufruf eine neue Verbindung herstellen, ohne dass die vorherige Verbindung zuerst geschlossen werden muss.

Bezüglich connection.end()

Dies kann dazu führen, dass ein nachfolgender Aufruf einen Fehler auslöst. Der Aufruf wird später noch wiederholt und funktioniert, jedoch mit Verzögerung.

In Bezug mysql.createPool()aufconnection.release()

Die Lambda-Funktion wird bis zum geplanten Zeitlimit weiter ausgeführt, da noch eine offene Verbindung besteht.

Codebeispiel

const mysql = require('mysql');

const pool = mysql.createPool({
  connectionLimit: 100,
  host:     process.env.DATABASE_HOST,
  user:     process.env.DATABASE_USER,
  password: process.env.DATABASE_PASSWORD,
});

exports.handler = (event) => {
  pool.getConnection((error, connection) => {
    if (error) throw error;
    connection.query(`
      INSERT INTO table_name (event) VALUES ('${event}')
    `, function(error, results, fields) {
      if (error) throw error;
      connection.destroy();
    });
  });
};

Ich habe ein NodeJS-Skript, das als AWS Lambda-Funktion ausgeführt wird. Es pingt eine Azure-API an, die jeweils 100 Datensätze mit einer "nächsten" URL zurückgibt, um die nächsten 100 bis zum Ende des Datensatzes abzurufen. Meine INSERT-Funktion wird also mehrmals aufgerufen. Ich habe den Fehler "Handshake kann nach Aufrufen von quit nicht in die Warteschlange gestellt werden" erhalten, bis ich die Zeilen connection.connect () und connection.end () entfernt habe. Ist es sinnvoller, hier stattdessen einen Pool zu verwenden? Ich bin nicht sicher, wann ich "connection.end ()" aufrufen würde, wenn der endgültige Datensatz von der API zurückgegeben wird ...
Shafique

2

anstelle der connection.connect();Verwendung -

if(!connection._connectCalled ) 
{
connection.connect();
}

Wenn es dann bereits aufgerufen wird connection._connectCalled =true, wird
& nicht ausgeführt connection.connect().

Hinweis - nicht verwendenconnection.end();


1

Ich denke, dieses Problem ähnelt meinem:

  1. Stellen Sie eine Verbindung zu MySQL her
  2. Beenden Sie den MySQL-Dienst (sollte das Knotenskript nicht beenden)
  3. Starten Sie den MySQL-Dienst. Der Knoten stellt die Verbindung zu MySQL wieder her
  4. Abfrage der Datenbank -> FAIL (Abfrage nach schwerwiegendem Fehler kann nicht in die Warteschlange gestellt werden.)

Ich habe dieses Problem gelöst, indem ich eine neue Verbindung mit der Verwendung von Versprechungen wiederhergestellt habe (q).

mysql-con.js

'use strict';
var config          = require('./../config.js');
var colors          = require('colors');
var mysql           = require('mysql');
var q               = require('q');
var MySQLConnection = {};

MySQLConnection.connect = function(){
    var d = q.defer();
    MySQLConnection.connection = mysql.createConnection({
        host                : 'localhost',
        user                : 'root',
        password            : 'password',
        database            : 'database'
    });

    MySQLConnection.connection.connect(function (err) {
        if(err) {
            console.log('Not connected '.red, err.toString().red, ' RETRYING...'.blue);
            d.reject();
        } else {
            console.log('Connected to Mysql. Exporting..'.blue);
            d.resolve(MySQLConnection.connection);
        }
    });
    return d.promise;
};

module.exports = MySQLConnection;

mysqlAPI.js

var colors          = require('colors');
var mysqlCon        = require('./mysql-con.js');
mysqlCon.connect().then(function(con){
   console.log('connected!');
    mysql = con;
    mysql.on('error', function (err, result) {
        console.log('error occurred. Reconneting...'.purple);
        mysqlAPI.reconnect();
    });
    mysql.query('SELECT 1 + 1 AS solution', function (err, results) {
            if(err) console.log('err',err);
            console.log('Works bro ',results);
    });
});

mysqlAPI.reconnect = function(){
    mysqlCon.connect().then(function(con){
      console.log("connected. getting new reference");
        mysql = con;
        mysql.on('error', function (err, result) {
            mysqlAPI.reconnect();
        });
    }, function (error) {
      console.log("try again");
        setTimeout(mysqlAPI.reconnect, 2000);
    });
};

Ich hoffe das hilft.


0

LÖSUNG: Um diesen Fehler zu vermeiden (für AWS LAMBDA):

Um die "Nodejs-Ereignisschleife" zu beenden, müssen Sie die Verbindung beenden und die Verbindung erneut herstellen. Fügen Sie den nächsten Code hinzu, um den Rückruf aufzurufen:

connection.end( function(err) {
        if (err) {console.log("Error ending the connection:",err);}

       //  reconnect in order to prevent the"Cannot enqueue Handshake after invoking quit"

         connection = mysql.createConnection({
                host     : 'rds.host',
                port     :  3306,
                user     : 'user',
               password : 'password',
               database : 'target database'

               });
        callback(null, {
            statusCode: 200,
            body: response,

        });
    });

Wird nicht jeder Aufruf der Lambda-Funktion zu einer offenen MySQL-Verbindung führen? Dies ist nur eine andere Möglichkeit, die akzeptierte Antwort zu geben, was keine gute Idee ist
Brian McCall

1
Nee. Wenn Sie connection.end aufrufen, wird die Schleife beendet, die Verbindung zur Datenbank bleibt jedoch im Status "Verbindung beenden". Wenn Sie versuchen, eine neue Verbindung zu öffnen, wird immer die Fehlermeldung "Handshake kann nach dem Aufrufen von quit nicht in die Warteschlange gestellt werden" angezeigt. Bei der zusätzlichen createConnection wird also dieser Fehler angezeigt, und die nächste Verbindung ist nicht fehlgeschlagen. Dies ist nur ein Weg, um dieses Problem zu lösen. Tatsächlich muss das MySQL-Modul ein saubereres Verbindungsende durchführen.
Jorge Valvert

0

Wenn Sie versuchen, ein Lambda zu bekommen, habe ich festgestellt, dass das Beenden des Handlers mit context.done()dem Lambda beendet werden muss. Bevor diese 1 Zeile hinzugefügt wird, wird sie nur ausgeführt und ausgeführt, bis das Zeitlimit abgelaufen ist.


Codebeispiel dafür bitte?
Shafique

0

Sie können debug verwenden: false,

Beispiel: // MySQL-Verbindung

var dbcon1 = mysql.createConnection({
      host: "localhost",
      user: "root",
      password: "",
      database: "node5",
      debug: false,
    });
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.