UNPKG

koatty_serve

Version:

Provide http1/2, websocket, gRPC server for Koatty.

1,845 lines (1,797 loc) 167 kB
/*! * @Author: richen * @Date: 2025-06-17 02:25:44 * @License: BSD (3-Clause) * @Copyright (c) - <richenlin(at)gmail.com> * @HomePage: https://koatty.org/ */ "use strict"; var e = require("@grpc/grpc-js"); var t = require("fs"); var o = require("koatty_logger"); var n = require("perf_hooks"); var i = require("koatty_lib"); var r = require("net"); var s = require("tls"); var c = require("http2"); var a = require("ws"); var l = require("http"); var h = require("https"); function d(e) { var t = Object.create(null); if (e) Object.keys(e).forEach((function(o) { if (o !== "default") { var n = Object.getOwnPropertyDescriptor(e, o); Object.defineProperty(t, o, n.get ? n : { enumerable: !0, get: function() { return e[o]; } }); } })); t.default = e; return Object.freeze(t); } var u = d(a); /* * @Description: Structured logging utilities based on koatty_logger * @Usage: * @Author: richen * @Date: 2025-01-27 12:00:00 * @License: BSD (3-Clause) * @Copyright (c): <richenlin(at)gmail.com> */ class StructuredLogger { constructor() { this.globalContext = {}; this.performanceTrackers = new Map; } static getInstance() { if (!StructuredLogger.instance) StructuredLogger.instance = new StructuredLogger; return StructuredLogger.instance; } setGlobalContext(e) { this.globalContext = { ...this.globalContext, ...e }; } clearGlobalContext() { this.globalContext = {}; } formatMessage(e, t, o) { const n = { ...this.globalContext, ...t }; const i = []; if (n.module) i.push(`[${n.module.toUpperCase()}]`); if (n.protocol) i.push(`[${n.protocol.toUpperCase()}]`); if (n.serverId) i.push(`[Server:${n.serverId}]`); if (n.connectionId) i.push(`[Conn:${n.connectionId}]`); if (n.action) i.push(`[${n.action}]`); const r = i.length > 0 ? `${i.join(" ")} ` : ""; let s = `${r}${e}`; if (o) { const e = typeof o === "object" ? JSON.stringify(o) : String(o); s += ` | Data: ${e}`; } if (n.traceId) s += ` | TraceId: ${n.traceId}`; return s; } debug(e, t, n) { const i = this.formatMessage(e, t, n); o.DefaultLogger.Debug(i); } info(e, t, n) { const i = this.formatMessage(e, t, n); o.DefaultLogger.Info(i); } warn(e, t, n) { const i = this.formatMessage(e, t, n); o.DefaultLogger.Warn(i); } error(e, t, n) { const i = n instanceof Error ? { name: n.name, message: n.message, stack: n.stack } : n; const r = this.formatMessage(e, t, i); o.DefaultLogger.Error(r); } startPerformanceTracking(e, t) { const o = { startTime: n.performance.now(), memoryUsage: process.memoryUsage() }; this.performanceTrackers.set(e, o); this.debug(`Performance tracking started`, { ...t, action: "perf_start" }, { trackingId: e }); } endPerformanceTracking(e, t) { const o = this.performanceTrackers.get(e); if (!o) { this.warn(`Performance tracking not found`, t, { trackingId: e }); return null; } o.endTime = n.performance.now(); o.duration = o.endTime - o.startTime; const i = process.memoryUsage(); const r = { heapUsed: i.heapUsed - o.memoryUsage.heapUsed, heapTotal: i.heapTotal - o.memoryUsage.heapTotal, external: i.external - o.memoryUsage.external }; this.info(`Performance tracking completed`, { ...t, action: "perf_end" }, { trackingId: e, duration: `${o.duration.toFixed(2)}ms`, memoryDiff: r }); this.performanceTrackers.delete(e); return o; } logServerEvent(e, t, o) { const n = { ...t, action: `server_${e}` }; switch (e) { case "starting": this.info(`Server starting`, n, o); break; case "started": this.info(`Server started successfully`, n, o); break; case "stopping": this.info(`Server stopping`, n, o); break; case "stopped": this.info(`Server stopped successfully`, n, o); break; case "error": this.error(`Server error occurred`, n, o); break; } } logConnectionEvent(e, t, o) { const n = { ...t, action: `connection_${e}` }; switch (e) { case "connected": this.info(`Connection established`, n, o); break; case "disconnected": this.info(`Connection closed`, n, o); break; case "error": this.error(`Connection error`, n, o); break; case "timeout": this.warn(`Connection timeout`, n, o); break; } } logSecurityEvent(e, t, o) { const n = { ...t, action: `security_${e}` }; switch (e) { case "auth_success": this.info(`Authentication successful`, n, o); break; case "auth_failure": this.warn(`Authentication failed`, n, o); break; case "rate_limit": this.warn(`Rate limit exceeded`, n, o); break; case "blocked": this.warn(`Request blocked`, n, o); break; } } createChildLogger(e) { return new ChildLogger(this, { ...this.globalContext, ...e }); } } class ChildLogger { constructor(e, t) { this.parent = e; this.childContext = t; } debug(e, t, o) { this.parent.debug(e, { ...this.childContext, ...t }, o); } info(e, t, o) { this.parent.info(e, { ...this.childContext, ...t }, o); } warn(e, t, o) { this.parent.warn(e, { ...this.childContext, ...t }, o); } error(e, t, o) { this.parent.error(e, { ...this.childContext, ...t }, o); } logServerEvent(e, t, o) { this.parent.logServerEvent(e, { ...this.childContext, ...t }, o); } logConnectionEvent(e, t, o) { this.parent.logConnectionEvent(e, { ...this.childContext, ...t }, o); } logSecurityEvent(e, t, o) { this.parent.logSecurityEvent(e, { ...this.childContext, ...t }, o); } startPerformanceTracking(e, t) { this.parent.startPerformanceTracking(e, { ...this.childContext, ...t }); } endPerformanceTracking(e, t) { return this.parent.endPerformanceTracking(e, { ...this.childContext, ...t }); } } const g = StructuredLogger.getInstance(); const p = e => g.createChildLogger(e); const m = () => `trace_${Date.now()}_${Math.random().toString(36).substr(2, 9)}` /* * @Description: * @Usage: * @Author: richen * @Date: 2023-12-09 12:02:29 * @LastEditTime: 2024-11-07 11:08:26 * @License: BSD (3-Clause) * @Copyright (c): <richenlin(at)gmail.com> */; function f(e, t, o = new WeakSet) { if (e === t) return !0; if (e == null || t == null) return !1; if (typeof e !== typeof t) return !1; if (typeof e === "object") { if (o.has(e)) return !0; o.add(e); const n = Array.isArray(e); const i = Array.isArray(t); if (n !== i) return !1; const r = Object.keys(e); const s = Object.keys(t); if (r.length !== s.length) return !1; const c = r.every((n => f(e[n], t[n], o))); o.delete(e); return c; } return !1; } function v(e) { return `${e}_${Date.now()}_${Math.random().toString(36).substr(2, 6)}`; } /* * @Description: 统一定时器管理器 * @Usage: 解决定时器资源泄漏问题,统一管理所有定时器 * @Author: richen * @Date: 2024-11-27 20:30:00 * @LastEditTime: 2024-11-27 20:30:00 */ var C; (function(e) { e[e["HIGH"] = 5e3] = "HIGH"; e[e["MEDIUM"] = 3e4] = "MEDIUM"; e[e["LOW"] = 6e4] = "LOW"; })(C || (C = {})); class TimerManager { constructor(e = {}) { this.timers = new Map; this.timerIdCounter = 0; this.logger = p({ module: "timer_manager" }); this.taskQueues = new Map; this.consolidatedTimers = new Map; this.performanceMetrics = { totalTasks: 0, executedTasks: 0, averageExecutionTime: 0, lastOptimization: Date.now() }; this.optimizerConfig = { enableConsolidation: !0, enableAdaptiveFrequency: !0, maxTimersPerFrequency: 10, loadThreshold: .7, ...e }; Object.values(C).forEach((e => { if (typeof e === "number") this.taskQueues.set(e, []); })); this.logger.info("Timer manager initialized with optimization", {}, { config: this.optimizerConfig, supportedFrequencies: Object.values(C).filter((e => typeof e === "number")) }); } addTimer(e, t, o) { return this.addOptimizedTimer({ name: e, callback: t, interval: o, priority: this.determinePriority(o), protocol: this.extractProtocol(e) }); } addOptimizedTimer(e) { const t = `${e.name}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; const o = { ...e, id: t, lastExecuted: 0, executionCount: 0 }; const n = this.selectOptimalFrequency(e.interval); const i = this.taskQueues.get(n); if (i) { i.push(o); this.performanceMetrics.totalTasks++; this.optimizeTimers(); } return t; } createPhysicalTimer(e, t, o) { const n = `${e}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; const i = () => { try { t(); } catch (t) { this.logger.error("Timer callback error", { name: e, timerId: n, error: t instanceof Error ? t.message : String(t) }); } }; const r = setInterval(i, o); if (typeof r.unref === "function") r.unref(); const s = { id: n, name: e, interval: o, callback: i, timer: r, createdAt: Date.now(), lastExecuted: Date.now() }; this.timers.set(n, s); return n; } clearTimer(e) { for (const [t, o] of this.taskQueues) { const t = o.findIndex((t => t.id === e || t.name === e)); if (t !== -1) { this.performanceMetrics.totalTasks--; this.optimizeTimers(); return !0; } } const t = this.timers.get(e); if (t) { clearInterval(t.timer); this.timers.delete(e); return !0; } this.logger.warn(`Timer '${e}' not found for clearing`); return !1; } clearAllTimers() { const e = this.performanceMetrics.totalTasks; const t = this.timers.size; this.logger.info(`Clearing all timers`, {}, { logicalTimers: e, physicalTimers: t }); this.taskQueues.clear(); this.performanceMetrics.totalTasks = 0; this.performanceMetrics.executedTasks = 0; Object.values(C).forEach((e => { if (typeof e === "number") this.taskQueues.set(e, []); })); for (const [e, t] of this.timers) try { clearInterval(t.timer); } catch (t) { this.logger.error(`Error clearing timer '${e}':`, {}, t); } this.timers.clear(); this.consolidatedTimers.clear(); this.logger.info(`All timers cleared successfully`, {}, { clearedLogicalTimers: e, clearedPhysicalTimers: t }); } getActiveTimerCount() { return this.performanceMetrics.totalTasks; } getTimerNames() { const e = []; for (const t of this.taskQueues.values()) e.push(...t.map((e => e.name))); return e; } getTimerInfo(e) { return this.timers.get(e); } getTimerStats() { const e = Date.now(); const t = []; for (const o of this.taskQueues.values()) for (const n of o) t.push({ id: n.id, name: n.name, interval: n.interval, uptime: e - (n.lastExecuted || e), lastExecuted: n.lastExecuted, priority: n.priority, executionCount: n.executionCount || 0 }); return { totalTimers: this.performanceMetrics.totalTasks, timers: t }; } hasTimer(e) { for (const t of this.taskQueues.values()) if (t.some((t => t.id === e))) return !0; return this.timers.has(e); } determinePriority(e) { if (e <= 5e3) return "high"; if (e <= 3e4) return "medium"; return "low"; } extractProtocol(e) { const t = e.match(/^(http|https|http2|grpc|websocket|ws)/i); return t ? t[1].toLowerCase() : void 0; } selectOptimalFrequency(e) { const t = [ C.HIGH, C.MEDIUM, C.LOW ]; for (const o of t) if (e <= o) return o; return C.LOW; } optimizeTimers() { this.clearConsolidatedTimers(); for (const [e, t] of this.taskQueues) if (t.length > 0) this.createConsolidatedTimer(e, t); this.performanceMetrics.lastOptimization = Date.now(); } createConsolidatedTimer(e, t) { const o = `consolidated_${e}ms`; const n = this.createPhysicalTimer(o, (() => { this.executeTaskBatch(e, t); }), e); this.consolidatedTimers.set(e, n); } executeTaskBatch(e, t) { let o = 0; const n = t.sort(((e, t) => { const o = { high: 3, medium: 2, low: 1 }; return o[t.priority] - o[e.priority]; })); for (const t of n) try { const n = Date.now(); if (this.shouldExecuteTask(t, e)) { t.callback(); t.lastExecuted = Date.now(); t.executionCount = (t.executionCount || 0) + 1; o++; const e = Date.now() - n; this.updateAverageExecutionTime(e); } } catch (e) { this.logger.error("Task execution error", {}, { taskId: t.id, taskName: t.name, error: e instanceof Error ? e.message : String(e) }); } this.performanceMetrics.executedTasks += o; } shouldExecuteTask(e, t) { if (!this.optimizerConfig.enableAdaptiveFrequency) return !0; if (e.priority === "high") return !0; const o = e.interval / t; if (o <= 1) return !0; const n = Date.now() - (e.lastExecuted || 0); return n >= e.interval; } updateAverageExecutionTime(e) { const t = .1; this.performanceMetrics.averageExecutionTime = this.performanceMetrics.averageExecutionTime * (1 - t) + e * t; } clearConsolidatedTimers() { for (const e of this.consolidatedTimers.values()) this.clearTimer(e); this.consolidatedTimers.clear(); } getOptimizationStats() { const e = new Map; const t = { high: 0, medium: 0, low: 0 }; for (const [o, n] of this.taskQueues) { e.set(o, n.length); for (const e of n) t[e.priority]++; } return { performance: this.performanceMetrics, consolidation: { activeTimers: this.consolidatedTimers.size, totalTasks: this.performanceMetrics.totalTasks, tasksByFrequency: Object.fromEntries(e), tasksByPriority: t }, config: this.optimizerConfig }; } static createOptimizedInstance() { return new TimerManager({ enableConsolidation: !0, enableAdaptiveFrequency: !0, maxTimersPerFrequency: 10, loadThreshold: .7 }); } demonstrateOptimization() { const e = [ 5e3, 5e3, 3e4, 3e4, 3e4, 6e4, 6e4 ]; const t = [ C.HIGH, C.MEDIUM, C.LOW ]; const o = ((e.length - t.length) / e.length * 100).toFixed(1); return { before: { timerCount: e.length, intervals: e }, after: { consolidatedTimers: t.length, frequencies: t, estimatedReduction: `${o}% timer reduction` } }; } destroy() { this.logger.info("TimerManager destroying", {}, { activeTimers: this.timers.size, totalTasks: this.performanceMetrics.totalTasks, executedTasks: this.performanceMetrics.executedTasks, activeConsolidatedTimers: this.consolidatedTimers.size }); this.clearConsolidatedTimers(); this.taskQueues.clear(); this.clearAllTimers(); this.logger.info("TimerManager destroyed successfully"); } } new TimerManager({ enableConsolidation: !0, enableAdaptiveFrequency: !0, maxTimersPerFrequency: 10, loadThreshold: .7 }); /* * @Description: 统一连接池监控管理器 * @Usage: 合并监控定时器,替代多个独立定时器,优化性能 * @Author: richen * @Date: 2024-11-27 20:30:00 * @LastEditTime: 2024-11-27 20:30:00 */ class UnifiedPoolMonitor { constructor(e, t = 5e3) { this.protocol = e; this.logger = p({ module: "unified_monitor" }); this.tasks = new Map; this.taskStats = new Map; this.monitoringInterval = 5e3; this.isRunning = !1; this.startTime = 0; this.lastExecutionTimes = new Map; this.timerManager = new TimerManager; this.monitoringInterval = t; } registerTask(e) { if (this.tasks.has(e.name)) this.logger.warn("Monitoring task already exists, replacing", {}, { taskName: e.name, protocol: this.protocol }); const t = { ...e, enabled: e.enabled !== !1, description: e.description || `Monitoring task: ${e.name}` }; this.tasks.set(e.name, t); this.taskStats.set(e.name, { tasksExecuted: 0, tasksSuccessful: 0, tasksFailed: 0, lastExecutionTime: 0, averageExecutionTime: 0, uptime: 0 }); } unregisterTask(e) { if (this.tasks.delete(e)) { this.taskStats.delete(e); this.lastExecutionTimes.delete(e); } } setTaskEnabled(e, t) { const o = this.tasks.get(e); if (o) o.enabled = t; } startMonitoring() { if (this.isRunning) { this.logger.warn("Unified monitoring already running", {}, { protocol: this.protocol }); return; } this.isRunning = !0; this.startTime = Date.now(); this.timerManager.addTimer("unified_monitoring", (() => { this.executeMonitoringCycle(); }), this.monitoringInterval); this.logger.info("Unified monitoring started", {}, { interval: this.monitoringInterval, tasksCount: this.tasks.size, protocol: this.protocol }); } stopMonitoring() { if (!this.isRunning) return; this.isRunning = !1; this.timerManager.destroy(); this.logger.info("Unified monitoring stopped", {}, { protocol: this.protocol, uptime: Date.now() - this.startTime }); } async executeMonitoringCycle() { const e = Date.now(); const t = m(); const o = this.getTasksToExecute(e); if (o.length === 0) return; const n = this.groupTasksByPriority(o); for (const [e, o] of n) await this.executePriorityGroup(o, e, t); } getTasksToExecute(e) { const t = []; for (const [o, n] of this.tasks) { if (!n.enabled) continue; const i = this.lastExecutionTimes.get(o) || 0; if (e - i >= n.interval) t.push(n); } return t.sort(((e, t) => e.priority - t.priority)); } groupTasksByPriority(e) { const t = new Map; for (const o of e) { const e = o.priority; if (!t.has(e)) t.set(e, []); t.get(e).push(o); } return t; } async executePriorityGroup(e, t, o) { const n = e.map((e => this.executeTask(e, o))); await Promise.allSettled(n); } async executeTask(e, t) { const o = Date.now(); const n = this.taskStats.get(e.name); try { await e.execute(); const t = Date.now() - o; this.lastExecutionTimes.set(e.name, o); n.tasksExecuted++; n.tasksSuccessful++; n.lastExecutionTime = t; n.averageExecutionTime = this.calculateAverageExecutionTime(n, t); n.uptime = Date.now() - this.startTime; return { taskName: e.name, success: !0, executionTime: t, timestamp: o }; } catch (i) { const r = Date.now() - o; n.tasksExecuted++; n.tasksFailed++; n.lastExecutionTime = r; n.uptime = Date.now() - this.startTime; if (e.onError) try { e.onError(i); } catch (o) { this.logger.error(`Task error handler failed: ${e.name}`, { traceId: t }, o); } this.logger.error(`Task execution failed: ${e.name}`, { traceId: t }, i); return { taskName: e.name, success: !1, executionTime: r, error: i, timestamp: o }; } } calculateAverageExecutionTime(e, t) { if (e.tasksExecuted === 1) return t; const o = .1; return e.averageExecutionTime * (1 - o) + t * o; } getTaskStats(e) { if (e) return this.taskStats.get(e) || { tasksExecuted: 0, tasksSuccessful: 0, tasksFailed: 0, lastExecutionTime: 0, averageExecutionTime: 0, uptime: 0 }; return new Map(this.taskStats); } getMonitorStatus() { let e = 0; let t = 0; let o = 0; for (const n of this.taskStats.values()) { e += n.tasksExecuted; t += n.tasksSuccessful; o += n.tasksFailed; } const n = Array.from(this.tasks.values()).filter((e => e.enabled)).length; return { isRunning: this.isRunning, uptime: this.isRunning ? Date.now() - this.startTime : 0, tasksCount: this.tasks.size, enabledTasksCount: n, totalExecutions: e, totalSuccesses: t, totalFailures: o }; } getTasks() { return Array.from(this.tasks.entries()).map((([e, t]) => ({ name: e, interval: t.interval, priority: t.priority, enabled: t.enabled || !1, description: t.description || "", stats: this.taskStats.get(e) || { tasksExecuted: 0, tasksSuccessful: 0, tasksFailed: 0, lastExecutionTime: 0, averageExecutionTime: 0, uptime: 0 } }))); } destroy() { this.stopMonitoring(); this.tasks.clear(); this.taskStats.clear(); this.lastExecutionTimes.clear(); } } class MonitoringTaskFactory { static createHealthCheckTask(e, t = 5e3) { return { name: "health_check", interval: t, priority: 1, execute: e, description: "Pool health check monitoring" }; } static createCleanupTask(e, t = 3e4) { return { name: "cleanup_expired", interval: t, priority: 3, execute: e, description: "Cleanup expired connections" }; } static createPingTask(e, t = 3e4, o = "generic") { return { name: `${o}_ping`, interval: t, priority: 2, execute: e, description: `${o.toUpperCase()} ping monitoring` }; } static createHeartbeatTask(e, t = 2e4) { return { name: "heartbeat", interval: t, priority: 2, execute: e, description: "Connection heartbeat monitoring" }; } static createSecurityMonitoringTask(e, t = 6e4) { return { name: "security_monitoring", interval: t, priority: 4, execute: e, description: "Security monitoring and analysis" }; } static createMetricsCollectionTask(e, t = 1e4) { return { name: "metrics_collection", interval: t, priority: 5, execute: e, description: "Performance metrics collection" }; } } /* * @Description: 统一连接池管理接口 * @Usage: 为各协议提供统一的连接池管理接口 * @Author: richen * @Date: 2024-11-27 20:30:00 * @LastEditTime: 2024-11-27 20:30:00 */ var S; (function(e) { e["HEALTHY"] = "healthy"; e["DEGRADED"] = "degraded"; e["OVERLOADED"] = "overloaded"; e["UNAVAILABLE"] = "unavailable"; })(S || (S = {})); var T; (function(e) { e["CONNECTION_ADDED"] = "connection_added"; e["CONNECTION_REMOVED"] = "connection_removed"; e["CONNECTION_TIMEOUT"] = "connection_timeout"; e["CONNECTION_ERROR"] = "connection_error"; e["POOL_LIMIT_REACHED"] = "pool_limit_reached"; e["HEALTH_STATUS_CHANGED"] = "health_status_changed"; })(T || (T = {})); class ConnectionPoolManager { constructor(e, t = {}) { this.logger = p({ module: "connection_pool" }); this.startTime = Date.now(); this.eventListeners = new Map; this.connections = new Map; this.connectionMetadata = new Map; this.waitingQueue = []; this.latencyBuffer = []; this.lastMetricsUpdate = Date.now(); this.protocol = e; this.config = this.validateAndNormalizeConfig(t); this.logger = p({ module: "connection_pool", protocol: this.protocol }); this.metrics = this.initializeMetrics(); this.currentHealth = this.initializeHealth(); this.timerManager = new TimerManager; this.unifiedMonitor = new UnifiedPoolMonitor(this.protocol, 5e3); this.setupUnifiedMonitoring(); this.logger.info("Connection pool manager initialized", {}, { protocol: this.protocol, config: this.config }); } initializeMetrics() { return { protocol: this.protocol, activeConnections: 0, totalConnections: 0, connectionsPerSecond: 0, averageLatency: 0, errorRate: 0, poolConfig: this.config, health: this.currentHealth, performance: { throughput: 0, latency: { p50: 0, p95: 0, p99: 0 }, memoryUsage: 0, cpuUsage: 0 }, uptime: 0 }; } initializeHealth() { return { status: S.HEALTHY, utilizationRatio: 0, activeConnections: 0, maxConnections: this.config.maxConnections || 1e3, rejectedConnections: 0, averageResponseTime: 0, errorRate: 0, message: "Connection pool is healthy", lastUpdated: Date.now() }; } validateAndNormalizeConfig(e) { const t = { maxConnections: e.maxConnections || 1e3, connectionTimeout: e.connectionTimeout || 3e4, keepAliveTimeout: e.keepAliveTimeout || 5e3, requestTimeout: e.requestTimeout || 3e4, headersTimeout: e.headersTimeout || 1e4, ...e }; if (t.maxConnections && t.maxConnections <= 0) throw new Error("maxConnections must be positive"); if (t.connectionTimeout && t.connectionTimeout <= 0) throw new Error("connectionTimeout must be positive"); return t; } async requestConnection(e = {}) { const t = Date.now(); const o = e.timeout || this.config.connectionTimeout || 3e4; try { if (!this.canAcceptConnection()) { this.currentHealth.rejectedConnections++; this.emitEvent(T.POOL_LIMIT_REACHED, { currentConnections: this.getActiveConnectionCount(), maxConnections: this.config.maxConnections }); return { connection: null, success: !1, error: new Error("Connection pool limit reached"), waitTime: Date.now() - t }; } const n = await this.getAvailableConnection(); if (n) return { connection: n.connection, success: !0, waitTime: Date.now() - t, connectionId: n.id }; const i = await this.createNewConnection(e); if (i) { await this.addConnection(i.connection, i.metadata); return { connection: i.connection, success: !0, waitTime: Date.now() - t, connectionId: i.id }; } return new Promise(((n, i) => { const r = setTimeout((() => { const e = this.waitingQueue.findIndex((e => e.resolve === n)); if (e >= 0) this.waitingQueue.splice(e, 1); n({ connection: null, success: !1, error: new Error("Connection request timeout"), waitTime: Date.now() - t }); }), o); this.waitingQueue.push({ resolve: e => { clearTimeout(r); n(e); }, reject: e => { clearTimeout(r); i(e); }, options: e, timestamp: t }); this.waitingQueue.sort(((e, t) => { const o = { low: 1, normal: 2, high: 3 }; const n = o[e.options.priority || "normal"]; const i = o[t.options.priority || "normal"]; return i - n; })); })); } catch (e) { this.recordConnectionEvent("error", { error: e }); return { connection: null, success: !1, error: e, waitTime: Date.now() - t }; } } async releaseConnection(e, t = {}) { var o; const n = m(); try { const i = this.findConnectionId(e); if (!i) { this.logger.warn("Attempting to release unknown connection", { traceId: n }); return !1; } if (t.destroy || t.error) await this.removeConnection(e, ((o = t.error) === null || o === void 0 ? void 0 : o.message) || "Explicitly destroyed"); else this.markConnectionAvailable(i); await this.processWaitingQueue(); return !0; } catch (e) { this.logger.error("Failed to release connection", { traceId: n }, e); return !1; } } async addConnection(e, t = {}) { const o = this.generateConnectionId(); try { if (!this.validateConnection(e)) throw new Error("Invalid connection"); if (!this.canAcceptConnection()) throw new Error("Connection pool limit reached"); this.connections.set(o, e); this.connectionMetadata.set(o, { ...t, id: o, createdAt: Date.now(), lastUsed: Date.now(), available: t.available !== void 0 ? t.available : !0 }); this.recordConnectionEvent("added", { connectionId: o, metadata: t }); this.emitEvent(T.CONNECTION_ADDED, { connectionId: o, connection: e }); return !0; } catch (e) { this.logger.error("Failed to add connection to pool", {}, e); return !1; } } async removeConnection(e, t) { const o = this.findConnectionId(e); if (!o) return; try { await this.cleanupConnection(e); this.connections.delete(o); this.connectionMetadata.delete(o); this.recordConnectionEvent("removed", { connectionId: o, reason: t }); this.emitEvent(T.CONNECTION_REMOVED, { connectionId: o, reason: t }); } catch (e) { this.logger.error("Error removing connection from pool", {}, e); } } getActiveConnectionCount() { return this.connections.size; } async closeAllConnections(e = 5e3) { const t = m(); this.logger.info("Closing all connections", { traceId: t }, { activeConnections: this.connections.size }); const o = []; for (const [e, n] of this.connections) o.push(this.removeConnection(n, "Pool shutdown").catch((o => { this.logger.error("Error closing connection", { traceId: t }, { connectionId: e, error: o }); }))); try { await Promise.race([ Promise.all(o), new Promise(((t, o) => setTimeout((() => o(new Error("Close timeout"))), e))) ]); } catch (e) { this.logger.warn("Some connections failed to close gracefully", { traceId: t }, e); } this.connections.clear(); this.connectionMetadata.clear(); this.logger.info("All connections closed", { traceId: t }); } async createNewConnection(e) { const t = await this.createProtocolConnection(e); if (t) return { connection: t.connection, id: this.generateConnectionId(), metadata: t.metadata }; return null; } canAcceptConnection() { const e = this.config.maxConnections; if (!e) return !0; const t = this.getActiveConnectionCount(); return t < e; } updateHealthStatus() { const e = this.getActiveConnectionCount(); const t = this.config.maxConnections || 1 / 0; const o = t === 1 / 0 ? 0 : e / t; let n = S.HEALTHY; let i = "Connection pool is healthy"; if (o > .95) { n = S.OVERLOADED; i = "Connection pool is overloaded"; } else if (o > .8) { n = S.DEGRADED; i = "Connection pool is under high load"; } const r = this.currentHealth.status; this.currentHealth = { ...this.currentHealth, status: n, utilizationRatio: o, activeConnections: e, maxConnections: t === 1 / 0 ? 0 : t, message: i, lastUpdated: Date.now() }; this.metrics.health = this.currentHealth; if (r !== n) this.emitEvent(T.HEALTH_STATUS_CHANGED, { oldStatus: r, newStatus: n, health: this.currentHealth }); } getHealth() { this.updateHealthStatus(); return { ...this.currentHealth }; } getMetrics() { const e = Date.now() - this.startTime; this.updatePerformanceMetrics(); return { ...this.metrics, uptime: e, health: this.getHealth(), activeConnections: this.getActiveConnectionCount() }; } getConfig() { return { ...this.config }; } async updateConfig(e) { const t = m(); try { this.logger.info("Updating connection pool configuration", { traceId: t }, { oldConfig: this.config, newConfig: e }); const o = this.validateAndNormalizeConfig({ ...this.config, ...e }); Object.assign(this.config, o); this.metrics.poolConfig = this.config; this.logger.info("Connection pool configuration updated successfully", { traceId: t }); return !0; } catch (e) { this.logger.error("Failed to update connection pool configuration", { traceId: t }, e); return !1; } } on(e, t) { if (!this.eventListeners.has(e)) this.eventListeners.set(e, new Set); this.eventListeners.get(e).add(t); } off(e, t) { const o = this.eventListeners.get(e); if (o) o.delete(t); } generateConnectionId() { return `${this.protocol}_${Date.now()}_${Math.random().toString(36).substr(2, 8)}`; } findConnectionId(e) { for (const [t, o] of this.connections) if (o === e) return t; return null; } markConnectionAvailable(e) { const t = this.connectionMetadata.get(e); if (t) { t.available = !0; t.lastUsed = Date.now(); } } async processWaitingQueue() { while (this.waitingQueue.length > 0 && this.canAcceptConnection()) { const e = this.waitingQueue.shift(); if (!e) break; try { const t = await this.requestConnection(e.options); e.resolve(t); } catch (t) { e.reject(t); } } } updatePerformanceMetrics() { const e = Date.now(); const t = (e - this.lastMetricsUpdate) / 1e3; if (t > 0) { this.metrics.connectionsPerSecond = this.metrics.totalConnections / ((e - this.startTime) / 1e3); this.metrics.performance.throughput = this.metrics.totalConnections / t; } if (this.latencyBuffer.length > 0) { const e = this.latencyBuffer.sort(((e, t) => e - t)); const t = e.length; this.metrics.performance.latency.p50 = e[Math.floor(t * .5)]; this.metrics.performance.latency.p95 = e[Math.floor(t * .95)]; this.metrics.performance.latency.p99 = e[Math.floor(t * .99)]; this.metrics.averageLatency = e.reduce(((e, t) => e + t)) / t; if (this.latencyBuffer.length > 1e3) this.latencyBuffer = this.latencyBuffer.slice(-500); } this.lastMetricsUpdate = e; } setupUnifiedMonitoring() { const e = MonitoringTaskFactory.createHealthCheckTask((() => this.updateHealthStatus()), 5e3); this.unifiedMonitor.registerTask(e); const t = MonitoringTaskFactory.createCleanupTask((() => this.cleanupExpiredConnections()), 3e4); this.unifiedMonitor.registerTask(t); this.unifiedMonitor.startMonitoring(); } cleanupExpiredConnections() { const e = Date.now(); const t = this.config.connectionTimeout || 3e4; const o = []; for (const [n, i] of this.connectionMetadata) if (i.available && e - i.lastUsed > t) { const e = this.connections.get(n); if (e) o.push({ id: n, connection: e }); } o.forEach((({connection: e}) => { this.removeConnection(e, "Connection expired").catch((e => { this.logger.error("Error cleaning up expired connection", {}, e); })); })); } emitEvent(e, t) { const o = this.eventListeners.get(e); if (o) o.forEach((e => { try { e(t); } catch (e) { this.logger.error("Error in connection pool event listener", {}, e); } })); } recordConnectionEvent(e, t) { const o = Date.now(); switch (e) { case "added": this.metrics.totalConnections++; this.metrics.activeConnections = this.getActiveConnectionCount(); break; case "removed": this.metrics.activeConnections = this.getActiveConnectionCount(); break; case "error": this.metrics.errorRate = Math.min(this.metrics.errorRate + .01, 1); break; } const n = (o - this.startTime) / 1e3; if (n > 0) this.metrics.connectionsPerSecond = this.metrics.totalConnections / n; } recordLatency(e) { this.latencyBuffer.push(e); } async destroy() { const e = m(); this.logger.info("Destroying connection pool manager", { traceId: e }); try { this.timerManager.destroy(); this.unifiedMonitor.destroy(); this.waitingQueue.forEach((e => { e.resolve({ connection: null, success: !1, error: new Error("Connection pool is being destroyed"), waitTime: Date.now() - e.timestamp }); })); this.waitingQueue = []; await this.closeAllConnections(5e3); this.eventListeners.clear(); this.logger.info("Connection pool manager destroyed successfully", { traceId: e }); } catch (t) { this.logger.error("Error destroying connection pool manager", { traceId: e }, t); throw t; } } async getConnection(e = {}) { return this.requestConnection(e); } async registerConnection(e, t = {}) { if (!this.canAcceptConnection()) return !1; const o = await this.addConnection(e, t); if (o) await this.setupProtocolSpecificHandlers(e); return o; } } /* * @Description: 统一优雅关闭逻辑 * @Usage: 解决各服务器类优雅关闭步骤的代码重复问题 * @Author: richen * @Date: 2024-11-27 20:30:00 * @LastEditTime: 2024-11-27 20:30:00 */ var y; (function(e) { e["NOT_STARTED"] = "not_started"; e["IN_PROGRESS"] = "in_progress"; e["DRAINING"] = "draining"; e["COMPLETING"] = "completing"; e["COMPLETED"] = "completed"; e["FAILED"] = "failed"; e["FORCED"] = "forced"; })(y || (y = {})); class GracefulShutdownManager { constructor(e) { this.protocol = e; this.isShuttingDown = !1; this.shutdownStartTime = 0; this.logger = p({ module: "graceful-shutdown" }); this.currentStatus = { isInProgress: !1, currentStep: "", startTime: 0, completedSteps: [], failedSteps: [] }; this.logger = p({ module: "graceful-shutdown", protocol: this.protocol }); } isInShutdown() { return this.isShuttingDown; } getStatus() { if (!this.currentStatus.isInProgress) return y.NOT_STARTED; return y.IN_PROGRESS; } async performGracefulShutdown(e, t = {}) { if (this.isShuttingDown) throw new Error("Graceful shutdown already in progress"); this.isShuttingDown = !0; this.shutdownStartTime = Date.now(); this.currentStatus = { isInProgress: !0, currentStep: "", startTime: this.shutdownStartTime, completedSteps: [], failedSteps: [] }; const o = t.timeout || 3e4; const n = t.drainDelay || 5e3; this.logger.info("Graceful shutdown initiated", {}, { protocol: this.protocol, totalSteps: e.length, timeout: o, drainDelay: n }); try { const t = await this.executeWithGlobalTimeout((() => this.executeShutdownSteps(e, n)), o); const i = Date.now() - this.shutdownStartTime; this.logger.info("Graceful shutdown completed", {}, { status: t.status, totalTime: i, completedSteps: t.completedSteps.length, failedSteps: t.failedSteps.length }); return { ...t, totalTime: i }; } catch (e) { const t = Date.now() - this.shutdownStartTime; this.logger.error("Graceful shutdown failed", {}, { error: e instanceof Error ? e.message : String(e), totalTime: t }); return { status: y.FAILED, completedSteps: this.currentStatus.completedSteps, failedSteps: [ ...this.currentStatus.failedSteps, { step: "global", error: e instanceof Error ? e.message : String(e), timestamp: Date.now() } ], totalTime: t }; } finally { this.isShuttingDown = !1; this.currentStatus.isInProgress = !1; } } async executeShutdownStep(e, t, o, n) { const i = e.timeout || t; const r = e.retryCount || 0; let s = 0; this.logger.info(`Executing shutdown step: ${e.name}`, { traceId: o }, { description: e.description, timeout: i, isRequired: e.isRequired !== !1 }); while (s <= r) try { await this.executeWithTimeout((() => e.execute(o)), i, `Shutdown step: ${e.name}`); n.completedSteps.push(e.name); this.logger.debug(`Shutdown step completed: ${e.name}`, { traceId: o }); return; } catch (t) { s++; if (s <= r) { this.logger.warn(`Shutdown step ${e.name} failed, retrying (${s}/${r})`, { traceId: o }, t); await new Promise((e => setTimeout(e, 1e3 * s))); } else { n.failedSteps.push({ step: e.name, error: t, timestamp: Date.now() }); if (e.isRequired !== !1) { this.logger.error(`Required shutdown step failed: ${e.name}`, { traceId: o }, t); throw t; } else this.logger.warn(`Optional shutdown step failed: ${e.name}`, { traceId: o }, t); } } } async performDrainDelay(e, t) { this.currentStatus.isInProgress = !0; this.logger.info("Starting drain delay", { traceId: t }, { drainDelay: e }); await new Promise((t => setTimeout(t, e))); this.logger.debug("Drain delay completed", { traceId: t }); } setupForceShutdownTimer(e, t, o) {} async executeWithTimeout(e, t, o) { return new Promise(((n, i) => { const r = setTimeout((() => { i(new Error(`${o} timed out after ${t}ms`)); }), t); e().then((e => { clearTimeout(r); n(e); })).catch((e => { clearTimeout(r); i(e); })); })); } createFailedResult(e) { return { status: y.FAILED, totalTime: 0, completedSteps: [], failedSteps: [ { step: "initialization", error: e, timestamp: Date.now() } ] }; } cleanup() {} async executeShutdownSteps(e, t) { const o = m(); for (const t of e) { this.currentStatus.currentStep = t.name; try { await this.executeWithTimeout((() => t.execute(o)), t.timeout || 5e3, t.name); this.currentStatus.completedSteps.push(t.name); } catch (e) { this.currentStatus.failedSteps.push({ step: t.name, error: e instanceof Error ? e.message : String(e), timestamp: Date.now() }); if (t.isRequired !== !1) throw e; } } if (t > 0) await new Promise((e => setTimeout(e, t))); return { status: y.COMPLETED, completedSteps: this.currentStatus.completedSteps, failedSteps: this.currentStatus.failedSteps, totalTime: 0 }; } async executeWithGlobalTimeout(e, t) { return new Promise(((o, n) => { const i = setTimeout((() => { n(new Error(`Global timeout of ${t}ms exceeded`)); }), t); e().then((e => { clearTimeout(i); o(e); })).catch((e => { clearTimeout(i); n(e); })); })); } } class ShutdownStepFactory { static createStopAcceptingStep(e, t = 5e3) { return { name: "stop_accepting_connections", description: "Stop accepting new connections", timeout: t, execute: e, isRequired: !0 }; } static createWaitConnectionsStep(e, t = 15e3) { return { name: "wait_connections_completion", description: "Wait for existing connections to complete", timeout: t, execute: o => e(t, o), isRequired: !0, retryCount: 1 }; } static createForceCloseStep(e, t = 5e3) { return { name: "force_close_connections", description: "Force close remaining connections", timeout: t, execute: e, isRequired: !0 }; } static createStopMonitoringStep(e, t = 3e3) { return { name: "stop_monitoring_cleanup", description: "Stop monitoring and cleanup resources", timeout: t, execute: async t => e(t), isRequired: !1 }; } static createProtocolShutdownStep(e, t, o = 3e3) { return { name: `${t}_force_shutdown`, description: `Force shutdown ${t} specific resources`, timeout: o, execute: async t => e(t), isRequired: !0 }; } } /* * @Description: Base server class with template method pattern for protocol servers * @Usage: 模板方法模式的基类,定义服务器生命周期的公共逻辑 * @Author: richen * @Date: 2025-04-08 10:45:00 * @LastEditTime: 2024-11-27 23:00:00 * @License: BSD (3-Clause) */ class BaseServer { constructor(e, t) { this.app = e; this.configVersion = 0; this.logger = p({ module: "base" }); this.shutdownTimeout = 3e4; this.drainDelay = 5e3; this.options = { ...t }; this.protocol = t.protocol; this.status = 0; this.serverId = v(t.protocol); this.timerManager = new TimerManager; this.shutdownManager = new GracefulShutdownManager(this.protocol); this.logger = p({ module: t.protocol, protocol: t.protocol, serverId: this.serverId }); this.logger.debug(`${t.protocol} server constructed`, {}, { protocol: t.protocol, hostname: t.hostname, port: t.port, serverId: this.serverId }); this.initializeServer(); } initializeServer() { this.logger.info("Initializing server", {}, { protocol: this.protocol, serverId: this.serverId }); try { this.initializeConnectionPool(); this.createProtocolServer(); this.configureServerOptions(); this.setupConnectionPoolEventListeners(); this.setupPeriodicCleanup(); this.performProtocolSpecificInitialization(); this.logger.debug("Server initialized successfully"); } catch (e) { this.logger.error("Server initialization failed", {}, e); throw e; } } async updateConfig(e) { const t = m(); const o = { ...this.options }; const n = { ...this.options, ...e }; const i = this.detectConfigurationChanges(o, e); if (i.length === 0) { this.logger.debug("No configuration changes detected", { traceId: t }); return !1; } this.logger.info("Configuration update initiated", { traceId: t }, { changedKeys: i.map(String), oldConfig: this.extractRelevantConfig(o), newConfig: this.extractRelevantConfig(n) }); const r = this.analyzeConfigChanges(i, o, n); this.logger.info("Configuration change analysis completed", { traceId: t }, r); try { if (r.requiresRestart) return await this.handleRestartRequiredChanges(n, t); else if (r.canApplyRuntime) return await this.handleRuntimeChanges(r, e, n, t); return !0; } catch (e) { this.logger.error("Configuration update failed", { traceId: t }, e); return !1; } } async gracefulShutdown(e = {}) { if (this.shutdownManager.isInShutdown()) { this.logger.warn("Graceful shutdown already in progress"); throw new Error("Graceful shutdown already in progress"); } const t = [ ShutdownStepFactory.createStopAcceptingStep((e => this.stopAcceptingNewConnections(e)), e.stepTimeout || 5e3), ShutdownStepFactory.createWaitConnectionsStep(((e, t) => this.waitForConnectionCompletion(e, t)), e.stepTimeout || 15e3), ShutdownStepFactory.createForceCloseStep((e => this.forceCloseRemainingConnections(e)), e.stepTimeout || 5e3), ShutdownStepFactory.createStopMonitoringStep((e => this.stopMonitoringAndCleanup(e)), 3e3) ]; const o = await this.shutdownManager.performGracefulShutdown(t, e); if (o.status === "failed") this.logger.error("Graceful shutdown failed", {}, { failedSteps: o.failedSteps.map((e => e.step)), totalTime: o.totalTime }); return o; } detectConfigurationChanges(e, t) { return Object.keys(t).filter((o => !f(e[o], t[o]))); } setupConnectionPoolEventListeners() { if (!this.connectionPool) return; this.connectionPool.on(T.POOL_LIMIT_REACHED, (e => { this.logger.warn(`${this.protocol.toUpperCase()} connection pool limit reached`, {}, e); })); this.connectionPool.on(T.HEALTH_STATUS_CHANGED, (e => { this.logger.info(`${this.protocol.toUpperCase()} connection pool health status changed`, {}, e); })); this.connectionPool.on(T.CONNECTION_ERROR, (e => { var t; this.logger.warn(`${this.protocol.toUpperCase()} connection pool error`, {}, { error: (t = e.error) === null || t === void 0 ? void 0 : t.message, connectionId: e.connectionId }); })); this.connectionPool.on(T.CONNECTION_TIMEOUT, (e => { this.logger.warn(`${this.protocol.toUpperCase()} connection timeout`, {}, e); })); this.connectionPool.on(T.CONNECTION_REMOVED, (e => { this.logger.debug(`${this.protocol.toUpperCase()} connection removed from pool`, {}, { connectionId: e.connectionId, reason: e.reason }); })); } setupPeriodicCleanup() { if (!this.connectionPool) return; this.timerManager.addTimer("base_cleanup", (() => { const e = this.connectionPool.getMetrics(); if (e.activeConnections === 0 && e.totalConnections > 0) this.logger.deb