You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

处理MariaDB Node.js连接器的‘Socket意外关闭’问题

解决方案

1. 给连接池添加全局错误捕获,避免进程崩溃

群晖备份会强制断开MariaDB连接,未处理的致命错误会导致进程崩溃。我们可以给连接池绑定error事件监听,捕获特定错误后重建连接池:

const mariaDB = require('mariadb');
let pool = mariaDB.createPool({
    host: host,
    port: port,
    user: user,
    password: password,
    connectionLimit: 5,
    database: database,
    // 配置连接池稳定性参数
    idleTimeout: 30000,       // 空闲连接超时销毁,避免持有无效连接
    connectionTimeout: 5000,  // 获取连接超时时间
    retryDelay: 2000,         // 连接失败重连间隔
    maxRetries: 3             // 最大重连次数
});

// 监听连接池全局错误
pool.on('error', (err) => {
    console.error('连接池致命错误:', err);
    // 匹配Socket意外关闭的错误标识
    if (err.code === 'ER_SOCKET_UNEXPECTED_CLOSE' || err.sqlState === '08S01') {
        // 销毁旧连接池并重建
        pool.end().then(() => {
            console.log('开始重建数据库连接池...');
            pool = mariaDB.createPool({
                host: host,
                port: port,
                user: user,
                password: password,
                connectionLimit: 5,
                database: database,
                idleTimeout: 30000,
                connectionTimeout: 5000,
                retryDelay: 2000,
                maxRetries: 3
            });
            // 给新连接池绑定相同的错误监听
            pool.on('error', arguments.callee);
        }).catch(endErr => {
            console.error('销毁旧连接池失败:', endErr);
        });
    }
});

module.exports = {
    getPool: () => pool
};

2. 优化业务代码的连接使用逻辑

你的业务代码存在两个问题:一是用conn.end()销毁连接而非归还到池;二是错误捕获后未处理连接类致命错误。修改后的代码如下:

const { getPool } = require('../models/databaseConnection');

async function submitNewQuote(quote) {
    let conn;
    try {
        const pool = getPool();
        conn = await pool.getConnection();
        const sqlString = 'INSERT INTO quotes (content) VALUES (?)';
        const valArray = [quote.content];
        const results = await conn.query(sqlString, valArray);
        return results.insertId;
    } catch (e) {
        console.error('提交报价失败:', e);
        // 针对连接断开错误,短暂等待后重试一次
        if (e.code === 'ER_SOCKET_UNEXPECTED_CLOSE' || e.sqlState === '08S01') {
            await new Promise(resolve => setTimeout(resolve, 2000));
            return submitNewQuote(quote);
        }
        // 其他错误抛出,交由上层处理
        throw e;
    } finally {
        if (conn) {
            // 使用release归还连接到池,而非end销毁
            conn.release();
        }
    }
}

3. 关键配置说明

  • idleTimeout: 自动销毁超时的空闲连接,避免连接池持有无效连接
  • connectionTimeout: 限制获取连接的等待时间,防止请求无限挂起
  • retryDelay/maxRetries: 连接失败时自动重连,提升连接池的自愈能力
  • 全局错误监听:捕获致命断开错误后主动重建连接池,避免进程崩溃

原理说明

群晖备份时会临时停止MariaDB服务,导致连接池内所有连接被强制断开,触发ER_SOCKET_UNEXPECTED_CLOSE错误。通过全局监听捕获该错误后,销毁旧连接池并重建,同时在业务层添加重试逻辑,确保备份期间的请求能在连接恢复后正常执行。

内容的提问来源于stack exchange,提问作者long-jumping-bag

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 13:45:31