TypeORM/NestJS:数据库故障或崩溃时如何实现自动重连?
在NestJS+TypeORM(PostgreSQL)中实现数据库自动重连并解决请求阻塞问题
一、利用TypeORM内置参数配置基础重连
TypeORM本身提供了连接重试的配置项,同时可以结合PostgreSQL驱动的连接池参数优化重连行为,在DataSource配置中添加以下参数:
import { DataSource } from 'typeorm'; export const AppDataSource = new DataSource({ type: 'postgres', host: 'localhost', port: 5432, username: 'your_username', password: 'your_password', database: 'your_db', entities: [__dirname + '/../**/*.entity{.ts,.js}'], synchronize: false, // TypeORM内置重连参数 reconnectAttempts: 10, // 最大重连尝试次数 reconnectInterval: 3000, // 每次重连间隔(毫秒) // PostgreSQL驱动额外配置(优化连接池与存活检测) extra: { connectionTimeoutMillis: 5000, // 单次连接超时时间 idleTimeoutMillis: 30000, // 空闲连接超时回收时间 max: 10, // 连接池最大连接数 keepAlive: true, // 启用TCP keep-alive keepAliveInitialDelayMillis: 30000, // 首次keep-alive探测延迟 }, });
二、自定义重连逻辑处理极端场景
如果内置参数无法覆盖所有断开场景(比如数据库重启后未触发自动重连),可以通过监听数据库连接事件,手动实现重连逻辑:
import { Module, OnModuleInit, OnModuleDestroy } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { AppDataSource } from './data-source'; @Module({ imports: [TypeOrmModule.forRoot(AppDataSource.options)], }) export class DatabaseModule implements OnModuleInit, OnModuleDestroy { async onModuleInit() { await AppDataSource.initialize(); // 监听连接断开事件 AppDataSource.driver.connection.on('end', () => { console.log('数据库连接已断开,启动重连流程'); this.initReconnect(); }); // 监听连接错误事件(针对异常断开) AppDataSource.driver.connection.on('error', (err) => { console.error('数据库连接错误:', err); if (['ECONNRESET', 'ETIMEDOUT', 'ECONNREFUSED'].includes(err.code)) { this.initReconnect(); } }); } private async initReconnect() { // 避免重复触发重连 if (AppDataSource.isInitialized) return; try { await AppDataSource.destroy(); await AppDataSource.initialize(); console.log('数据库重连成功'); } catch (err) { console.error('重连失败,3秒后重试:', err); setTimeout(() => this.initReconnect(), 3000); } } async onModuleDestroy() { await AppDataSource.destroy(); } }
三、解决请求阻塞问题
当数据库连接丢失时,未处理的请求会被阻塞,可通过全局拦截器捕获数据库连接异常,快速返回错误响应:
import { Injectable, NestInterceptor, ExecutionContext, CallHandler, HttpException, HttpStatus } from '@nestjs/common'; import { catchError, Observable } from 'rxjs'; @Injectable() export class DatabaseErrorInterceptor implements NestInterceptor { intercept(context: ExecutionContext, next: CallHandler): Observable<any> { return next.handle().pipe( catchError(err => { // 匹配数据库连接相关错误码或信息 const connectionErrors = ['ECONNREFUSED', 'connection lost', 'no pg_hba.conf entry']; if (connectionErrors.some(code => err.code === code || err.message.includes(code))) { throw new HttpException('数据库暂时不可用,请稍后重试', HttpStatus.SERVICE_UNAVAILABLE); } throw err; }), ); } }
在main.ts中注册全局拦截器:
import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { DatabaseErrorInterceptor } from './interceptors/database-error.interceptor'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.useGlobalInterceptors(new DatabaseErrorInterceptor()); await app.listen(3000); } bootstrap();
内容的提问来源于stack exchange,提问作者Theo Cerutti
相关产品推荐
相关产品推荐

