You need to enable JavaScript to run this app. Node.js用pg库连接Postgres闲置断连后如何实现自动重连
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.27 11:45:05