Node.js应用随机出现NJS-040连接请求超时问题求助
Node.js Oracle连接池随机NJS-040超时错误解决方案
问题概述
运行Node.js应用时随机触发NJS-040: Request exceeded queueTimeout of 60000错误,无固定触发规律,重启应用可暂时恢复。错误发生在获取连接池连接的await oracledb.getConnection(process.env.ORACLE_POOL);代码行。
现有Oracle连接池配置
const oracledb = require("oracledb"); exports.initOracle = async function init() { try { oracledb.initOracleClient({ libDir: "C:\\instantclient_21_10"}); await oracledb.createPool({ user: process.env.ORACLE_USER, password: process.env.ORACLE_PASSWORD, connectString: process.env.ORACLE_STRING, poolAlias: process.env.ORACLE_POOL, poolMin: 1, poolMax: 30, poolIncrement: 1, poolTimeout: 10 }); console.log("Oracle connection pool started"); } catch (err) { console.error("init() error: " + err.message); } }; exports.closePool = async function () { try { const pool = await oracledb.getPool(process.env.ORACLE_POOL); await pool.close(); } catch (err) { console.log("Error occur while closing pool: ", process.env.ORACLE_POOL); } };
触发错误的查询函数
//Search for patient in oracle exports.getPatient = async (req, res, next) => { let result; let pesel = req.body.pesel; let conn; try { conn = await oracledb.getConnection(process.env.ORACLE_POOL); result = await conn.execute( "SELECT id, imie, nazw, pesl FROM pacj WHERE pesl=:pesel", { pesel: pesel }, { outFormat: oracledb.OBJECT } ); if (result.rows.length !== 1) { res.status(400).send({ message: "Patient not found in Oracle DB", }); } else { res.locals.patient = { patientId: result.rows[0]["ID"], firstname: result.rows[0]["IMIE"], lastname: result.rows[0]["NAZW"], pesel: result.rows[0]["PESL"], }; next(); } } catch (err) { console.error(err); res.status(500).send({ message: "Something went wrong while searching for patient query", }); } finally { if (conn) { try { await conn.close(); console.log("getPatient finally closed connection") } catch (err) { console.error(err); } } } };
排查与解决建议
1. 优化连接池参数配置
当前配置未显式设置queueTimeout,默认60秒,当poolMax(30个)连接全部被占用且无法及时释放时,新请求会排队超时。调整参数:
- 延长
queueTimeout至120秒,给连接释放留缓冲时间 - 添加
poolPingInterval定时检测空闲连接有效性,避免池中存在失效连接 - 可选添加
pingInterval,每次获取连接前自动检测连接可用性
修改后的连接池创建代码:
await oracledb.createPool({ user: process.env.ORACLE_USER, password: process.env.ORACLE_PASSWORD, connectString: process.env.ORACLE_STRING, poolAlias: process.env.ORACLE_POOL, poolMin: 1, poolMax: 30, poolIncrement: 1, poolTimeout: 10, queueTimeout: 120000, // 延长队列超时至2分钟 poolPingInterval: 60, // 每60秒检测空闲连接 pingInterval: 30 // 每次获取连接前检测有效性 });
2. 排查隐性连接泄漏
虽然日志显示连接已关闭,仍需确认是否存在极端场景下的泄漏:
- 在连接获取和释放时添加更详细日志,记录连接ID、时间戳,追踪每个连接的生命周期:
// 获取连接时 conn = await oracledb.getConnection(process.env.ORACLE_POOL); console.log(`获取连接ID: ${conn.id},时间: ${new Date().toISOString()}`); // 释放时 await conn.close(); console.log(`释放连接ID: ${conn.id},时间: ${new Date().toISOString()}`); - 检查其他使用连接池的代码,确保所有获取的连接都在finally块中关闭,避免遗漏。
3. 优化查询性能,减少连接占用时长
查询缓慢会导致连接长时间被占用,加剧池资源紧张:
- 为
pacj表的pesl字段创建索引,加速查询:CREATE INDEX idx_pacj_pesl ON pacj(pesl); - 确认查询语句无冗余逻辑,确保只返回必要字段(当前已实现)。
4. 添加连接池健康检查与自动恢复
定时检测连接池状态,异常时自动重建:
// 连接池健康检查函数 async function checkPoolHealth() { try { const pool = await oracledb.getPool(process.env.ORACLE_POOL); const conn = await pool.getConnection(); await conn.close(); } catch (err) { console.error("连接池异常,尝试重建:", err.message); // 关闭旧池 try { const oldPool = await oracledb.getPool(process.env.ORACLE_POOL); await oldPool.close(0); // 强制关闭所有连接 } catch (e) { console.error("关闭旧池失败:", e.message); } // 重新初始化连接池 await exports.initOracle(); } } // 每5分钟执行一次健康检查 setInterval(checkPoolHealth, 300000);
5. 升级oracledb依赖版本
旧版本可能存在连接池隐性bug,升级到最新稳定版:
npm install oracledb@latest
内容的提问来源于stack exchange,提问作者Callz
相关产品推荐
相关产品推荐

