Node.js用pg库连接Postgres闲置断连后如何实现自动重连
问题根因
- 当前代码使用单例
pg.Client维护永久长连接,本身存在两个天然短板:- Postgres数据库、网络中间层(防火墙、负载均衡)都会主动掐断闲置数分钟的TCP连接,导致连接异常终止
- 单Client是串行处理请求的长连接,本身不适合Web服务的并发场景
- 原代码未给Client实例绑定
error事件监听器,连接意外断开时触发的error事件没有捕获逻辑——Node.js的EventEmitter对error事件做了特殊处理:一旦触发如果没有监听器,就会直接抛出未处理错误导致整个进程崩溃,这也是你看到堆栈里报Unhandled 'error' event的直接原因。
落地实现自动重连的方案
1. 紧急修复崩溃问题
不管用哪种重连方案,必须先在连接初始化阶段绑定error事件监听器,否则连接断开时依然会触发未捕获错误:
db.on('error', (err) => { console.error('数据库连接异常断开', err.message) // 触发重连逻辑 scheduleReconnect() })
2. 封装带退避策略的重连逻辑
不要在连接断开瞬间无限制重连,避免数据库短暂故障时被大量重连请求打挂。重连逻辑需要满足:
- 对已经断开的旧Client实例,直接新建全新的Client实例,不要复用失效的连接对象
- 加入指数退避策略,逐步拉长重连间隔,避免重连风暴
- 设置最大重试次数,超过阈值时触发告警或进程退出,不要无限循环重试
const DB_CONFIG = { user: 'xxx', host: 'xxx', database: 'xxx', password: 'xxx', port: xxx, } const MAX_RETRY = 10 const BASE_DELAY = 1000 let db = null let retryCount = 0 let isReconnecting = false async function initDB() { isReconnecting = true // 每次连接都新建Client,不复用旧实例 db = new Client(DB_CONFIG) db.on('error', (err) => { console.error('连接异常', err.message) scheduleReconnect() }) try { await db.connect() console.log('数据库连接成功') // 连接成功重置重试计数 retryCount = 0 isReconnecting = false } catch (err) { console.error('连接失败,准备重试', err.message) scheduleReconnect() } } function scheduleReconnect() { if (isReconnecting) return if (retryCount >= MAX_RETRY) { console.error('重连超过最大次数,数据库服务不可用') // 此处可接入告警逻辑 process.exit(1) } // 指数退避,最大延迟封顶30秒 const delay = Math.min(2 ** retryCount * BASE_DELAY, 30000) retryCount += 1 setTimeout(initDB, delay) } // 服务启动时初始化首次连接 initDB()
3. 查询层容错兜底
原代码直接调用db.query,遇到连接正在重连的场景会直接报错。封装统一的查询方法,对连接类错误做自动重试:
async function execQuery(sql, params = []) { // 重连过程中循环等待连接就绪 while (isReconnecting) { await new Promise(resolve => setTimeout(resolve, 100)) } try { return await db.query(sql, params) } catch (err) { // 仅针对连接类错误触发重连+重试,业务SQL错误直接抛出 if (err.code === 'ECONNRESET' || err.message.includes('Connection terminated') || err.message.includes('Connection is closed')) { console.log('查询遇到连接异常,准备重连后重试') scheduleReconnect() // 等待连接恢复后重试一次查询 return await execQuery(sql, params) } throw err } }
4. 更省心的替代方案:使用pg内置连接池
单Client长连接本身就不是为Web服务设计的,完全可以换成pg自带的Pool连接池,无需手写复杂的重连逻辑,就能天然解决闲置连接超时断开的问题:
const { Pool } = require('pg') const pool = new Pool({ ...DB_CONFIG, // 闲置连接超过30秒主动关闭,避免被网络层掐断 idleTimeoutMillis: 30000, // 新建连接的超时时间 connectionTimeoutMillis: 2000, }) // 必须给pool绑定error事件,否则闲置连接断开依然会触发未捕获错误 pool.on('error', (err) => { console.error('连接池闲置连接异常', err.message) }) // 查询时直接调用pool.query,内部会自动管理连接的创建、复用、失效重连 pool.query(MY_REQUEST, (err, data) => { if (err) throw err res.render('hello/world', { title: 'Hello', data: data.rows }) })
Pool的核心优势:
- 自动检测失效连接,遇到断开的连接直接剔除,按需新建可用连接,不需要手写重连逻辑
- 支持并发查询,不会像单Client那样串行阻塞
- 可配置闲置连接的存活时长,主动关闭长时间不用的连接,从根源避免超时断开的问题
内容的提问来源于stack exchange,提问作者mmmmmmm
相关产品推荐
相关产品推荐

