// ===================================================== // 資料庫連線監控服務 // ===================================================== import { db } from './database'; import { dbFailover } from './database-failover'; export interface ConnectionStats { totalConnections: number; activeConnections: number; idleConnections: number; queuedRequests: number; maxConnections: number; usagePercentage: number; isHealthy: boolean; lastCheck: Date; } export class ConnectionMonitor { private static instance: ConnectionMonitor; private stats: ConnectionStats | null = null; private checkInterval: NodeJS.Timeout | null = null; private isMonitoring = false; private constructor() {} public static getInstance(): ConnectionMonitor { if (!ConnectionMonitor.instance) { ConnectionMonitor.instance = new ConnectionMonitor(); } return ConnectionMonitor.instance; } // 開始監控 public startMonitoring(intervalMs: number = 30000): void { if (this.isMonitoring) { console.log('⚠️ 連線監控已在運行中'); return; } this.isMonitoring = true; console.log(`🔍 開始連線監控,檢查間隔: ${intervalMs}ms`); this.checkInterval = setInterval(async () => { await this.checkConnectionHealth(); }, intervalMs); // 立即執行一次檢查 this.checkConnectionHealth(); } // 停止監控 public stopMonitoring(): void { if (this.checkInterval) { clearInterval(this.checkInterval); this.checkInterval = null; } this.isMonitoring = false; console.log('🛑 連線監控已停止'); } // 檢查連線健康狀態 public async checkConnectionHealth(): Promise { try { const stats = await this.getConnectionStats(); this.stats = stats; // 記錄連線狀態 console.log(`📊 資料庫連線狀態: ${stats.activeConnections}/${stats.maxConnections} (${stats.usagePercentage.toFixed(1)}%)`); // 檢查是否健康 if (stats.usagePercentage > 80) { console.warn(`⚠️ 資料庫連線使用率過高: ${stats.usagePercentage.toFixed(1)}%`); } if (stats.usagePercentage > 95) { console.error(`🚨 資料庫連線使用率危險: ${stats.usagePercentage.toFixed(1)}%`); } return stats; } catch (error) { console.error('❌ 連線健康檢查失敗:', error); return { totalConnections: 0, activeConnections: 0, idleConnections: 0, queuedRequests: 0, maxConnections: 0, usagePercentage: 0, isHealthy: false, lastCheck: new Date() }; } } // 獲取連線統計 public async getConnectionStats(): Promise { try { // 檢查備援狀態 const failoverStatus = db.getFailoverStatus(); if (failoverStatus?.isEnabled) { // 使用備援系統獲取統計 const result = await dbFailover.queryOne<{ current_connections: number; max_connections: number; }>('SHOW STATUS LIKE "Threads_connected"'); const maxResult = await dbFailover.queryOne<{ Variable_name: string; Value: string; }>('SHOW VARIABLES LIKE "max_connections"'); const currentConnections = result?.current_connections || 0; const maxConnections = parseInt(maxResult?.Value || '0'); const usagePercentage = maxConnections > 0 ? (currentConnections / maxConnections) * 100 : 0; return { totalConnections: currentConnections, activeConnections: currentConnections, idleConnections: 0, // MySQL 不直接提供空閒連線數 queuedRequests: 0, maxConnections, usagePercentage, isHealthy: usagePercentage < 90, lastCheck: new Date() }; } else { // 使用主資料庫獲取統計 const result = await db.queryOne<{ current_connections: number; max_connections: number; }>('SHOW STATUS LIKE "Threads_connected"'); const maxResult = await db.queryOne<{ Variable_name: string; Value: string; }>('SHOW VARIABLES LIKE "max_connections"'); const currentConnections = result?.current_connections || 0; const maxConnections = parseInt(maxResult?.Value || '0'); const usagePercentage = maxConnections > 0 ? (currentConnections / maxConnections) * 100 : 0; return { totalConnections: currentConnections, activeConnections: currentConnections, idleConnections: 0, queuedRequests: 0, maxConnections, usagePercentage, isHealthy: usagePercentage < 90, lastCheck: new Date() }; } } catch (error) { console.error('獲取連線統計失敗:', error); throw error; } } // 獲取當前統計 public getCurrentStats(): ConnectionStats | null { return this.stats; } // 強制清理連線 public async forceCleanup(): Promise { try { console.log('🧹 開始強制清理資料庫連線...'); // 獲取當前連線數 const stats = await this.getConnectionStats(); console.log(`清理前連線數: ${stats.activeConnections}`); // 強制關閉所有空閒連線 try { const { db } = await import('./database'); await db.close(); console.log('✅ 資料庫連線池已關閉'); // 等待連線完全關閉 await new Promise(resolve => setTimeout(resolve, 1000)); // 重新初始化 const { Database } = await import('./database'); const newDb = Database.getInstance(); console.log('✅ 資料庫連線池已重新初始化'); } catch (error) { console.error('❌ 強制清理連線池失敗:', error); } console.log('✅ 連線清理完成'); } catch (error) { console.error('❌ 連線清理失敗:', error); } } // 檢查是否正在監控 public isCurrentlyMonitoring(): boolean { return this.isMonitoring; } } // 導出單例實例 export const connectionMonitor = ConnectionMonitor.getInstance();