退出后无法排队握手


81

我已经实现了以下代码:

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
您能否至少解决这个问题?
Andrew Rhyne

对我来说,当我尝试在一个连接处于打开状态时打开一个连接(即,彼此相邻有两个connection.connect()调用)时,抛出了错误
sqram

调用connection.end内部的connection.query回调函数,因为它会被异步执行。
Arjun Singh

Answers:


240

如果使用node-mysql模块,则只需删除.connect和.end。自己解决问题。显然,他们在上次迭代中输入了不必要的代码,该代码也遭到了错误处理。如果您已经运行过createConnection调用,则无需连接


21
那是我发现的最糟糕的答案!当您创建一个连接并打开它时,现在,我们以为我们在一个函数中创建了一个mysql节点,每次调用该函数时,它都会创建并保持打开连接,一小段时间后,您将获得mysql连接的最大限制范围
阿塔

4
那你做错了。您应该重用连接
Andrew Rhyne 2014年

6
@ata node-mysql实现一个连接池。您不应该破坏每个请求的连接对象,因为它不是实际的请求。甚至不确定您的评论为什么会被接受。显然,人们没有读文档
Andrew Rhyne

5
如果您在aws lambda中运行,则如果不关闭连接,lambda将保持超时。因此所有这些建议都无济于事。我在这个问题上花了几天的时间。
约瑟夫·博拉德·卡克斯顿·伊多乌

谢谢,对我有用。@ XP1下面的答案提供了有关此行为的更多详细信息。
KeitelDOG

54

根据:

TL; DRcreateConnection每次断开连接后,都需要通过调用该方法来建立新的连接。

注意:如果您正在处理Web请求,则不应终止每个请求的连接。只需在服务器启动时创建连接,然后使用连接/客户端对象一直进行查询。您可以侦听错误事件,以处理服务器断开连接和重新连接的目的。完整代码 在这里


从:

它说:

服务器断开

由于网络问题,服务器超时或服务器崩溃,您可能会失去与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);

如您在上面的示例中看到的,通过建立新连接来重新连接连接。一旦终止,现有的连接对象就无法通过设计重新连接。

使用池,断开的连接将从池中删除,从而释放空间,以便在下一个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"
});

次要建议:这可能并不适用于所有人,但我确实遇到了与范围有关的次要问题。如果OP认为不需要此编辑,则他/她可以选择删除它。对我来说,我必须在中更改一行initializeConnectionvar connection = mysql.createConnection(config);只是

connection = mysql.createConnection(config);

原因是如果connection在程序中是全局变量,那么以前的问题是您connection在处理错误信号时正在创建新变量。但是在我的nodejs代码中,我一直使用相同的全局connection变量来运行查询,因此新方法connection将在方法的局部范围内丢失initalizeConnection。但是在修改中,它确保connection重置了全局变量。如果您遇到称为“

致命错误后无法排队查询

断开连接后尝试执行查询,然后成功重新连接后。这可能是OP的错字,但我只是想澄清一下。


1
很棒的代码,但是我的脚本似乎在90秒后似乎仍然退出(代码8),甚至没有进入addDisconectHandler例程。有想法吗?
emc

出色的答案,我在重构后就去了池,但这是一个很好的选择。
Pogrindis

好答案,谢谢。这应该是官方文档的一部分(并由node-mysql处理,而不是开发人员处理)。
Skoua

1
这是一个很棒的答案,但是我确实有一个建议/调整,需要这个建议才能针对我。我不确定是否每个人都需要这样做,但这确实对我有所帮助。如果您认为不必要,则可以选择删除该编辑,感谢您到目前为止的帮助。
克里斯·龚

23

我遇到了同样的问题,而Google带领我来到了这里。我同意@Ata的观点,只是删除不正确end()。经过进一步的谷歌搜索,我认为使用pooling是更好的方法。

关于池的node-mysql文档

就像这样:

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

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

7

不要在函数内部使用connect()和end()。这将在重复调用该函数时引起问题。仅建立连接

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

Lambda函数将一直运行直到计划的超时,因为仍然有打开的连接。

代码示例

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

我有一个作为AWS Lambda函数运行的NodeJS脚本。它会ping一次一次返回100条记录的Azure API,并带有“下一个” URL来检索下100条记录,直到数据集结束。因此,我的INSERT函数被调用了几次。我收到“调用退出后无法排队握手”错误,直到删除了connection.connect()和connection.end()行。改为在此处使用池是否更有意义?我不确定从API返回最终数据集时何时调用“ connection.end()” ...
Shafique

2

代替connection.connect();使用-

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

如果它已经被调用了connection._connectCalled =true
它将不会执行connection.connect();

注意-请勿使用connection.end();


1

我认为这个问题与我的类似:

  1. 连接到MySQL
  2. 结束MySQL服务(不应退出节点脚本)
  3. 启动MySQL服务,Node重新连接到MySQL
  4. 查询数据库->失败(致命错误后无法排队查询。)

我通过使用promise(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,

        });
    });

不会每次调用lambda函数都导致打开的mysql连接吗?这只是做得到的答案的另一种方法,这不是一个好主意
Brian McCall,

1
不。当您调用connection.end时,循环结束,但是与数据库的连接仍处于“正在退出连接”状态。当您尝试打开新连接时,始终会收到错误消息“调用退出后无法入队握手”,因此,实际上,附加的createConnection会出现此错误错误,并且下一个连接不会失败。这只是解决此问题的一种方法,实际上,mysql模块需要执行更干净的连接结束。
豪尔赫·瓦尔维特

0

如果您尝试获取lambda,我发现以lambda结尾的处理程序context.done()结束了。在添加那一行之前,它会一直运行直到超时。


为此的代码示例吗?
沙菲克

0

您可以使用debug:false,

示例:// mysql连接

var dbcon1 = mysql.createConnection({
      host: "localhost",
      user: "root",
      password: "",
      database: "node5",
      debug: false,
    });
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.