You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Node.js WhatsApp机器人MySQL连接超时报错问题咨询

Node.js WhatsApp机器人MySQL连接超时报错问题咨询

你好,我来帮你分析这个问题并给出针对性的解决方案:

你的问题场景与报错信息

你用Node.js开发了一个连接MySQL的WhatsApp机器人,每次收到消息都会执行数据库操作。当前的实现是每次触发操作时创建新的数据库连接对象,操作完成后调用this.conn.end()关闭连接。但运行几个小时后会出现以下报错:

Warning: got packets out of order. Expected 21 but received 0

node:events:498
throw er; // Unhandled 'error' event
^

Error: The client was disconnected by the server because of inactivity. See wait_timeout and interactive_timeout for configuring this behavior.

at Connection.protocolError (C:\Users\Woton\Desktop\zeplin_bot\node_modules\mysql2\lib\connection.js:421:17)
at Connection.handlePacket (C:\Users\Woton\Desktop\zeplin_bot\node_modules\mysql2\lib\connection.js:476:14)
at PacketParser.onPacket (C:\Users\Woton\Desktop\zeplin_bot\node_modules\mysql2\lib\connection.js:94:12)
at PacketParser.executeStart (C:\Users\Woton\Desktop\zeplin_bot\node_modules\mysql2\lib\packet_parser.js:75:16)
at Socket.<anonymous> (C:\Users\Woton\Desktop\zeplin_bot\node_modules\mysql2\lib\connection.js:101:25)
at Socket.emit (node:events:520:28)
at addChunk (node:internal/streams/readable:315:12)
at readableAddChunk (node:internal/streams/readable:289:9)
at Socket.Readable.push (node:internal/streams/readable:228:10)
at TCP.onStreamRead (node:internal/stream_base_commons:190:23)

Emitted 'error' event on Connection instance at:
at Connection.protocolError (C:\Users\Woton\Desktop\zeplin_bot\node_modules\mysql2\lib\connection.js:424:10)
at Connection.handlePacket (C:\Users\Woton\Desktop\zeplin_bot\node_modules\mysql2\lib\connection.js:476:14)
[... lines matching original stack trace ...]
at TCP.onStreamRead (node:internal/stream_base_commons:190:23) {
fatal: true,
code: 4031

你考虑过调大wait_timeoutinteractive_timeout,但担心这只是延缓问题,这个判断是完全正确的——这两个参数只是延长MySQL服务器断开空闲连接的时间,不能从根本上解决连接失效的问题。

问题根源

当前每次操作创建、用完就关闭连接的方式,看似合理,但如果机器人长时间没有收到消息(也就是没有数据库操作),如果此时连接还没被关闭(或者某些场景下连接没有正确关闭),就会因为空闲时间超过MySQL的超时设置被服务器主动断开。当新消息触发操作时,使用这个已经失效的连接就会抛出报错。

解决方案

推荐采用数据库连接池的方式来管理连接,这是Node.js操作MySQL的最佳实践,既能解决连接超时问题,又能提升性能:

1. 使用连接池代替单次连接

连接池会自动管理连接的复用、空闲回收和失效重连,无需手动创建/关闭单个连接:

// 只需要在项目初始化时创建一次连接池
const mysql = require('mysql2');
const pool = mysql.createPool({
  database: 'database',
  host: "host",
  user: "user",
  password: 'pass',
  connectionLimit: 10, // 连接池最大连接数,根据你的业务调整
  waitForConnections: true,
  queueLimit: 0
});

// 每次需要操作数据库时,从连接池获取连接
function handleMessageDatabaseOperation() {
  pool.getConnection((err, conn) => {
    if (err) {
      console.error('获取连接失败:', err);
      return;
    }

    // 执行你的数据库操作,比如查询或插入
    conn.query('INSERT INTO messages (content) VALUES (?)', ['用户消息内容'], (queryErr, results) => {
      // 操作完成后,将连接释放回连接池(不是关闭)
      conn.release();

      if (queryErr) {
        console.error('数据库操作失败:', queryErr);
        return;
      }

      console.log('操作成功:', results);
    });
  });
}

2. 若坚持使用单次连接(不推荐)

如果你不想改用连接池,可以在每次操作前检查连接的有效性,失效则重新创建:

const mysql = require('mysql2');
let conn;

function getValidConnection() {
  return new Promise((resolve, reject) => {
    // 检查连接是否存在且处于连接状态
    if (conn && conn.state === 'connected') {
      // 发送ping命令测试连接有效性
      conn.ping((pingErr) => {
        if (pingErr) {
          // ping失败,关闭旧连接并重新创建
          conn.end();
          createNewConnection(resolve, reject);
        } else {
          // 连接有效,直接返回
          resolve(conn);
        }
      });
    } else {
      // 连接不存在或已关闭,创建新连接
      createNewConnection(resolve, reject);
    }
  });
}

function createNewConnection(resolve, reject) {
  conn = mysql.createConnection({
    database: 'database',
    host: "host",
    user: "user",
    password: 'pass'
  });

  conn.connect((connectErr) => {
    if (connectErr) {
      reject(connectErr);
    } else {
      resolve(conn);
    }
  });
}

// 使用示例
async function handleMessage() {
  try {
    const validConn = await getValidConnection();
    // 执行数据库操作
    validConn.query('SELECT * FROM users', (err, results) => {
      // 操作完成后关闭连接
      validConn.end();
      if (err) throw err;
      console.log('查询结果:', results);
    });
  } catch (err) {
    console.error('处理失败:', err);
  }
}

总结

连接池是最稳妥的解决方案,不仅能彻底解决连接超时问题,还能避免频繁创建销毁连接带来的性能损耗。调大超时参数只是权宜之计,无法从根本上解决问题。

备注:内容来源于stack exchange,提问作者Woton Sampaio

火山引擎 最新活动