179 lines
5.4 KiB
TypeScript
179 lines
5.4 KiB
TypeScript
// =====================================================
|
|
// 連線生命週期管理器
|
|
// =====================================================
|
|
|
|
import { db } from './database';
|
|
import { dbFailover } from './database-failover';
|
|
|
|
export class ConnectionLifecycleManager {
|
|
private static instance: ConnectionLifecycleManager;
|
|
private activeConnections = new Map<string, {
|
|
connection: any;
|
|
createdAt: number;
|
|
lastUsed: number;
|
|
userId?: string;
|
|
sessionId?: string;
|
|
}>();
|
|
private cleanupInterval: NodeJS.Timeout | null = null;
|
|
private maxIdleTime = 5 * 60 * 1000; // 5分鐘空閒超時
|
|
private maxConnectionAge = 30 * 60 * 1000; // 30分鐘最大連線時間
|
|
|
|
private constructor() {
|
|
this.startCleanupProcess();
|
|
}
|
|
|
|
public static getInstance(): ConnectionLifecycleManager {
|
|
if (!ConnectionLifecycleManager.instance) {
|
|
ConnectionLifecycleManager.instance = new ConnectionLifecycleManager();
|
|
}
|
|
return ConnectionLifecycleManager.instance;
|
|
}
|
|
|
|
// 註冊連線
|
|
public registerConnection(connectionId: string, connection: any, metadata?: {
|
|
userId?: string;
|
|
sessionId?: string;
|
|
}) {
|
|
this.activeConnections.set(connectionId, {
|
|
connection,
|
|
createdAt: Date.now(),
|
|
lastUsed: Date.now(),
|
|
userId: metadata?.userId,
|
|
sessionId: metadata?.sessionId
|
|
});
|
|
|
|
console.log(`📝 註冊連線: ${connectionId} (總數: ${this.activeConnections.size})`);
|
|
}
|
|
|
|
// 更新連線使用時間
|
|
public updateConnectionUsage(connectionId: string) {
|
|
const conn = this.activeConnections.get(connectionId);
|
|
if (conn) {
|
|
conn.lastUsed = Date.now();
|
|
}
|
|
}
|
|
|
|
// 釋放連線
|
|
public releaseConnection(connectionId: string) {
|
|
const conn = this.activeConnections.get(connectionId);
|
|
if (conn) {
|
|
try {
|
|
if (conn.connection && typeof conn.connection.release === 'function') {
|
|
conn.connection.release();
|
|
}
|
|
this.activeConnections.delete(connectionId);
|
|
console.log(`🗑️ 釋放連線: ${connectionId} (剩餘: ${this.activeConnections.size})`);
|
|
} catch (error) {
|
|
console.error(`❌ 釋放連線失敗: ${connectionId}`, error);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 開始清理程序
|
|
private startCleanupProcess() {
|
|
// 每30秒檢查一次空閒連線
|
|
this.cleanupInterval = setInterval(() => {
|
|
this.cleanupIdleConnections();
|
|
}, 30 * 1000);
|
|
|
|
console.log('🧹 連線生命週期管理器已啟動');
|
|
}
|
|
|
|
// 清理空閒連線
|
|
private cleanupIdleConnections() {
|
|
const now = Date.now();
|
|
const toRemove: string[] = [];
|
|
|
|
for (const [connectionId, conn] of this.activeConnections) {
|
|
const idleTime = now - conn.lastUsed;
|
|
const connectionAge = now - conn.createdAt;
|
|
|
|
// 檢查空閒時間
|
|
if (idleTime > this.maxIdleTime) {
|
|
console.log(`⏰ 連線 ${connectionId} 空閒時間過長 (${Math.round(idleTime / 1000)}s),準備釋放`);
|
|
toRemove.push(connectionId);
|
|
}
|
|
// 檢查連線年齡
|
|
else if (connectionAge > this.maxConnectionAge) {
|
|
console.log(`⏰ 連線 ${connectionId} 存在時間過長 (${Math.round(connectionAge / 1000)}s),準備釋放`);
|
|
toRemove.push(connectionId);
|
|
}
|
|
}
|
|
|
|
// 釋放需要清理的連線
|
|
toRemove.forEach(connectionId => {
|
|
this.releaseConnection(connectionId);
|
|
});
|
|
|
|
if (toRemove.length > 0) {
|
|
console.log(`🧹 清理了 ${toRemove.length} 個空閒連線`);
|
|
}
|
|
}
|
|
|
|
// 強制清理所有連線
|
|
public forceCleanupAll() {
|
|
console.log('🚨 強制清理所有連線...');
|
|
|
|
const connectionIds = Array.from(this.activeConnections.keys());
|
|
connectionIds.forEach(connectionId => {
|
|
this.releaseConnection(connectionId);
|
|
});
|
|
|
|
console.log(`✅ 已清理 ${connectionIds.length} 個連線`);
|
|
}
|
|
|
|
// 獲取連線統計
|
|
public getConnectionStats() {
|
|
const now = Date.now();
|
|
const connections = Array.from(this.activeConnections.values());
|
|
|
|
const idleConnections = connections.filter(conn =>
|
|
now - conn.lastUsed > this.maxIdleTime
|
|
).length;
|
|
|
|
const oldConnections = connections.filter(conn =>
|
|
now - conn.createdAt > this.maxConnectionAge
|
|
).length;
|
|
|
|
return {
|
|
totalConnections: this.activeConnections.size,
|
|
idleConnections,
|
|
oldConnections,
|
|
maxIdleTime: this.maxIdleTime,
|
|
maxConnectionAge: this.maxConnectionAge,
|
|
connections: connections.map(conn => ({
|
|
createdAt: new Date(conn.createdAt).toISOString(),
|
|
lastUsed: new Date(conn.lastUsed).toISOString(),
|
|
idleTime: now - conn.lastUsed,
|
|
age: now - conn.createdAt,
|
|
userId: conn.userId,
|
|
sessionId: conn.sessionId
|
|
}))
|
|
};
|
|
}
|
|
|
|
// 停止清理程序
|
|
public stop() {
|
|
if (this.cleanupInterval) {
|
|
clearInterval(this.cleanupInterval);
|
|
this.cleanupInterval = null;
|
|
}
|
|
|
|
// 清理所有連線
|
|
this.forceCleanupAll();
|
|
|
|
console.log('⏹️ 連線生命週期管理器已停止');
|
|
}
|
|
|
|
// 設置清理參數
|
|
public setCleanupParams(maxIdleTime?: number, maxConnectionAge?: number) {
|
|
if (maxIdleTime) this.maxIdleTime = maxIdleTime;
|
|
if (maxConnectionAge) this.maxConnectionAge = maxConnectionAge;
|
|
|
|
console.log(`⚙️ 更新清理參數: 空閒時間=${this.maxIdleTime}ms, 最大年齡=${this.maxConnectionAge}ms`);
|
|
}
|
|
}
|
|
|
|
// 導出單例實例
|
|
export const connectionLifecycleManager = ConnectionLifecycleManager.getInstance();
|