Không thể xếp hàng Bắt tay sau khi gọi thoát


81

Tôi đã triển khai mã sau:

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

Mọi thứ chỉ hoạt động tốt trong lần đầu tiên. Nếu tôi muốn "sử dụng" truy vấn lần thứ hai, tôi gặp lỗi sau:

Cannot enqueue Handshake after invoking quit

Tôi đã cố gắng không .end()kết nối nhưng nó không giúp được gì.

Làm cách nào để khắc phục sự cố này?


2
Bạn có thể ít nhất đóng câu hỏi được không?
Andrew Rhyne

Đối với tôi, lỗi đã xảy ra khi tôi cố gắng mở một kết nối trong khi một kết nối đang mở (tức là có hai cuộc gọi connection.connect () bên cạnh nhau)
sqram

gọi connection.endbên trong connection.queryhàm gọi lại vì nó sẽ thực thi không đồng bộ.
Arjun Singh

Câu trả lời:


240

Nếu bạn sử dụng mô-đun node-mysql, chỉ cần xóa .connect và .end. Chỉ tự mình giải quyết vấn đề. Rõ ràng là họ đã đẩy mã không cần thiết trong lần lặp lại cuối cùng của họ cũng bị lỗi. Bạn không cần kết nối nếu bạn đã chạy cuộc gọi createConnection


21
Đó là câu trả lời tồi tệ nhất mà tôi đã tìm thấy! khi bạn tạo một kết nối và nó mở ra, bây giờ, hãy nghĩ rằng chúng ta tạo một nút mysql trong một hàm, mỗi khi hàm được gọi, nó sẽ tạo và giữ kết nối đang mở, sau một thời gian ngắn, bạn sẽ nhận được Max Limit Reach của kết nối mysql
Ata

4
Vậy thì bạn đang làm sai. Bạn phải sử dụng lại kết nối
Andrew Rhyne

6
@ata node-mysql triển khai một nhóm kết nối. Bạn không được phép hủy đối tượng kết nối với mọi yêu cầu vì nó không phải là yêu cầu thực tế. Thậm chí không chắc chắn tại sao bình luận của bạn nhận được sự tán thành. Rõ ràng mọi người không đọc các tài liệu
Andrew Rhyne

5
Nếu bạn đang chạy trong aws lambda, lambda sẽ tiếp tục hết thời gian nếu bạn không đóng kết nối. vì vậy tất cả những đề xuất này không giúp ích gì. Tôi đã dành nhiều ngày cho vấn đề này.
Joseph Bolade Caxton-Idowu

Cảm ơn nó làm việc cho tôi. Câu trả lời dưới đây của @ XP1 cung cấp thêm chi tiết về hành vi này.
KeitelDOG

54

Dựa theo:

TL; DR Bạn cần thiết lập một kết nối mới bằng cách gọi createConnectionphương thức sau mỗi lần ngắt kết nối.

Lưu ý: Nếu bạn đang cung cấp các yêu cầu web, thì bạn không nên kết thúc kết nối theo mọi yêu cầu. Chỉ cần tạo kết nối khi khởi động máy chủ và sử dụng đối tượng kết nối / máy khách để truy vấn mọi lúc. Bạn có thể lắng nghe sự kiện lỗi để xử lý ngắt kết nối máy chủ và cho các mục đích kết nối lại. Toàn mã ở đây .


Từ:

Nó nói rằng:

Máy chủ ngắt kết nối

Bạn có thể mất kết nối với máy chủ MySQL do sự cố mạng, máy chủ hết thời gian chờ hoặc máy chủ gặp sự cố. Tất cả các sự kiện này được coi là lỗi nghiêm trọng và sẽ có err.code = 'PROTOCOL_CONNECTION_LOST'. Xem phần Xử lý lỗi để biết thêm thông tin.

Cách tốt nhất để xử lý các ngắt kết nối không mong muốn như vậy được hiển thị bên dưới:

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

