Node.js MySQL错误:连接丢失 服务器关闭了连接。

119

当我使用node mysql时,在12:00至2:00之间会出现一个错误,即TCP连接被服务器关闭。以下是完整的错误信息:

Error: Connection lost: The server closed the connection.
at Protocol.end (/opt/node-v0.10.20-linux-x64/IM/node_modules/mysql/lib/protocol/Protocol.js:73:13)
at Socket.onend (stream.js:79:10)
at Socket.EventEmitter.emit (events.js:117:20)
at _stream_readable.js:920:16
at process._tickCallback (node.js:415:13)

这里有一个解决方案。然而,我按照这种方式尝试后,问题仍然存在。现在我不知道该怎么办了。是否有人遇到过这个问题?

以下是我按照这个解决方案编写的方式:

    var handleKFDisconnect = function() {
    kfdb.on('error', function(err) {
        if (!err.fatal) {
            return;
        }
        if (err.code !== 'PROTOCOL_CONNECTION_LOST') {
            console.log("PROTOCOL_CONNECTION_LOST");
            throw err;
        }
        log.error("The database is error:" + err.stack);

        kfdb = mysql.createConnection(kf_config);

        console.log("kfid");

        console.log(kfdb);
        handleKFDisconnect();
    });
   };
   handleKFDisconnect();

请注意,您的代码中有错误的消息:if (err.code !== 'PROTOCOL_CONNECTION_LOST') { console.log("PROTOCOL_CONNECTION_LOST"); throw err; }。如果不是PROTOCOL_CONNECTION_LOST,则执行if()块,但消息却说它是该错误...可能非常令人困惑。 - Alexis Wilke
5个回答

198

尝试使用这段代码来处理服务器断开连接的情况:

var db_config = {
  host: 'localhost',
    user: 'root',
    password: '',
    database: 'example'
};

var connection;

function handleDisconnect() {
  connection = mysql.createConnection(db_config); // Recreate the connection, since
                                                  // the old one cannot be reused.

  connection.connect(function(err) {              // The server is either down
    if(err) {                                     // or restarting (takes a while sometimes).
      console.log('error when connecting to db:', err);
      setTimeout(handleDisconnect, 2000); // We introduce a delay before attempting to reconnect,
    }                                     // to avoid a hot loop, and to allow our node script to
  });                                     // process asynchronous requests in the meantime.
                                          // If you're also serving http, display a 503 error.
  connection.on('error', function(err) {
    console.log('db error', err);
    if(err.code === 'PROTOCOL_CONNECTION_LOST') { // Connection to the MySQL server is usually
      handleDisconnect();                         // lost due to either server restart, or a
    } else {                                      // connnection idle timeout (the wait_timeout
      throw err;                                  // server variable configures this)
    }
  });
}

handleDisconnect();

在您的代码中,我缺少connection = mysql.createConnection(db_config);之后的部分。


2
只是一个小提示:我正在通过重启MySQL服务来测试重新连接,以确保一切正常。 - kriskodzi
2
@jackieLin 你可以模拟一下这种情况,在Ubuntu上重启MySQL服务,使用命令sudo service mysql restart。 - igor
2
谢谢 @user3073745,这个问题已经通过重启解决了。 - jackieLin
1
@OkiErieRinaldi,JavaScript中的计时器仅从主循环执行。因此它不是递归的。 - Alexis Wilke
1
完美适用于Node 8.*,npm 5.6.0和mysql:5.7...谢谢!! - JRichardsz
显示剩余11条评论

49

我不记得我最初使用这个机制的原因。现在,我想不出任何有效的用例。

您的客户端应该能够检测到连接丢失并允许您重新创建连接。如果重要的是使用相同的连接执行程序逻辑,则使用事务。

tl;dr; 不要使用这种方法。


一种实用的解决方案是强制MySQL保持连接活动状态:

setInterval(function () {
    db.query('SELECT 1');
}, 5000);

相比连接池和断开处理,我更喜欢这种解决方案,因为它不需要结构化你的代码以便知道连接是否存在。每隔5秒查询一次可以确保连接保持活跃,而且不会出现PROTOCOL_CONNECTION_LOST的情况。

此外,这种方法确保保持同一连接活跃,而不是重新连接。这很重要。想象一下,如果你的脚本依赖于LAST_INSERT_ID(),并且MySQL连接在你没有意识到的情况下被重置了会发生什么?

然而,这只能确保连接超时(wait_timeoutinteractive_timeout)不会发生。在所有其他情况下,它都将失败。因此,请确保处理其他错误。


2
你应该按需连接数据库并断开连接。这个解决方案适用于持续运行并始终利用数据库连接的服务。 - Gajus
1
考虑到您的代码遵循所描述的模式(类似于https://gist.github.com/gajus/5bcd3c7ec5ddcaf53893),这两种情况都不太可能发生。如果这种情况只是在成千上万次查询中偶尔发生一两次,我会认为这是连接问题、服务器超载或类似问题所导致的。 - Gajus
5
那可能是最糟糕的建议了!一个人建议你频繁查询数据库,以防止连接断开?如果有100个人这样做会发生什么?或者为什么不是10,000人呢?如果你的应用程序没有执行任何操作,线程应该返回到MYSQL线程池,而不是占用线程,以避免你脆弱的代码出错!在这种情况下,你应该实现重新连接功能,以防此类事件发生! - Patrik Forsberg
1
Patrik的评论在许多层面上(无意中)都很有趣,你甚至没有意识到。这点让我感到开心。无论如何,Patrik并没有错 - 我彻底不建议除了在本地机器上运行单个进程时使用肮脏的黑客解决方案之外的任何其他情况。即使是这样,这也是一个相当短视的建议,我无法想出一个有效的用例。 - Gajus
4
我已经更新了答案以反映我不建议采用这种方法。感谢提醒,Patrik。 - Gajus
显示剩余4条评论

27
更好的解决方案是使用池 - 它会自动处理这个问题。

const pool = mysql.createPool({
  host: 'localhost',
  user: '--',
  database: '---',
  password: '----'
});

// ... later
pool.query('select 1 + 1', (err, rows) => { /* */ });

https://github.com/sidorares/node-mysql2/issues/836


3
这应该是被认可的答案。使用池机制可以隐藏所有处理连接超时的复杂细节。此外,MySQL节点模块附带的 node_modules/mysql/Readme.md 文件中有关于如何使用它的文档。 - Alexis Wilke
2
@AlexisWilke,很棒,你是否知道 - 相似的思考方式适用于mysql2吗? - Fattie

12

2

在每个查询中创建和销毁连接可能会很复杂,当我决定安装MariaDB而不是MySQL时,我在服务器迁移中遇到了一些问题。由于某种原因,在etc/my.cnf文件中,wait_timeout参数的默认值为10秒(这导致无法实现持久性)。然后,解决方案是将其设置为28800,即8小时。希望我的经验能对有需要的人有所帮助。抱歉我的英语不好。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接