修復 too many connection 問題
This commit is contained in:
178
lib/connection-lifecycle-manager.ts
Normal file
178
lib/connection-lifecycle-manager.ts
Normal file
@@ -0,0 +1,178 @@
|
||||
// =====================================================
|
||||
// 連線生命週期管理器
|
||||
// =====================================================
|
||||
|
||||
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();
|
Reference in New Issue
Block a user