quitを呼び出した後、ハンドシェイクをエンキューできません


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

すべてが初めてうまく機能します。クエリを2回目に「使用」したい場合、次のエラーが発生します。

Cannot enqueue Handshake after invoking quit

私はしないようにしようとした.end()接続が、それは助けにはなりませんでした。

この問題を修正するにはどうすればよいですか?


2
少なくとも質問を閉じていただけますか?
Andrew Rhyne 2013年

私の場合、1つが開いているときに接続を開こうとすると(つまり、2つのconnection.connect()呼び出しが隣り合っている)、エラーがスローされました
sqram 2016

非同期で実行されるため、コールバック関数connection.end内を呼び出しconnection.queryます。
ArjunSingh20年

回答:


244

node-mysqlモジュールを使用している場合は、.connectと.endを削除するだけです。自分で問題を解決しました。どうやら彼らはまたバグがある彼らの最後の反復で不必要なコードを押し込んだ。createConnection呼び出しをすでに実行している場合は、接続する必要はありません。


22
それは私が見つけた最悪の答えです!接続を作成し、それが開くと、関数内にmysqlノードを作成すると思います。関数が呼び出されるたびに、開いている接続が作成されて保持されます。しばらくすると、mysql接続の最大制限リーチが得られます
アタ

5
その後、あなたはそれを間違っています。接続を再利用することになっています
Andrew Rhyne 2014年

6
@ atanode-mysqlは接続プールを実装します。実際のリクエストではないため、リクエストごとに接続オブジェクトを破棄することは想定されていません。あなたのコメントがなぜ賛成を得ているのかさえ分かりません。明らかに人々はドキュメントを読んでいません
Andrew Rhyne 2017

5
aws lambdaで実行している場合、接続を閉じないと、lambdaはタイムアウトを続けます。したがって、これらすべての提案は役に立ちません。私はこの問題に何日も費やしました。
Joseph Bolade Caxton-Idowu 2017

それは私のために働くありがとう。以下の@ 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がこの編集が不要であると感じた場合、OPはそれを削除することを選択できます。私にとっては、私は、内の行を変更しなければならなかったinitializeConnectionあった、var connection = mysql.createConnection(config);ただ単に

connection = mysql.createConnection(config);

その理由connectionは、がプログラム内のグローバル変数である場合、以前の問題はconnection、エラー信号を処理するときに新しい変数を作成していたことでした。しかし、私のnodejsコードでは、connectionクエリを実行するために同じグローバル変数を使用し続けたためconnectioninitalizeConnectionメソッドのローカルスコープで新しいものが失われました。ただし、変更では、グローバルconnection変数が確実にリセットされます。これは、次のような問題が発生している場合に関連する可能性があります。

致命的なエラーの後にクエリをキューに入れることができません

接続が失われた後にクエリを実行しようとした後、正常に再接続した後。これはOPによるタイプミスだったかもしれませんが、明確にしたかっただけです。


1
素晴らしいコードですが、私のスクリプトは、addDisconectHandlerルーチンを入力しなくても、90秒後に終了するようです(コード8)。アイデア?
emc 2014

素晴らしい答えです。リファクタリング後にプーリングに行きましたが、これは優れたオプションです。
pogrindis 2016年

良い答えありがとう。これは公式ドキュメントの一部である必要があります(開発者ではなくnode-mysqlによって処理されます)。
Skoua 2017

1
これは素晴らしい答えですが、これが私のために機能するために必要な提案/微調整があります。これがすべての人に必要かどうかはわかりませんが、これは間違いなく役に立ちました。これまでのご協力に感謝し、不要と思われる場合は編集を削除することもできます。
Chris Gong

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

AWSLambda関数

connection.destroy()mysql.createPool()を使用します

このように、新しい呼び出しは確立されたプールを使用しますが、関数の実行を維持しません。プーリングのメリットを十分に享受することはできませんが(新しい接続ごとに既存の接続ではなく新しい接続を使用します)、2回目の呼び出しで、前の接続を最初に閉じなくても新しい接続を確立できます。

について 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();
    });
  });
};

AWSLambda関数として実行されているNodeJSスクリプトがあります。データセットが終了するまで、一度に100レコードを返すAzure APIにpingを実行し、「次の」URLを使用して次の100レコードを取得します。そのため、私のINSERT関数は数回呼び出されます。connection.connect()行とconnection.end()行を削除するまで、「quitを呼び出した後にハンドシェイクをエンキューできません」というエラーが発生していました。代わりにここでプールを使用する方が理にかなっていますか?最終的なデータセットが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サービスを開始すると、ノードはMySQLに再接続します
  4. DBのクエリ-> FAIL(致命的なエラーの後にクエリをキューに入れることはできません。)

私は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,

        });
    });

ラムダ関数を呼び出すたびに、mysql接続が開かれるのではないでしょうか。これは、受け入れられた答えを行うための別の方法であり、良い考えではありません
Brian McCall

1
いいえ。connection.endを呼び出すと、ループは終了しますが、データベースへの接続は「接続の終了」ステータスのままになります。新しい接続を開こうとすると、常に「quitを呼び出した後にハンドシェイクをエンキューできません」というエラーが発生するため、実際には、追加のcreateConnectionでこの失敗エラーが発生し、次の接続は失敗しませんでした。これは、この問題を回避するための唯一の方法です。実際、mysqlモジュールはよりクリーンな接続終了を実行する必要があります。
ホルヘバルバート2018

0

ラムダを取得しようとしている場合、ハンドラーをで終了するとcontext.done()ラムダが終了することがわかりました。その1行を追加する前は、タイムアウトするまで実行されます。


このためのコード例をお願いしますか?
Shafique

0

デバッグを使用できます:false、

例:// mysql接続

var dbcon1 = mysql.createConnection({
      host: "localhost",
      user: "root",
      password: "",
      database: "node5",
      debug: false,
    });
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.