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

Promise内无法捕获异常:AWS Aurora MySQL连接重试逻辑失效问题排查求助

问题分析与修复方案

你的代码主要有几个核心问题导致重试逻辑没生效,我来逐一拆解:

1. 异步回调的错误无法被外层try/catch捕获

this.cPool.query是基于回调的异步操作,回调函数里的throw err不在createPool的Promise执行器的同步代码栈里,所以外层的try/catch根本抓不到这个错误,它会直接抛出到全局环境导致进程退出,这就是你看到直接报错退出的原因。

2. Promise提前resolve,未等待测试连接结果

你在创建完连接池后立刻设置this.cPoolInit = true并resolve了Promise,但此时测试连接的query还没执行完成。这意味着无论测试连接成功与否,createPool的Promise都会直接resolve,不会进入catch分支触发重试逻辑。

3. 重试时无法重新创建连接池

第一次进入createPool后,this.cPoolInit被设为true,后续重试调用createPool时,会直接跳过创建池的逻辑,一直用之前创建的错误池对象去测试连接,自然无法成功。

修复后的代码

我推荐改用mysql2/promise(原生支持Promise,比回调更易维护),同时调整重试逻辑,确保测试连接完成后再处理Promise状态,并且重试时能重新创建连接池:

import mysql from 'mysql2/promise'; // 改用mysql2/promise版本

export class DatabaseConnectionFactory {
  private cPool: mysql.Pool | null = null;
  private cPoolInit: boolean = false;

  public async initConnection() {
    try {
      await this.createPool();
      console.log('连接池初始化完成');
    } catch (err) {
      console.error('最终初始化失败:', err);
      throw err; // 向上抛出错误,让调用方处理
    }
  }

  private async createPool(attempt: number = 1): Promise<void> {
    try {
      // 每次重试都重新创建连接池(如果之前的池存在,先销毁)
      if (this.cPool) {
        await this.cPool.end();
        this.cPool = null;
      }
      
      this.cPool = mysql.createPool({
        connectionLimit: 500,
        host: "mysqlHost",
        port: "mysqlPort",
        user: "mysqlUser",
        password: "mysqlPassword"
      });

      // 使用Promise化的query测试连接,await等待结果
      await this.cPool.query('SHOW DATABASES;');
      console.log(`第${attempt}次尝试,测试连接成功`);
      this.cPoolInit = true;
    } catch (err) {
      console.error(`第${attempt}次尝试失败:`, err);
      if (attempt >= 3) {
        // 达到最大重试次数,抛出错误
        throw new Error(`连接重试3次均失败: ${(err as Error).message}`);
      }
      // 根据重试次数设置等待时间
      const waitTime = [30, 20, 10][attempt - 1];
      console.log(`等待${waitTime}秒后进行第${attempt + 1}次重试`);
      await this.wait(waitTime);
      // 递归调用createPool,继续重试
      await this.createPool(attempt + 1);
    }
  }

  private wait(seconds: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, seconds * 1000));
  }

  // 对外提供获取连接池的方法
  public getPool(): mysql.Pool {
    if (!this.cPool || !this.cPoolInit) {
      throw new Error('连接池未初始化,请先调用initConnection');
    }
    return this.cPool;
  }
}

关键修复点说明

  • 改用mysql2/promise:原生支持Promise,避免回调嵌套和错误捕获问题,query方法直接返回Promise,用await就能等待结果。
  • 重试时销毁旧连接池:每次重试都重新创建连接池,避免复用之前的错误连接配置。
  • 等待测试连接结果后再处理状态:只有当query成功执行后,才标记cPoolInit = true,否则进入catch分支处理重试。
  • 简化重试逻辑:用数组对应重试次数的等待时间,递归调用createPool直到达到最大重试次数,此时直接抛出错误。
  • 增加getPool方法:确保外部只能获取已初始化完成的连接池,避免使用未就绪的池。

这样修改后,当模拟连接失败时,代码会按照30秒、20秒、10秒的间隔重试3次,全部失败后才抛出最终错误,符合你的预期。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 20:27:44