You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

GKE环境下node-postgres每小时触发Connection terminated unexpectedly错误排查

问题分析与修复方案:node-postgres每小时固定连接断开报错

多个基于Node.js和pg(node-postgres)开发的应用,生产环境每小时固定抛出Connection terminated unexpectedly错误导致进程崩溃,本地运行无此问题。

完整报错信息

> node ./dist/app.js

App Started
events.js:174
      throw er; // Unhandled 'error' event
      ^

Error: Connection terminated unexpectedly
    at Connection.con.once (/app/node_modules/pg/lib/client.js:255:9)
    at Object.onceWrapper (events.js:286:20)
    at Connection.emit (events.js:198:13)
    at Socket.<anonymous> (/app/node_modules/pg/lib/connection.js:139:10)
    at Socket.emit (events.js:203:15)
    at endReadableNT (_stream_readable.js:1145:12)
    at process._tickCallback (internal/process/next_tick.js:63:19)
Emitted 'error' event at:
    at connectedErrorHandler (/app/node_modules/pg/lib/client.js:202:10)
    at Connection.con.once (/app/node_modules/pg/lib/client.js:272:9)
    at Object.onceWrapper (events.js:286:20)
    [... lines matching original stack trace ...]
    at process._tickCallback (internal/process/next_tick.js:63:19)

现有实现代码

Database.ts

import { Client, QueryResult } from 'pg';

export default class DatabaseModule {

    private client: Client;

    constructor() {
        this.client = new Client({
            connectionString: process.env.DATABASE_URL
        });
    }

    public init(): Promise<any> {
        return this.client.connect();
    }
}

app.ts

Promise.all([
    express.init(),
    database.init()
])
.then(() => {
    console.log("App Started");
    [load routes...];
})
.catch((error) => {
    console.error(error);
    process.exit(1);
});

生产环境特征

  • 应用以微服务形式部署在Google Kubernetes Engine(GKE)
  • PostgreSQL使用Google Cloud SQL托管实例,GKE内应用通过本地地址访问
  • Pod重启规律:4天累计重启96次,刚好等于4*24,对应每小时崩溃1次
  • 无论数据库客户端是否处于空闲状态,都会触发断开问题

根因定位

根据Google Cloud SQL官方说明:

WebSockets are always available to your application without any additional setup. Once a WebSockets connection is established, it will time out after one hour.

报错是两个问题叠加导致:

  1. 当前使用pg.Client建立单持久连接,连接存活满1小时后被Cloud SQL主动断开
  2. 代码未对数据库客户端监听error事件,连接断开抛出的错误没有被捕获,直接触发进程未捕获异常退出

修复方案

1. 替换单客户端为连接池管理

改用pg.Pool替代pg.Client管理数据库连接,连接池会自动处理连接的创建、复用与销毁,避免长连接被服务端断开的问题。

2. 连接池参数适配Cloud SQL规则

配置连接池的idleTimeoutMillis参数小于3600000(1小时),让连接池主动释放空闲超过阈值的连接,避免等服务端断开触发错误。

3. 添加错误事件监听

无论使用单客户端还是连接池,都要监听error事件,捕获连接异常避免进程直接崩溃。

改造后Database.ts示例

import { Pool, QueryResult } from 'pg';

export default class DatabaseModule {

    private pool: Pool;

    constructor() {
        this.pool = new Pool({
            connectionString: process.env.DATABASE_URL,
            // 空闲连接30分钟自动释放,小于Cloud SQL的1小时断开阈值
            idleTimeoutMillis: 1800000,
            // 可根据业务需要配置最大连接数等参数
            max: 20
        });
        // 监听连接池错误,避免未捕获异常
        this.pool.on('error', (err) => {
            console.error('Database pool error:', err);
        });
    }

    public init(): Promise<any> {
        // 连接池初始化不需要主动调用connect,首次查询会自动创建连接,也可以预连接
        return Promise.resolve();
    }

    // 封装查询方法,从连接池获取连接执行查询后自动归还
    public query(queryText: string, params?: any[]): Promise<QueryResult> {
        return this.pool.query(queryText, params);
    }
}

内容的提问来源于stack exchange,提问作者mmoussa

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.09 21:00:01