如何优化所有MySQL数据库连接成功后才初始化服务的实现方案
多MySQL连接池启动逻辑优化方案
现有代码核心问题梳理
- 异步构造函数属于反模式:
await new DB()的写法不符合常规类实例化的直觉,可读性低,也容易被误用 - 重试逻辑冗余:每次重试时重复调用
initialize方法重复创建连接池,带来不必要的性能损耗,也可能产生资源泄漏风险 - 连接测试串行执行:三个数据库的连接校验依次执行,拉长了整体初始化的耗时
- 职责不单一:DB类内部直接调用
process.exit杀进程,和上层启动逻辑耦合度高 - 扩展性差:新增数据库需要修改
initialize、testConnections多处代码,硬编码参数多
优化方案(基于工厂模式+通用重试逻辑)
核心优化思路
- 采用静态工厂方法替代异步构造函数,符合JS的常规异步实例创建范式
- 连接池仅创建1次,重试阶段仅重复执行连接校验逻辑,不重复建池
- 用
Promise.all并行执行所有数据库的连接测试,减少初始化耗时 - 抽离通用重试工具函数,复用重试逻辑,代码更简洁
- 数据库配置列表化,新增数据库仅需修改配置数组,无需改动核心逻辑
- 错误统一向上抛出,由上层启动逻辑统一处理进程退出,职责拆分更清晰
优化后代码实现
DB类代码
const mysql = require('mysql2/promise'); // 直接用promise版本的mysql2,不用自己转promisify const logger = require('./你的logger路径'); const config = require('./你的config路径'); // 通用重试工具函数 const retry = async (fn, maxRetries, retryInterval) => { for (let i = 0; i < maxRetries; i++) { try { return await fn(); } catch (err) { if (i === maxRetries - 1) throw err; logger.error(`执行失败,${retryInterval/1000}秒后进行第${i+1}次重试,错误:${err}`); await new Promise(resolve => setTimeout(resolve, retryInterval)); } } } module.exports = class DB { // 私有构造函数,禁止外部直接new实例,必须通过静态create方法获取 constructor(pools) { this.pools = pools; // 保留原来的属性别名,兼容旧代码调用 this.db = pools.main; this.statisticsDb = pools.statistics; this.provisioningDb = pools.provisioning; } // 静态工厂方法,创建并初始化DB实例 static async create(options = {}) { const { maxRetries = 4, retryInterval = 2000, testQuery = 'SELECT 1 LIMIT 1' // 默认测试SQL不用业务表,避免test表不存在的问题 } = options; // 1. 一次性创建所有连接池 const dbConfigs = [ { name: 'main', config: config.get('db') }, { name: 'statistics', config: config.get('statisticsDb') }, { name: 'provisioning', config: config.get('provisioningDb') } ]; const pools = {}; dbConfigs.forEach(({name, config}) => { pools[name] = mysql.createPool(config); }); // 2. 带重试的连接校验逻辑 await retry(async () => { logger.info('开始校验所有数据库连接'); // 并行测试所有连接 const testPromises = Object.entries(pools).map(async ([dbName, pool]) => { const conn = await pool.getConnection(); await conn.query(testQuery); conn.release(); logger.info(`${dbName}数据库连接校验成功`); }); await Promise.all(testPromises); logger.info('所有数据库连接校验通过'); }, maxRetries, retryInterval); // 3. 全部校验通过后返回DB实例 return new DB(pools); } // 封装统一的查询方法,也可以保留原来各个库单独调用的方式 async query(dbName, sql, params) { return this.pools[dbName].query(sql, params); } }
index.js代码调整
const logger = require('./你的logger路径'); const DB = require('./你的DB类路径'); initialize(); async function initialize() { try { // 调用静态工厂方法获取DB实例,语义更清晰 const db = await DB.create({ maxRetries: 4, retryInterval: 2000 }); await cleanUpSomeData(db); new Server(db); } catch (e) { logger.error(`服务初始化失败,错误:${JSON.stringify(e)},即将关闭服务`); process.exit(1); } }
额外优化建议
- 如果后续需要新增数据库,仅需要在
create方法的dbConfigs数组里新增对应配置即可,无需修改其他逻辑 - 可以给连接池配置自带的
connectionLimit、connectTimeout等参数,和业务侧的重试逻辑形成双层保障 - 如果需要支持运行时连接断开自动重连,可以给每个连接池监听
error事件,内部维护重连逻辑,不影响上层业务使用
内容的提问来源于stack exchange,提问作者Thabet Sabha
相关产品推荐
相关产品推荐

