修正資料庫未關閉問題
This commit is contained in:
206
lib/connection-monitor.ts
Normal file
206
lib/connection-monitor.ts
Normal file
@@ -0,0 +1,206 @@
|
||||
// =====================================================
|
||||
// 資料庫連線監控服務
|
||||
// =====================================================
|
||||
|
||||
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<ConnectionStats> {
|
||||
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<ConnectionStats> {
|
||||
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<void> {
|
||||
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();
|
Reference in New Issue
Block a user