Node.js操作PostgreSQL批量查询时出现ECONNREFUSED错误求助
看起来你遇到的核心问题是高并发的数据库连接请求耗尽了系统资源,导致PostgreSQL拒绝新连接——虽然你调整了PostgreSQL的连接参数,但代码里的连接管理逻辑有严重问题,加上Windows 10的网络/端口限制放大了这个问题。下面是针对性的分析和修复方案:
一、先看代码里的致命问题
1. 每次查询都新建连接池(完全违背连接池设计初衷)
你的queryDb2函数里,每次执行查询都会new PostGre.Pool(clientConfig),这会导致每一条SQL请求都创建一个新的连接池,瞬间产生大量的数据库连接,直接耗尽PostgreSQL的连接上限,甚至Windows系统的本地端口资源(每个连接都会占用一个本地端口)。
2. Promise的resolve时机错误
在loadProcesses的循环中,当createProcess成功时你直接调用了resolve(createdProcess)——这会直接resolve外层的loadProcesses Promise,而不是当前循环的单个Promise,导致Promise.all提前结束,后续的查询逻辑混乱,连接无法正常释放。
3. 字符串拼接SQL存在注入风险(附带问题)
readProcessByName里用字符串拼接SQL,哪怕有convertApostrophe处理,也不是安全的做法,还可能因为特殊字符导致查询失败。
二、分步修复方案
1. 重构连接池为全局单例
连接池应该全局只初始化一次,复用已有的连接,而不是每次查询都新建:
// 放在postGreAPI模块的顶部(全局位置),只创建一次 const pool = new PostGre.Pool({ ...clientConfig, max: 20, // 合理设置最大连接数,建议20-50,别贪大 idleTimeoutMillis: 30000 // 空闲连接30秒后释放 }); // 修改queryDb2函数,复用全局连接池 var queryDb2 = function (query, params) { return new Promise(function (resolve, reject) { pool.connect(function(err, client, done) { if(err) { return reject(err); } client.query(query, params, function(err, result) { done(); // 无论查询成功失败,都要调用done释放连接 if(err) { return reject(err); } resolve(result); }); }); }); }
2. 修复Promise的链式调用逻辑
调整loadProcesses里的Promise链,让每个循环的Promise正确完成,不要提前resolve外层Promise:
function loadProcesses(processArray){ return new Promise(function (resolve, reject) { let promises = []; for(let i=0;i<processArray.length;i++){ const process = new Process(null, processArray[i], false); // 把完整的Promise链push到数组,不要提前resolve const promise = postGreAPI.readProcessByName(process.name) .then(resultProcess => { if (!resultProcess) { // 返回createProcess的Promise,让链式调用继续 return postGreAPI.createProcess(process); } else { return resultProcess; } }) .catch(err => { return Promise.reject({msg:"readProcessByName hata aldı", err:err}); }); promises.push(promise); } Promise.all(promises) .then(processArray => { resolve({key:"Process", value:processArray}); }) .catch(err => { reject({msg:"Processlerden birisi insert edilemedi", err:err}); }); }); }
3. 改用参数化查询避免注入和字符问题
修改readProcessByName,用PostgreSQL的参数化查询代替字符串拼接:
var readProcessByName = function (name){ return new Promise(function (resolve, reject) { // $1是参数占位符,自动处理特殊字符 const query = "SELECT * FROM process WHERE name = $1"; queryDb2(query, [name]) .then(result => { if (result.rows.length>0){ const process = new Process(result.rows[0].id, result.rows[0].name, result.rows[0].isactive); resolve(process); } else { resolve(null); } }) .catch(err => { reject(err); }); }); }
4. 调整Windows 10的网络参数(可选但有效)
Windows 10默认的TcpTimedWaitDelay是240秒,关闭的连接会在TIME_WAIT状态保留4分钟,大量短连接会占用端口。可以修改这个值加快端口回收:
- 打开注册表编辑器(
regedit) - 定位到
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters - 创建或修改DWORD值
TcpTimedWaitDelay,设置为30(单位:秒) - 重启电脑生效
另外,检查Windows防火墙是否拦截了本地的5432端口连接,临时关闭防火墙测试是否解决问题,再添加对应的允许规则。
三、总结
你的问题根源是连接池使用错误导致的连接/端口耗尽,Windows 10的网络限制只是放大了这个问题。调整连接池为全局单例、修复Promise逻辑后,再配合合理的连接数配置,应该就能解决插入180条后出现ECONNREFUSED的问题。
内容的提问来源于stack exchange,提问作者Alper Bilgil

