处理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
相关产品推荐
相关产品推荐