Như bạn có thể thấy trong ví dụ trên, việc kết nối lại một kết nối được thực hiện bằng cách thiết lập một kết nối mới. Sau khi kết thúc, đối tượng kết nối hiện có không thể được kết nối lại theo thiết kế.

Với Pool, các kết nối đã ngắt kết nối sẽ bị xóa khỏi pool, giải phóng dung lượng cho một kết nối mới sẽ được tạo trong lần gọi getConnection tiếp theo.


Tôi đã điều chỉnh chức năng để mỗi khi cần kết nối, chức năng khởi tạo sẽ tự động thêm các trình xử lý:

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

Khởi tạo kết nối:

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

Gợi ý nhỏ: Điều này có thể không áp dụng cho tất cả mọi người nhưng tôi đã gặp phải một vấn đề nhỏ liên quan đến phạm vi. Nếu OP cảm thấy chỉnh sửa này là không cần thiết thì họ có thể chọn loại bỏ nó. Đối với tôi, tôi phải thay đổi một dòng initializeConnection, điều này var connection = mysql.createConnection(config);chỉ đơn giản là

connection = mysql.createConnection(config);

Lý do là if connectionlà một biến toàn cục trong chương trình của bạn, thì vấn đề trước đây là bạn đang tạo một connectionbiến mới khi xử lý một tín hiệu lỗi. Nhưng trong mã nodejs của tôi, tôi tiếp tục sử dụng cùng một connectionbiến toàn cục để chạy các truy vấn, vì vậy biến mới connectionsẽ bị mất trong phạm vi cục bộ của initalizeConnectionphương thức. Nhưng trong sửa đổi, nó đảm bảo rằng connectionbiến toàn cục được đặt lại. Điều này có thể liên quan nếu bạn đang gặp sự cố được gọi là

Không thể xếp hàng truy vấn sau lỗi nghiêm trọng

sau khi cố gắng thực hiện truy vấn sau khi mất kết nối và sau đó kết nối lại thành công. Đây có thể là lỗi đánh máy của OP, nhưng tôi chỉ muốn làm rõ.


1
Mã tuyệt vời, nhưng tập lệnh của tôi dường như vẫn thoát (mã 8) sau 90 giây dường như thậm chí không cần nhập quy trình addDisconectHandler. Ý tưởng?
emc

Câu trả lời tuyệt vời, tôi đã tìm cách gộp sau khi tái cấu trúc, nhưng đây là một lựa chọn tuyệt vời.
Pogrindis

Câu trả lời tốt, cảm ơn. Đây phải là một phần của tài liệu chính thức (và được xử lý bởi node-mysql, không phải nhà phát triển).
Skoua

1
Đây là một câu trả lời tuyệt vời, nhưng tôi thực sự có một đề xuất / điều chỉnh mà tôi cần thực hiện để điều này có hiệu quả với tôi. Tôi không chắc liệu điều này có cần thiết cho mọi người hay không, nhưng điều này chắc chắn đã giúp tôi. Bạn có thể chọn xóa bản chỉnh sửa nếu cảm thấy không cần thiết, cảm ơn sự giúp đỡ của bạn cho đến nay.
Chris Gong

23

Tôi đã gặp vấn đề tương tự và Google đã dẫn tôi đến đây. Tôi đồng ý với @Ata rằng chỉ cần xóa là không đúng end(). Sau khi tiếp tục Google, tôi nghĩ sử dụng poolinglà một cách tốt hơn.

tài liệu node-mysql về gộp

Nó như thế này:

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

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

7

Không kết nối () và end () bên trong hàm. Điều này sẽ gây ra sự cố khi gọi hàm lặp đi lặp lại. Chỉ tạo kết nối

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

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

});

một lần và sử dụng lại kết nối đó.

Bên trong hàm

function insertData(name,id) {

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


}

5

Các hàm AWS Lambda

Sử dụng mysql.createPool () với connection.destroy ()

Bằng cách này, các lời gọi mới sử dụng nhóm đã thiết lập, nhưng không giữ cho hàm hoạt động. Mặc dù bạn không nhận được đầy đủ lợi ích của việc gộp chung (mỗi kết nối mới sử dụng một kết nối mới thay vì một kết nối hiện có), nó khiến cho lời gọi thứ hai có thể thiết lập một kết nối mới mà không phải đóng kết nối trước đó.

