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

如何在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自动初始化连接,改为手动在业务服务中控制:

  1. 模块中手动创建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 {}
  1. 业务服务中先检查连接状态,失败则跳过操作:
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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 00:43:19