如何在NestJS(使用TypeORM)中忽略getaddrinfo ENOTFOUND错误
解决方案
1. 全局异常过滤器兜底捕获未处理异常
有些异常(比如TypeORM连接池的异步错误)不在业务代码的try/catch范围内,需要全局过滤器兜底,避免应用崩溃。
创建全局异常过滤器,专门处理getaddrinfo ENOTFOUND错误:
import { ExceptionFilter, Catch, ArgumentsHost, Logger } from '@nestjs/common'; import { Request, Response } from 'express'; @Catch(Error) export class GlobalExceptionFilter implements ExceptionFilter { private readonly logger = new Logger(GlobalExceptionFilter.name); catch(exception: Error, host: ArgumentsHost) { const ctx = host.switchToHttp(); const response = ctx.getResponse<Response>(); const request = ctx.getRequest<Request>(); // 捕获DNS解析失败错误 if (exception.message.includes('getaddrinfo ENOTFOUND')) { this.logger.error(`数据库DNS解析失败: ${exception.message}`, exception.stack); // 不抛出致命错误,返回正常响应或跳过处理 return response.status(200).json({ statusCode: 200, message: '数据库操作暂时不可用,已跳过', path: request.url, }); } // 其他异常按原有逻辑处理 this.logger.error(`未处理异常: ${exception.message}`, exception.stack); response.status(500).json({ statusCode: 500, message: '服务器内部错误', path: request.url, }); } }
在main.ts里注册全局过滤器:
async function bootstrap() { const app = await NestFactory.create(AppModule); app.useGlobalFilters(new GlobalExceptionFilter()); await app.listen(3000); } bootstrap();
2. 配置TypeORM连接的错误处理与重试
修改TypeORM的DataSource配置,增强连接错误处理,避免连接失败直接崩溃:
import { DataSource, Logger } from 'typeorm'; export const AppDataSource = new DataSource({ type: 'postgres', // CockroachDB兼容PostgreSQL驱动 url: process.env.DATABASE_URL, entities: [__dirname + '/**/*.entity{.ts,.js}'], synchronize: false, reconnect: true, // 开启自动重连 extra: { // PG连接池自定义配置 pool: { max: 5, min: 1, idleTimeoutMillis: 30000, connectionTimeoutMillis: 2000, // 自定义连接重试逻辑 async connect() { const originalConnect = this.connect.bind(this); let retries = 3; while (retries > 0) { try { return await originalConnect(); } catch (err) { if (err.message.includes('getaddrinfo ENOTFOUND') && retries > 1) { retries--; await new Promise(resolve => setTimeout(resolve, 1000 * (4 - retries))); } else { throw err; } } } } } }, // 连接失败回调:仅记录日志,不抛出致命错误 onFailedConnect: (error) => { const logger = new Logger('TypeORM'); logger.error(`数据库连接失败: ${error.message}`, error.stack); } });
如果使用NestJS的TypeORMModule,直接在forRoot中传入上述配置即可。
3. 手动控制数据库连接,避免启动时崩溃
既然数据库操作是可选的,不要让NestJS自动初始化连接,改为手动在业务服务中控制:
- 模块中手动创建DataSource,不依赖自动初始化:
import { Module, Logger } from '@nestjs/common'; import { DataSource } from 'typeorm'; @Module({ providers: [ { provide: 'DATA_SOURCE', useFactory: async () => { const dataSource = new DataSource({ // 数据库配置项 }); try { await dataSource.initialize(); Logger.log('数据库连接成功'); } catch (err) { Logger.error('数据库初始化失败,将在需要时重试', err.stack); // 不抛出错误,确保应用正常启动 } return dataSource; }, }, ], exports: ['DATA_SOURCE'], }) export class DatabaseModule {}
- 业务服务中先检查连接状态,失败则跳过操作:
import { Injectable, Inject, Logger } from '@nestjs/common'; import { DataSource } from 'typeorm'; import { YourEntity } from './entities/your.entity'; @Injectable() export class DataService { private readonly logger = new Logger(DataService.name); constructor(@Inject('DATA_SOURCE') private dataSource: DataSource) {} async saveData(data: any) { if (!this.dataSource.isInitialized) { this.logger.warn('数据库未连接,跳过数据保存'); return; } try { const repository = this.dataSource.getRepository(YourEntity); await repository.save(data); } catch (err) { if (err.message.includes('getaddrinfo ENOTFOUND')) { this.logger.error('保存数据时DNS解析失败,已跳过', err.stack); // 可标记数据待重试,后续定期检查补存 } else { throw err; } } } }
4. 实现优雅重启机制
当DNS错误次数达到阈值时,触发优雅重启,确保当前任务完成后再重启Pod:
import { Injectable, Logger, INestApplication } from '@nestjs/common'; @Injectable() export class HealthCheckService { private dnsErrorCount = 0; private readonly maxDnsErrors = 5; // 自定义错误阈值 private isShuttingDown = false; constructor(private app: INestApplication) {} async handleDnsError() { this.dnsErrorCount++; if (this.dnsErrorCount >= this.maxDnsErrors && !this.isShuttingDown) { this.isShuttingDown = true; Logger.warn('DNS错误次数达到阈值,将在当前任务完成后重启'); // 等待当前请求处理完成(根据业务调整等待时长) await new Promise(resolve => setTimeout(resolve, 10000)); // 关闭应用,触发shutdown hooks清理资源 await this.app.close(); // 退出进程,K8s会自动重启Pod process.exit(0); } } }
在全局异常过滤器或数据库错误回调中调用handleDnsError()即可触发逻辑。
内容的提问来源于stack exchange,提问作者Matthew Husák
相关产品推荐
相关产品推荐

