Не вдається поставити в рукостискання чергу після виходу із системи


83

Я реалізував такий код:

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

Все чудово працює лише вперше. Якщо я хочу "використати" запит вдруге, я отримую таку помилку:

Cannot enqueue Handshake after invoking quit

Я намагався не .end()підключати, але це не допомагало.

Як я можу вирішити цю проблему?


2
Не могли б ви хоча б закрити питання?
Ендрю Райн,

Для мене помилка виникла, коли я намагаюся відкрити з'єднання, коли одне відкрите (тобто, маючи два дзвінки connection.connect () поруч один з одним)
sqram

виклик connection.endусередині connection.queryфункції зворотного виклику, оскільки вона буде виконуватися асинхронно.
Арджун Сінгх

Відповіді:


244

Якщо ви використовуєте модуль node-mysql, просто видаліть .connect та .end. Просто вирішив проблему сам. Очевидно, вони всунули непотрібний код під час останньої ітерації, яка також була помилкою. Вам не потрібно підключатися, якщо ви вже запустили виклик createConnection


22
Це найгірша відповідь, яку я знайшов! коли ви створюєте з'єднання, і воно відкривається, тепер, подумайте, ми створюємо вузол mysql у функції, кожен раз, коли функція викликається, вона створює та утримує відкрите з'єднання, а через короткий час ви отримуєте Max Limit Reach для з'єднання mysql
Ata

5
Тоді ви робите це неправильно. Ви повинні повторно використовувати зв’язок
Andrew Rhyne

6
@ata node-mysql реалізує пул з'єднань. Ви не повинні знищувати об'єкт з'єднання з кожним запитом, оскільки це не власне запит. Навіть не впевнений, чому ваш коментар отримує позитивні голоси. Очевидно, що люди не читають документи
Ендрю Райн,

5
Якщо ви працюєте в aws lambda, лямбда буде тримати час очікування, якщо ви не розірвете з'єднання. тому всі ці пропозиції не допомагають. Я витрачаю цілі дні на це питання.
Джозеф Болейд Какстон-Ідоу

Дякую, що працює для мене. Відповідь нижче @ XP1 містить докладнішу інформацію про цю поведінку.
KeitelDOG

55

Відповідно до:

TL; DR Вам потрібно встановити нове з'єднання, викликаючи createConnectionметод після кожного відключення.

і

Примітка. Якщо ви обслуговуєте веб-запити, то не слід розривати зв’язки на кожному запиті. Просто створіть підключення під час запуску сервера та використовуйте об’єкт підключення / клієнт для постійного запиту. Ви можете прослухати подію помилки для обробки відключення сервера та для цілей повторного підключення. Повний код тут .


Від:

Він говорить:

Сервер відключається

Ви можете втратити зв’язок із сервером MySQL через проблеми з мережею, тайм-аут сервера або збій сервера. Усі ці події вважаються фатальними помилками і матимуть err.code = 'PROTOCOL_CONNECTION_LOST'. Для отримання додаткової інформації див. Розділ Обробка помилок.

Найкращий спосіб усунення таких несподіваних відключень показаний нижче:

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

Як ви можете бачити у прикладі вище, повторне підключення з'єднання здійснюється шляхом встановлення нового з'єднання. Після припинення існуючий об’єкт підключення не може бути повторно підключений за проектом.

За допомогою Pool відключені підключення буде видалено з пулу, звільняючи місце для нового підключення, яке буде створено під час наступного виклику getConnection.


Я налаштував функцію таким чином, що кожного разу, коли потрібне з'єднання, функція ініціалізації додає обробники автоматично:

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

Ініціалізація з'єднання:

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

Незначна пропозиція: Це може стосуватися не всіх, але я натрапив на незначну проблему, що стосується сфери застосування. Якщо ОП вважає, що це редагування було непотрібним, тоді він / вона може вибрати його видалити. Для мене мені довелося змінити рядок initializeConnection, який мав var connection = mysql.createConnection(config);бути просто справедливим

connection = mysql.createConnection(config);

Причиною було те, що якщо connectionу вашій програмі є глобальна змінна, то раніше проблема полягала в тому, що ви створювали нову connectionзмінну під час обробки сигналу про помилку. Але в моєму коді nodejs я продовжував використовувати ту саму глобальну connectionзмінну для запуску запитів, тому нове connectionбуде втрачено в локальній області застосування initalizeConnectionметоду. Але в модифікації він гарантує connectionскидання глобальної змінної. Це може бути доречно, якщо у вас виникає проблема, відома як

Не вдається поставити запит у чергу після фатальної помилки

після спроби виконати запит після втрати з’єднання та успішного повторного підключення. Можливо, це була помилка ОП, але я просто хотів пояснити.


