Node.js 连接 MySQL 报 ECONNRESET 错误?原因及解决
2025-02-26 01:17:01
Node.js 后端连接 MySQL 数据库出现 ECONNRESET 错误
最近做项目,Node.js 后端连 MySQL 数据库,一开始挺好,查询都没问题。可服务器闲置几小时后,第一次查询就报错,报read ECONNRESET
错误,很烦。
错误堆栈像这样:
Error
at exports.Error.utils.createClass.init (D:\home\site\wwwroot\errors.js:180:16)
at new newclass (D:\home\site\wwwroot\utils.js:68:14)
at Query._callback (D:\home\site\wwwroot\db.js:281:21)
at Query.Sequence.end (D:\home\site\wwwroot\node_modules\mysql\lib\protocol\sequences\Sequence.js:78:24)
at Protocol.handleNetworkError (D:\home\site\wwwroot\node_modules\mysql\lib\protocol\Protocol.js:271:14)
at PoolConnection.Connection._handleNetworkError (D:\home\site\wwwroot\node_modules\mysql\lib\Connection.js:269:18)
at Socket.EventEmitter.emit (events.js:95:17)
at net.js:441:14
at process._tickCallback (node.js:415:13)
我在云服务器和本地环境都遇到了这个错误。 琢磨半天,来给大家说说我是怎么解决的。
一、问题原因分析
这问题,我感觉很可能是 Node.js 和 MySQL 数据库之间的连接断了,可能是因为连接有时间限制。
进一步分析:
- 连接超时: MySQL 服务器或者网络设备(比如防火墙、负载均衡器)为了节省资源,可能会主动关闭长时间空闲的连接。
- 网络波动: 不稳定的网络环境也可能导致连接中断。
- 连接池配置问题: 虽然使用了连接池,
node-mysql
模块在处理断开连接时, 可能没处理好,导致旧连接没有及时从池中移除。 - 数据库服务器配置问题:数据库服务器设置了较短的连接超时时间.
二、解决方案
针对上述原因,我整理了下面几个解决方案。
1. 使用连接池并正确配置
node-mysql
提供了连接池功能,连接池能自动管理多个数据库连接,包括创建、复用和释放连接。正确配置连接池,可以很大程度上避免连接超时问题。
原理: 连接池会维护一定数量的数据库连接,当应用需要访问数据库时,从池中获取一个连接,用完后再归还到池中。这样可以避免频繁创建和销毁连接带来的开销。
代码示例:
const mysql = require('mysql');
const pool = mysql.createPool({
connectionLimit: 10, // 连接池最大连接数(根据实际需求调整)
host: 'your_mysql_host',
user: 'your_mysql_user',
password: 'your_mysql_password',
database: 'your_mysql_database',
waitForConnections: true, // 当连接池中没有可用连接时,是否等待连接(true:等待;false:抛出错误)
queueLimit: 0, // 等待队列的长度限制(0:不限制)
acquireTimeout: 10000 // 连接池获取连接超时
});
// 从连接池获取连接
pool.getConnection((err, connection) => {
if (err) {
console.error('获取连接失败:', err);
return;
}
// 执行查询
connection.query('SELECT * FROM your_table', (err, results) => {
// 释放连接
connection.release();
if (err) {
console.error('查询失败:', err);
return;
}
console.log('查询结果:', results);
});
});
安全建议:
- 不要把数据库的用户名、密码等敏感信息直接写在代码里, 最好从环境变量或者配置文件读取。
- 合理设置连接池大小, 太小会影响并发, 太大会增加数据库负担。
进阶技巧:
可以设置idleTimeoutMillis
, 超时会被销毁, 这样旧连接会被清理掉.
const pool = mysql.createPool({
//...其他配置...
idleTimeoutMillis: 30000 // 空闲连接超时时间(毫秒),根据需要调整。
});
2. 心跳检测 (Connection Keep-Alive)
心跳检测就是定期向数据库发送一个简单的查询(比如 SELECT 1
),以保持连接的活跃状态。
原理: 通过定期发送心跳查询,可以防止连接因为空闲时间过长而被关闭。
代码示例:
const mysql = require('mysql');
const pool = mysql.createPool({ /* 连接池配置 */ });
function keepAlive() {
pool.getConnection((err, connection) => {
if (err) {
console.error('心跳检测:获取连接失败', err);
return;
}
connection.query('SELECT 1', (err) => {
connection.release();
if (err) {
console.error('心跳检测:查询失败', err);
return;
}
//什么也不做
});
});
}
// 每隔一段时间执行心跳检测(例如,每 5 分钟)
setInterval(keepAlive, 300000);
额外说明:
心跳间隔需要根据实际情况来设置.如果数据库的wait_timeout
设置的较小, 心跳间隔就应该更频繁一些。
3. 错误重连
即使做了上面的处理, 连接还是有可能断开, 毕竟网络这东西, 谁也说不准。所以,咱们还得加个错误重连机制。
原理: 在捕获到 ECONNRESET
错误后, 尝试重新建立数据库连接.
代码示例:
我们改造一下数据库查询部分代码:
function executeQuery(sql, values, retries = 3) {
return new Promise((resolve, reject) => {
pool.getConnection((err, connection) => {
if (err) {
console.error('获取连接失败', err);
if (retries > 0 )
{
console.log(`尝试重新连接...剩余重试次数:${retries}`);
setTimeout(()=>{
executeQuery(sql,values,retries - 1).then(resolve).catch(reject)
}, 2000); //间隔两秒进行重试
return;
}
reject(err);
return;
}
connection.query(sql, values, (err, results) => {
connection.release();
if (err) {
if (err.code === 'ECONNRESET' && retries > 0) {
console.error('连接中断,尝试重新连接...');
setTimeout(()=>{
executeQuery(sql,values,retries - 1).then(resolve).catch(reject)
}, 2000); //间隔两秒进行重试
}
else
{
reject(err);
}
return;
}
resolve(results);
});
});
});
}
// 使用方法:
executeQuery('SELECT * FROM your_table', [])
.then(results => {
console.log('查询结果:', results);
})
.catch(err => {
console.error('查询出错:', err);
});
额外说明:
- 这里设置了最大重试次数(
retries = 3
), 防止无限重连。 - 重连间隔可以适当调整, 不要太频繁。
4. 检查并调整 MySQL 服务器配置
有时候问题不在我们这边, 而在数据库服务器那边。我们可以检查一下 MySQL 的配置,看看有没有需要调整的地方。
操作步骤:
-
登录 MySQL 服务器:
mysql -u your_mysql_user -p
-
查看
wait_timeout
和interactive_timeout
的值:SHOW VARIABLES LIKE 'wait_timeout'; SHOW VARIABLES LIKE 'interactive_timeout';
wait_timeout
是服务器关闭非交互式连接之前等待的时间(秒),interactive_timeout
是服务器关闭交互式连接之前等待活动的时间。
- 如果需要, 修改这些值:
可以通过修改my.cnf
(或者my.ini
) 文件来修改这些配置, 修改后,需要重启MySQL 服务器才能生效。 举例, 修改my.cnf
:
[mysqld]
wait_timeout = 28800 # 8 hours
interactive_timeout = 28800 #8 hours
或者可以在运行时设置(不推荐,重启失效):
SET GLOBAL wait_timeout = 28800;
SET GLOBAL interactive_timeout = 28800;
注意:
- 调大这些值可以减少连接断开的频率,但也会占用更多的服务器资源。所以,要根据实际情况权衡。
5. 检查网络设备配置
除了数据库服务器配置, 我们还需要考虑网络设备。例如:防火墙, 负载均衡器等。
原理:
有些网络设备会有连接超时设置,会自动关闭长时间空闲的连接。我们需要找出这些设备,并调整它们的配置,或者通过心跳包机制来避免连接被关闭。
检查内容:
- 防火墙: 查看防火墙规则,确保没有阻止 Node.js 应用程序与 MySQL 数据库之间的连接。
- 负载均衡器: 检查负载均衡器的连接超时设置。
- ** 其他网络设备:** 路由器,交换机等,也可能有关闭空闲连接的策略。
6. 升级node-mysql
模块
旧版本的 node-mysql
可能存在一些 bug 或者对连接处理不完善。 尝试更新一下,可能直接解决问题。
npm install mysql@latest
通过上面这些方法, 大部分ECONNRESET
问题应该都可以搞定了. 大家可以根据自己的情况来选择合适的方案。关键还是要把原理弄懂,才能更好地应对各种问题。