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.
报错是两个问题叠加导致:
- 当前使用
pg.Client建立单持久连接,连接存活满1小时后被Cloud SQL主动断开 - 代码未对数据库客户端监听
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
相关产品推荐
相关产品推荐