Về connection.end()

Điều này có thể khiến một lời gọi tiếp theo gây ra lỗi. Lời gọi sẽ vẫn thử lại sau đó và hoạt động, nhưng có độ trễ.

Về mysql.createPool()vớiconnection.release()

Hàm Lambda sẽ tiếp tục chạy cho đến khi hết thời gian đã lên lịch, vì vẫn còn một kết nối đang mở.

Mã ví dụ

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

Tôi có một tập lệnh NodeJS đang chạy dưới dạng một hàm AWS Lambda. Nó ping một API Azure trả về 100 bản ghi cùng một lúc, với một URL "tiếp theo" để truy xuất 100 bản tiếp theo, cho đến khi kết thúc tập dữ liệu. Vì vậy, hàm INSERT của tôi được gọi nhiều lần. Tôi gặp lỗi "Không thể xếp hàng Bắt tay sau khi gọi thoát", cho đến khi tôi xóa các dòng connect.connect () và connect.end (). Thay vào đó, sử dụng hồ bơi ở đây có hợp lý hơn không? Tôi không chắc khi nào mình sẽ gọi "connect.end ()" khi tập dữ liệu cuối cùng được trả lại từ API ...
Shafique

2

nơi connection.connect();sử dụng -

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

nếu nó đã được gọi là sau đó connection._connectCalled =true,
và nó sẽ không thực hiện connection.connect();

lưu ý - không sử dụngconnection.end();


1

Tôi nghĩ vấn đề này cũng tương tự như của tôi:

  1. Kết nối với MySQL
  2. Kết thúc dịch vụ MySQL (không nên thoát khỏi tập lệnh nút)
  3. Khởi động dịch vụ MySQL, Node kết nối lại với MySQL
  4. Truy vấn DB -> FAIL (Không thể xếp hàng trước Truy vấn sau lỗi nghiêm trọng.)

Tôi đã giải quyết vấn đề này bằng cách tạo lại một kết nối mới với việc sử dụng các lời hứa (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);
    });
};

Tôi hi vọng cái này giúp được.


0

GIẢI PHÁP: để ngăn lỗi này (đối với AWS LAMBDA):

Để thoát khỏi "Vòng lặp sự kiện Nodejs", bạn phải kết thúc kết nối, sau đó kết nối lại. Thêm mã tiếp theo để gọi lại:

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,

        });
    });

Mọi lệnh gọi của hàm lambda sau đó sẽ dẫn đến một kết nối mysql mở? Đây chỉ là một cách khác để thực hiện câu trả lời được chấp nhận không phải là một ý tưởng hay
Brian McCall

1
Không. Khi bạn gọi connect.end, vòng lặp kết thúc, nhưng kết nối với cơ sở dữ liệu vẫn ở trạng thái "thoát kết nối". Khi bạn cố gắng mở một kết nối mới, bạn sẽ luôn gặp lỗi "Cannot enqueue Handshake sau khi gọi thoát", do đó, trên thực tế, createConnection bổ sung, gặp lỗi không thành công này và kết nối tiếp theo không bị lỗi. Đây chỉ là một cách để giải quyết vấn đề này, trên thực tế, mô-đun mysql cần thực hiện một kết thúc kết nối sạch hơn.
Jorge Valvert

0

Nếu bạn đang cố gắng lấy lambda, tôi thấy rằng kết thúc trình xử lý bằng context.done()thì lambda sẽ kết thúc. Trước khi thêm 1 dòng đó, Nó sẽ chạy và chạy cho đến khi hết thời gian.


Mã ví dụ cho điều này xin vui lòng?
Shafique

0

Bạn có thể sử dụng debug: false,

Ví dụ: // kết nối mysql

var dbcon1 = mysql.createConnection({
      host: "localhost",
      user: "root",
      password: "",
      database: "node5",
      debug: false,
    });
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.