1
Чудовий код, але мій сценарій, здається, все одно виходить (код 8) через 90 секунд, здавалося б, навіть не ввівши процедуру addDisconectHandler. Ідеї?
emc

Блискуча відповідь, я пішов на пул після рефактора, але це чудовий варіант.
Погріндіс

Гарна відповідь спасибі. Це має бути частиною офіційного документа (і оброблятиметься node-mysql, а не розробником).
Skoua

1
Це фантастична відповідь, але у мене є пропозиція / налаштування, які мені потрібно було зробити, щоб це працювало для мене. Я не впевнений, чи це буде потрібно для всіх, але це мені точно допомогло. Ви можете видалити редагування, якщо вважаєте, що воно непотрібне, дякую за вашу допомогу.
Кріс Гонг,

23

У мене була та сама проблема, і Google привів мене сюди. Я погоджуюся з @Ata, що не правильно просто видаляти end(). Після подальшого гуглиння, я думаю, використання poolingє кращим способом.

node-mysql doc про об'єднання

Це приблизно так:

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

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

7

Не підключайте () та кінець () всередині функції. Це спричинить проблеми при повторних викликах функції. Встановіть лише підключення

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

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

});

один раз і повторно використати це з’єднання.

Усередині функції

function insertData(name,id) {

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


}

5

Функції AWS Lambda

Використовуйте mysql.createPool () з connection.destroy ()

Таким чином, нові виклики використовують встановлений пул, але не продовжують функціонувати. Незважаючи на те, що ви не отримуєте всіх переваг об'єднання (кожне нове з'єднання використовує нове з'єднання замість існуючого), воно робить це таким чином, що другий виклик може встановити нове з'єднання без попереднього закриття попереднього.

Щодо connection.end()

Це може призвести до подальшого виклику помилки. Виклик все одно повториться пізніше і спрацює, але із затримкою.

Щодо mysql.createPool()сconnection.release()

Функція лямбда продовжуватиме працювати до запланованого часу очікування, оскільки все ще є відкрите з'єднання.

Приклад коду

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

У мене є сценарій NodeJS, який працює як функція AWS Lambda. Він перевіряє API Azure, який одночасно повертає 100 записів із "наступною" URL-адресою для отримання наступних 100, до кінця набору даних. Тож мою функцію INSERT викликають кілька разів. Я отримував помилку "Не вдається поставити в чергу рукостискання після виклику виходу", поки не видалив рядки connection.connect () та connection.end (). Чи є сенс використовувати тут замість басейну? Я не впевнений, коли би зателефонував "connection.end ()", коли остаточний набір даних повертається з API ...
Шафік,

2

замість connection.connect();використання -

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

якщо його вже викликано connection._connectCalled =true,
& він не буде виконуватися connection.connect();

примітка - не використовуватиconnection.end();


1

Я думаю, що це питання схоже на моє:

  1. Підключіться до MySQL
  2. Завершення служби MySQL (не повинен виходити зі сценарію вузла)
  3. Запустіть службу MySQL, Вузол знову підключається до MySQL
  4. Надіслати запит до БД -> FAIL (Не вдається надіслати запит в чергу після фатальної помилки.)

Я вирішив це питання, відтворивши новий зв’язок із використанням обіцянок (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);
    });
};

Сподіваюся, це допоможе.


0

РІШЕННЯ: щоб запобігти цій помилці (для AWS LAMBDA):

Для того, щоб вийти з "циклу подій Nodejs", потрібно розірвати з'єднання, а потім знову підключитися. Додайте наступний код, щоб викликати зворотний виклик:

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,

        });
    });

Чи кожне виклик лямбда-функції не призведе до відкритого підключення mysql? Це лише ще один спосіб прийняти прийняту відповідь, що не є гарною ідеєю
Брайан Макколл,

1
Ні. Коли ви викликаєте connection.end, цикл закінчується, але підключення до бази даних залишається в стані "вихід із з'єднання". При спробі відкрити нове підключення ви завжди отримуватимете повідомлення про помилку "Не вдається поставити в чергу рукостискання після виклику виходу", отже, додаткове createConnection фактично отримує цю помилку про збій, і наступне з'єднання не провалюється. Це лише спосіб розв’язати цю проблему, насправді модулю mysql потрібно виконати більш чітке закінчення підключення.
Хорхе Вальверт

0

Якщо ви намагаєтесь отримати лямбду, я виявив, що закінчення обробника context.done()отримало ламбду для закінчення. Перш ніж додати цей 1 рядок, він просто запускався і працював, поки не закінчився час очікування.


Приклад коду для цього, будь ласка?
Шафіке

0

Ви можете використовувати налагодження: false,

Приклад: // підключення mysql

var dbcon1 = mysql.createConnection({
      host: "localhost",
      user: "root",
      password: "",
      database: "node5",
      debug: false,
    });
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.