Nie można umieścić w kolejce Handshake po wywołaniu quit

83

Zaimplementowałem następujący kod:

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

Tylko za pierwszym razem wszystko działa świetnie. Jeśli chcę „użyć” zapytania po raz drugi, pojawia się następujący błąd:

Cannot enqueue Handshake after invoking quit

Próbowałem nie .end()podłączać, ale to nie pomogło.

Jak mogę rozwiązać ten problem?

radek
źródło
2
Czy mógłbyś przynajmniej zamknąć to pytanie?
Andrew Rhyne
U mnie błąd został wyrzucony, gdy próbuję otworzyć połączenie, gdy jedno jest otwarte (tj. Mając dwa wywołania connection.connect () obok siebie)
sqram
zadzwoń connection.endwewnątrz connection.queryfunkcji zwrotnej, ponieważ będzie wykonanie asynchronicznie.
Arjun Singh,

Odpowiedzi:

244

Jeśli używasz modułu node-mysql, po prostu usuń pliki .connect i .end. Właśnie sam rozwiązałem problem. Najwyraźniej w ostatniej iteracji wrzucili niepotrzebny kod, który również zawiera błędy. Nie musisz się łączyć, jeśli już uruchomiłeś wywołanie createConnection

Andrew Rhyne
źródło
22
To najgorsza odpowiedź, jaką znalazłem! kiedy tworzysz połączenie i otwiera się, pomyśl, że tworzymy węzeł mysql w funkcji, za każdym razem, gdy funkcja jest wywoływana, tworzy i utrzymuje otwarte połączenie, a po krótkim czasie otrzymujesz Max Limit Reach połączenia mysql
Ata
5
Więc robisz to źle. Powinieneś ponownie użyć połączenia
Andrew Rhyne,
6
@ata node-mysql implementuje pulę połączeń. Nie należy niszczyć obiektu połączenia przy każdym żądaniu, ponieważ nie jest to rzeczywiste żądanie. Nie wiem nawet, dlaczego Twój komentarz zyskuje na popularności. Najwyraźniej ludzie nie czytają dokumentów
Andrew Rhyne,
5
Jeśli korzystasz z aws lambda, lambda będzie przekraczać limit czasu, jeśli nie zamkniesz połączenia. więc wszystkie te sugestie nie pomagają. Spędziłem kilka dni nad tą kwestią.
Joseph Bolade Caxton-Idowu
Dzięki, że dla mnie działa. Poniższa odpowiedź @ XP1 zawiera więcej szczegółów na temat tego zachowania.
KeitelDOG
54

Według:

TL; DR Musisz nawiązać nowe połączenie, wywołując createConnectionmetodę po każdym rozłączeniu.

i

Uwaga: jeśli obsługujesz żądania internetowe, nie powinieneś przerywać połączeń przy każdym żądaniu. Po prostu utwórz połączenie podczas uruchamiania serwera i używaj obiektu połączenia / klienta do wysyłania zapytań przez cały czas. Możesz nasłuchiwać zdarzenia błędu, aby obsłużyć rozłączenie serwera i w celu ponownego połączenia. Pełny kod tutaj .


Od:

To mówi:

Serwer rozłącza się

Możesz utracić połączenie z serwerem MySQL z powodu problemów z siecią, przekroczenia limitu czasu serwera lub awarii serwera. Wszystkie te zdarzenia są uważane za błędy krytyczne i będą miały rozszerzenie err.code = 'PROTOCOL_CONNECTION_LOST'. Więcej informacji można znaleźć w sekcji Obsługa błędów.

Najlepszy sposób radzenia sobie z takimi nieoczekiwanymi rozłączeniami pokazano poniżej:

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

Jak widać w powyższym przykładzie, ponowne podłączenie połączenia odbywa się poprzez ustanowienie nowego połączenia. Po zakończeniu istniejącego obiektu połączenia nie można ponownie podłączyć zgodnie z projektem.

W przypadku puli rozłączone połączenia zostaną usunięte z puli, zwalniając miejsce na nowe połączenie, które ma zostać utworzone w następnym wywołaniu getConnection.


Dostosowałem funkcję w taki sposób, że za każdym razem, gdy potrzebne jest połączenie, funkcja inicjalizująca automatycznie dodaje programy obsługi:

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

Inicjowanie połączenia:

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

Drobna sugestia: może to nie dotyczyć wszystkich, ale napotkałem drobny problem dotyczący zakresu. Jeśli OP uzna, że ​​ta zmiana była niepotrzebna, może ją usunąć. Dla mnie musiałem zmienić linię initializeConnection, która miała var connection = mysql.createConnection(config);po prostu po prostu

connection = mysql.createConnection(config);

Powodem jest to, że jeśli connectionjest to zmienna globalna w twoim programie, to poprzedni problem polegał na tym, że tworzyłeś nową connectionzmienną podczas obsługi sygnału błędu. Ale w moim kodzie nodejs nadal używałem tej samej connectionzmiennej globalnej do wykonywania zapytań, więc nowa connectionbyłaby utracona w lokalnym zakresie initalizeConnectionmetody. Ale w modyfikacji zapewnia, że connectionzmienna globalna zostanie zresetowana.Może to mieć znaczenie, jeśli występuje problem znany jako

Nie można umieścić zapytania w kolejce po błędzie krytycznym

po próbie wykonania zapytania po utracie połączenia, a następnie pomyślnym ponownym połączeniu. To mogła być literówka OP, ale chciałem tylko wyjaśnić.

XP1
źródło
1
Niesamowity kod, ale mój skrypt nadal wydaje się kończyć (kod 8) po 90 sekundach bez wchodzenia nawet w procedurę addDisconectHandler. Pomysły?
emc
Świetna odpowiedź, po refaktorze wybrałem pulę, ale to doskonała opcja.
Pogrindis
Dobra odpowiedź, dziękuję. Powinien być częścią oficjalnego dokumentu (i obsługiwany przez node-mysql, a nie programistę).
Skoua,
1
To fantastyczna odpowiedź, ale mam sugestię / poprawkę, którą musiałem zrobić, aby to zadziałało. Nie jestem pewien, czy będzie to potrzebne wszystkim, ale to zdecydowanie mi pomogło. Możesz zdecydować się na usunięcie zmiany, jeśli uważasz, że jest ona niepotrzebna. Dzięki za dotychczasową pomoc.
Chris Gong,
23

Miałem ten sam problem i Google mnie tu zaprowadziło. Zgadzam się z @Ata, że ​​nie można po prostu usunąć end(). Po dalszych Googlach myślę, że używanie poolingjest lepszym sposobem.

node-mysql doc o buforowaniu

To jest tak:

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

pool.getConnection(function(err, connection) {
    connection.query( 'bla bla', function(err, rows) {
        connection.release();
    });
});
hbrls
źródło
7

Nie łącz () i end () wewnątrz funkcji. Spowoduje to problemy z powtarzającymi się wywołaniami funkcji. Wykonaj tylko połączenie

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

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

});

raz i ponownie użyj tego połączenia.

Wewnątrz funkcji

function insertData(name,id) {

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


}
ajin
źródło
5

Funkcje AWS Lambda

Użyj mysql.createPool () z connection.destroy ()

W ten sposób nowe wywołania używają ustalonej puli, ale nie utrzymują działania funkcji. Mimo że nie uzyskujesz pełnej korzyści z puli (każde nowe połączenie używa nowego połączenia zamiast istniejącego), sprawia to, że drugie wywołanie może ustanowić nowe połączenie bez konieczności wcześniejszego zamykania poprzedniego.

Jeżeli chodzi o connection.end()

Może to spowodować, że kolejne wywołanie zgłosi błąd. Wywołanie będzie nadal ponawiane później i będzie działać, ale z opóźnieniem.

Odnośnie mysql.createPool()doconnection.release()

Funkcja Lambda będzie działać do zaplanowanego limitu czasu, ponieważ nadal istnieje otwarte połączenie.

Przykład kodu

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();
    });
  });
};
James Nuanez
źródło
Mam skrypt NodeJS działający jako funkcja AWS Lambda. Pinguje interfejs API platformy Azure, który zwraca 100 rekordów naraz, z „następnym” adresem URL w celu pobrania następnych 100, aż do końca zbioru danych. Więc moja funkcja INSERT jest wywoływana kilka razy. Pojawiał się błąd „Nie można wpisać do kolejki Handshake po wywołaniu quit”, dopóki nie usunąłem linii connection.connect () i connection.end (). Czy zamiast tego lepiej jest używać puli? Nie jestem pewien, kiedy wywołałbym „connection.end ()”, gdy z API zostanie zwrócony ostateczny zestaw danych ...
Shafique,
2

w miejscu connection.connect();użytkowania -

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

jeśli jest już wywołana connection._connectCalled =true,
& nie zostanie wykonana connection.connect();

uwaga - nie używajconnection.end();

Kundan Thakur
źródło
1

Myślę, że ten problem jest podobny do mojego:

  1. Połącz się z MySQL
  2. Zakończ usługę MySQL (nie należy zamykać skryptu węzła)
  3. Uruchom usługę MySQL, Node ponownie łączy się z MySQL
  4. Zapytanie do bazy danych -> FAIL (Nie można umieścić zapytania w kolejce po błędzie krytycznym).

Rozwiązałem ten problem, odtwarzając nowe połączenie za pomocą obietnic (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);
    });
};

Mam nadzieję, że to pomoże.

tsuz
źródło
0

ROZWIĄZANIE: aby zapobiec temu błędowi (dla AWS LAMBDA):

Aby wyjść z „pętli zdarzeń Nodejs”, musisz zakończyć połączenie, a następnie połączyć się ponownie. Dodaj następny kod, aby wywołać wywołanie zwrotne:

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,

        });
    });
Jorge Valvert
źródło
Czy każde wywołanie funkcji lambda nie spowoduje wtedy otwarcia połączenia mysql? To tylko kolejny sposób na udzielenie akceptowanej odpowiedzi, która nie jest dobrym pomysłem
Brian McCall
1
Nie. Kiedy wywołujesz connection.end, pętla kończy się, ale połączenie z bazą danych pozostaje w stanie „kończenie połączenia”. Przy próbie otwarcia nowego połączenia zawsze pojawia się błąd „Nie można umieścić w kolejce Handshake po wywołaniu quit”, więc dodatkowe createConnection w rzeczywistości powoduje błąd niepowodzenia, a następne połączenie nie powiodło się. To tylko sposób na pozbycie się tego problemu, w rzeczywistości moduł mysql musi wykonać czystsze zakończenie połączenia.
Jorge Valvert
0

Jeśli próbujesz uzyskać lambdę, okazało się, że kończenie programu obsługującego context.done()powoduje, że lambda kończy się. Przed dodaniem tej 1 linii po prostu działałby i działał, aż upłynął limit czasu.

JonTroncoso
źródło
Przykład kodu na to proszę?
Shafique,
0

Możesz użyć debug: false,

Przykład: // połączenie mysql

var dbcon1 = mysql.createConnection({
      host: "localhost",
      user: "root",
      password: "",
      database: "node5",
      debug: false,
    });
Jay Bharat
źródło