UNPKG

koatty_serve

Version:

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

1,820 lines (1,810 loc) 328 kB
'use strict'; var grpcJs = require('@grpc/grpc-js'); var koatty_logger = require('koatty_logger'); var perf_hooks = require('perf_hooks'); var crypto$1 = require('crypto'); var koatty_lib = require('koatty_lib'); var fs = require('fs'); var path = require('path'); var net = require('net'); var tls = require('tls'); var http2 = require('http2'); var WS = require('ws'); var http = require('http'); var events = require('events'); var https = require('https'); var koatty_core = require('koatty_core'); function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; } function _interopNamespace(e) { if (e && e.__esModule) return e; var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n.default = e; return Object.freeze(n); } var fs__default = /*#__PURE__*/_interopDefault(fs); var path__namespace = /*#__PURE__*/_interopNamespace(path); var WS__namespace = /*#__PURE__*/_interopNamespace(WS); /*! * @Author: richen * @Date: 2026-04-24 08:20:32 * @License: BSD (3-Clause) * @Copyright (c) - <richenlin(at)gmail.com> * @HomePage: https://koatty.org/ */ var __defProp = Object.defineProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); function deepEqual(obj1, obj2, visited = /* @__PURE__ */ new WeakSet()) { if (obj1 === obj2) return true; if (obj1 == null || obj2 == null) return false; if (typeof obj1 !== typeof obj2) return false; if (typeof obj1 === "object") { if (visited.has(obj1)) return true; visited.add(obj1); const isArray1 = Array.isArray(obj1); const isArray2 = Array.isArray(obj2); if (isArray1 !== isArray2) return false; const keys1 = Object.keys(obj1); const keys2 = Object.keys(obj2); if (keys1.length !== keys2.length) return false; const result = keys1.every((key) => deepEqual(obj1[key], obj2[key], visited)); visited.delete(obj1); return result; } return false; } __name(deepEqual, "deepEqual"); function generateShortId() { try { return crypto$1.randomUUID().replace(/-/g, "").substring(0, 12); } catch { const bytes = crypto$1.randomBytes(16); bytes[6] = bytes[6] & 15 | 64; bytes[8] = bytes[8] & 63 | 128; return bytes.toString("hex").substring(0, 12); } } __name(generateShortId, "generateShortId"); function generateTraceId() { return `trace_${generateShortId()}`; } __name(generateTraceId, "generateTraceId"); function generateServerId(protocol) { return `${protocol}_server_${generateShortId()}`; } __name(generateServerId, "generateServerId"); // src/utils/logger.ts var StructuredLogger = class _StructuredLogger { static { __name(this, "StructuredLogger"); } static instance; globalContext = {}; constructor() { } static getInstance() { if (!_StructuredLogger.instance) { _StructuredLogger.instance = new _StructuredLogger(); } return _StructuredLogger.instance; } /** * Set global context for all logs * @param context Global context to merge with all log entries */ setGlobalContext(context) { this.globalContext = { ...this.globalContext, ...context }; } /** * Get current global context */ getGlobalContext() { return { ...this.globalContext }; } /** * Clear global context */ clearGlobalContext() { this.globalContext = {}; } /** * Format log message with context * @param message Log message * @param context Additional context * @param data Additional data to log * @returns Formatted message string */ formatMessage(message, context, data) { const mergedContext = context ? { ...this.globalContext, ...context } : this.globalContext; const parts = []; const moduleUpper = mergedContext.module?.toUpperCase(); const protocolUpper = mergedContext.protocol?.toUpperCase(); if (moduleUpper && protocolUpper && moduleUpper === protocolUpper) { parts.push(`[${protocolUpper}]`); } else { if (mergedContext.module && moduleUpper !== "KOATTYSERVER") { parts.push(`[${moduleUpper}]`); } if (mergedContext.protocol) { parts.push(`[${protocolUpper}]`); } } if (mergedContext.connectionId) { parts.push(`[conn:${mergedContext.connectionId}]`); } if (mergedContext.requestId) { parts.push(`[req:${mergedContext.requestId}]`); } if (mergedContext.traceId) { parts.push(`[trace:${mergedContext.traceId}]`); } let finalMessage = parts.length > 0 ? `${parts.join(" ")} ${message}` : message; if (data) { const contextKeys = Object.keys(mergedContext); const additionalData = Object.entries(data).filter(([key]) => !contextKeys.includes(key)).reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {}); if (Object.keys(additionalData).length > 0) { finalMessage += ` | Data: ${JSON.stringify(additionalData)}`; } } return finalMessage; } /** * Log debug message * @param message Log message * @param context Additional context * @param data Additional data */ debug(message, context, data) { const formattedMessage = this.formatMessage(message, context, data); koatty_logger.DefaultLogger.Debug(formattedMessage); } /** * Log info message * @param message Log message * @param context Additional context * @param data Additional data */ info(message, context, data) { const formattedMessage = this.formatMessage(message, context, data); koatty_logger.DefaultLogger.Info(formattedMessage); } /** * Log warning message * @param message Log message * @param context Additional context * @param data Additional data */ warn(message, context, data) { const formattedMessage = this.formatMessage(message, context, data); koatty_logger.DefaultLogger.Warn(formattedMessage); } /** * Log error message * @param message Log message * @param context Additional context * @param error Error object or additional data */ error(message, context, error) { let errorData = error; if (error instanceof Error) { errorData = { name: error.name, message: error.message, stack: error.stack }; } const formattedMessage = this.formatMessage(message, context, errorData); koatty_logger.DefaultLogger.Error(formattedMessage); } /** * Create a child logger with merged context * @param context Context to merge * @returns New logger instance with merged context */ child(context) { const childLogger = new _StructuredLogger(); childLogger.setGlobalContext({ ...this.globalContext, ...context }); return childLogger; } /** * Start performance measurement * @param label Performance measurement label * @returns Performance metrics object */ startPerformanceMeasurement(label) { const startTime = perf_hooks.performance.now(); const memoryUsage = process.memoryUsage(); return { startTime, memoryUsage, label }; } /** * End performance measurement and log result * @param metrics Performance metrics from startPerformanceMeasurement * @param context Additional context */ endPerformanceMeasurement(metrics, context) { const endTime = perf_hooks.performance.now(); const duration = endTime - metrics.startTime; const endMemoryUsage = process.memoryUsage(); const memoryDelta = { rss: endMemoryUsage.rss - (metrics.memoryUsage?.rss || 0), heapTotal: endMemoryUsage.heapTotal - (metrics.memoryUsage?.heapTotal || 0), heapUsed: endMemoryUsage.heapUsed - (metrics.memoryUsage?.heapUsed || 0), external: endMemoryUsage.external - (metrics.memoryUsage?.external || 0) }; this.debug(`Performance: ${metrics.label || "Operation"} completed`, context, { duration: `${duration.toFixed(2)}ms`, memoryDelta: { rss: `${(memoryDelta.rss / 1024 / 1024).toFixed(2)}MB`, heapUsed: `${(memoryDelta.heapUsed / 1024 / 1024).toFixed(2)}MB` } }); } /** * Measure and log performance of async operation * @param label Performance measurement label * @param operation Async operation to measure * @param context Additional context * @returns Result of the operation */ async measureAsync(label, operation, context) { const metrics = this.startPerformanceMeasurement(label); try { const result = await operation(); this.endPerformanceMeasurement(metrics, context); return result; } catch (error) { this.endPerformanceMeasurement(metrics, context); throw error; } } /** * Measure and log performance of sync operation * @param label Performance measurement label * @param operation Sync operation to measure * @param context Additional context * @returns Result of the operation */ measureSync(label, operation, context) { const metrics = this.startPerformanceMeasurement(label); try { const result = operation(); this.endPerformanceMeasurement(metrics, context); return result; } catch (error) { this.endPerformanceMeasurement(metrics, context); throw error; } } }; function createLogger(context) { const logger5 = StructuredLogger.getInstance(); if (context) { return logger5.child(context); } return logger5; } __name(createLogger, "createLogger"); StructuredLogger.getInstance(); // src/utils/timer-manager.ts var TimerFrequency = /* @__PURE__ */ (function(TimerFrequency2) { TimerFrequency2[TimerFrequency2["HIGH"] = 5e3] = "HIGH"; TimerFrequency2[TimerFrequency2["MEDIUM"] = 3e4] = "MEDIUM"; TimerFrequency2[TimerFrequency2["LOW"] = 6e4] = "LOW"; return TimerFrequency2; })({}); var TimerManager = class _TimerManager { static { __name(this, "TimerManager"); } timers = /* @__PURE__ */ new Map(); timerIdCounter = 0; logger = createLogger({ module: "timer_manager" }); // Phase 3 优化功能 optimizerConfig; taskQueues = /* @__PURE__ */ new Map(); consolidatedTimers = /* @__PURE__ */ new Map(); performanceMetrics = { totalTasks: 0, executedTasks: 0, averageExecutionTime: 0, lastOptimization: Date.now() }; constructor(config = {}) { this.optimizerConfig = { enableConsolidation: true, enableAdaptiveFrequency: true, maxTimersPerFrequency: 10, loadThreshold: 0.7, ...config }; Object.values(TimerFrequency).forEach((frequency) => { if (typeof frequency === "number") { this.taskQueues.set(frequency, []); } }); this.logger.debug("Timer manager initialized with optimization", {}, { config: this.optimizerConfig, supportedFrequencies: Object.values(TimerFrequency).filter((f) => typeof f === "number") }); } /** * 添加定时器 (传统方式) * @param name 定时器名称 * @param callback 回调函数 * @param interval 间隔时间(毫秒) * @returns 定时器ID */ /** * 添加定时器 - 统一使用优化模式 * @param name 定时器名称 * @param callback 回调函数 * @param interval 间隔时间(毫秒) * @returns 定时器ID */ addTimer(name, callback, interval) { return this.addOptimizedTimer({ name, callback, interval, priority: this.determinePriority(interval), protocol: this.extractProtocol(name) }); } /** * 添加优化定时器 - Phase 3 * @param task 定时器任务 * @returns 任务ID */ addOptimizedTimer(task) { const taskId = `${task.name}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; const optimizedTask = { ...task, id: taskId, lastExecuted: 0, executionCount: 0 }; const frequency = this.selectOptimalFrequency(task.interval); const taskQueue = this.taskQueues.get(frequency); if (taskQueue) { taskQueue.push(optimizedTask); this.performanceMetrics.totalTasks++; this.optimizeTimers(); } return taskId; } /** * 创建物理定时器 - 用于合并定时器的底层实现 */ createPhysicalTimer(name, callback, interval) { const timerId = `${name}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; const wrappedCallback = /* @__PURE__ */ __name(() => { try { callback(); } catch (error) { this.logger.error("Timer callback error", { name, timerId, error: error instanceof Error ? error.message : String(error) }); } }, "wrappedCallback"); const timer = setInterval(wrappedCallback, interval); if (typeof timer.unref === "function") { timer.unref(); } const timerInfo = { id: timerId, name, interval, callback: wrappedCallback, timer, createdAt: Date.now(), lastExecuted: Date.now() }; this.timers.set(timerId, timerInfo); return timerId; } /** * 清理指定定时器 - 支持逻辑定时器清理 * @param timerIdOrName 定时器ID或名称 * @returns 是否成功清理 */ clearTimer(timerIdOrName) { for (const [_frequency, tasks] of this.taskQueues) { const taskIndex = tasks.findIndex((task) => task.id === timerIdOrName || task.name === timerIdOrName); if (taskIndex !== -1) { this.performanceMetrics.totalTasks--; this.optimizeTimers(); return true; } } const timerInfo = this.timers.get(timerIdOrName); if (timerInfo) { clearInterval(timerInfo.timer); this.timers.delete(timerIdOrName); return true; } this.logger.warn(`Timer '${timerIdOrName}' not found for clearing`); return false; } /** * 清理所有定时器 - 支持逻辑和物理定时器清理 */ clearAllTimers() { const logicalTimerCount = this.performanceMetrics.totalTasks; const physicalTimerCount = this.timers.size; this.logger.debug(`Clearing all timers`, {}, { logicalTimers: logicalTimerCount, physicalTimers: physicalTimerCount }); this.taskQueues.clear(); this.performanceMetrics.totalTasks = 0; this.performanceMetrics.executedTasks = 0; Object.values(TimerFrequency).forEach((frequency) => { if (typeof frequency === "number") { this.taskQueues.set(frequency, []); } }); for (const [timerId, timerInfo] of this.timers) { try { clearInterval(timerInfo.timer); } catch (error) { this.logger.error(`Error clearing timer '${timerId}':`, {}, error); } } this.timers.clear(); this.consolidatedTimers.clear(); this.logger.debug(`All timers cleared successfully`, {}, { clearedLogicalTimers: logicalTimerCount, clearedPhysicalTimers: physicalTimerCount }); } /** * 获取活跃定时器数量 - 返回逻辑定时器数量 */ getActiveTimerCount() { return this.performanceMetrics.totalTasks; } /** * 获取所有定时器名称 - 返回逻辑定时器名称 */ getTimerNames() { const names = []; for (const tasks of this.taskQueues.values()) { names.push(...tasks.map((task) => task.name)); } return names; } /** * 获取定时器详细信息 */ getTimerInfo(timerId) { return this.timers.get(timerId); } /** * 获取所有定时器统计信息 - 返回逻辑定时器统计 */ getTimerStats() { const now = Date.now(); const timers = []; for (const tasks of this.taskQueues.values()) { for (const task of tasks) { timers.push({ id: task.id, name: task.name, interval: task.interval, uptime: now - (task.lastExecuted || now), lastExecuted: task.lastExecuted, priority: task.priority, executionCount: task.executionCount || 0 }); } } return { totalTimers: this.performanceMetrics.totalTasks, timers }; } /** * 检查是否存在指定定时器 - 支持逻辑和物理定时器 */ hasTimer(timerId) { for (const tasks of this.taskQueues.values()) { if (tasks.some((task) => task.id === timerId)) { return true; } } return this.timers.has(timerId); } /** * 确定定时器优先级 - Phase 3 优化 */ determinePriority(interval) { if (interval <= 5e3) return "high"; if (interval <= 3e4) return "medium"; return "low"; } /** * 从定时器名称提取协议 - Phase 3 优化 */ extractProtocol(name) { const protocolMatch = name.match(/^(http|https|http2|grpc|websocket|ws)/i); return protocolMatch ? protocolMatch[1].toLowerCase() : void 0; } /** * 选择最佳频率 - Phase 3 优化 */ selectOptimalFrequency(interval) { const frequencies = [ 5e3, 3e4, 6e4 ]; for (const freq of frequencies) { if (interval <= freq) { return freq; } } return 6e4; } /** * 优化定时器 - Phase 3 核心优化逻辑 */ optimizeTimers() { this.clearConsolidatedTimers(); for (const [frequency, tasks] of this.taskQueues) { if (tasks.length > 0) { this.createConsolidatedTimer(frequency, tasks); } } this.performanceMetrics.lastOptimization = Date.now(); } /** * 创建合并定时器 - Phase 3 优化 */ createConsolidatedTimer(frequency, tasks) { const timerName = `consolidated_${frequency}ms`; const timerId = this.createPhysicalTimer(timerName, () => { this.executeTaskBatch(frequency, tasks); }, frequency); this.consolidatedTimers.set(frequency, timerId); } /** * 执行任务批次 - Phase 3 优化 */ executeTaskBatch(frequency, tasks) { let executedCount = 0; const sortedTasks = tasks.sort((a, b) => { const priorityOrder = { high: 3, medium: 2, low: 1 }; return priorityOrder[b.priority] - priorityOrder[a.priority]; }); for (const task of sortedTasks) { try { const taskStartTime = Date.now(); if (this.shouldExecuteTask(task, frequency)) { task.callback(); task.lastExecuted = Date.now(); task.executionCount = (task.executionCount || 0) + 1; executedCount++; const taskExecutionTime = Date.now() - taskStartTime; this.updateAverageExecutionTime(taskExecutionTime); } } catch (error) { this.logger.error("Task execution error", {}, { taskId: task.id, taskName: task.name, error: error instanceof Error ? error.message : String(error) }); } } this.performanceMetrics.executedTasks += executedCount; } /** * 判断是否应该执行任务(自适应频率)- Phase 3 优化 */ shouldExecuteTask(task, frequency) { if (!this.optimizerConfig.enableAdaptiveFrequency) { return true; } if (task.priority === "high") { return true; } const intervalRatio = task.interval / frequency; if (intervalRatio <= 1) { return true; } const timeSinceLastExecution = Date.now() - (task.lastExecuted || 0); return timeSinceLastExecution >= task.interval; } /** * 更新平均执行时间 - Phase 3 优化 */ updateAverageExecutionTime(executionTime) { const alpha = 0.1; this.performanceMetrics.averageExecutionTime = this.performanceMetrics.averageExecutionTime * (1 - alpha) + executionTime * alpha; } /** * 清理合并定时器 - Phase 3 优化 */ clearConsolidatedTimers() { for (const timerId of this.consolidatedTimers.values()) { this.clearTimer(timerId); } this.consolidatedTimers.clear(); } /** * 获取优化统计信息 - Phase 3 优化 */ getOptimizationStats() { const tasksByFrequency = /* @__PURE__ */ new Map(); const tasksByPriority = { high: 0, medium: 0, low: 0 }; for (const [frequency, tasks] of this.taskQueues) { tasksByFrequency.set(frequency, tasks.length); for (const task of tasks) { tasksByPriority[task.priority]++; } } return { performance: this.performanceMetrics, consolidation: { activeTimers: this.consolidatedTimers.size, totalTasks: this.performanceMetrics.totalTasks, tasksByFrequency: Object.fromEntries(tasksByFrequency), tasksByPriority }, config: this.optimizerConfig }; } /** * 演示Phase 3优化功能 - 创建优化版本的TimerManager实例 */ static createOptimizedInstance() { return new _TimerManager({ enableConsolidation: true, enableAdaptiveFrequency: true, maxTimersPerFrequency: 10, loadThreshold: 0.7 }); } /** * 演示定时器优化效果 */ demonstrateOptimization() { const traditionalIntervals = [ 5e3, 5e3, 3e4, 3e4, 3e4, 6e4, 6e4 ]; const optimizedFrequencies = [ 5e3, 3e4, 6e4 ]; const reduction = ((traditionalIntervals.length - optimizedFrequencies.length) / traditionalIntervals.length * 100).toFixed(1); return { before: { timerCount: traditionalIntervals.length, intervals: traditionalIntervals }, after: { consolidatedTimers: optimizedFrequencies.length, frequencies: optimizedFrequencies, estimatedReduction: `${reduction}% timer reduction` } }; } /** * 安全销毁管理器 */ destroy() { this.logger.debug("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.debug("TimerManager destroyed successfully"); } }; new TimerManager({ enableConsolidation: true, enableAdaptiveFrequency: true, maxTimersPerFrequency: 10, loadThreshold: 0.7 }); // src/utils/ring_buffer.ts var RingBuffer = class { static { __name(this, "RingBuffer"); } buffer; head = 0; tail = 0; count = 0; capacity; /** * Create a ring buffer with fixed capacity * @param capacity Maximum number of items to store */ constructor(capacity) { if (capacity <= 0) { throw new Error("Ring buffer capacity must be greater than 0"); } this.capacity = capacity; this.buffer = new Array(capacity); } /** * Add an item to the buffer * If buffer is full, overwrites the oldest item * @param item Item to add */ push(item) { this.buffer[this.head] = item; this.head = (this.head + 1) % this.capacity; if (this.count < this.capacity) { this.count++; } else { this.tail = (this.tail + 1) % this.capacity; } } /** * Get all items in insertion order (oldest to newest) * @returns Array of items */ toArray() { if (this.count === 0) { return []; } const result = new Array(this.count); let index = this.tail; for (let i = 0; i < this.count; i++) { result[i] = this.buffer[index]; index = (index + 1) % this.capacity; } return result; } /** * Get a sorted copy of the buffer contents * @param compareFn Optional comparison function * @returns Sorted array */ toSortedArray(compareFn) { return this.toArray().sort(compareFn); } /** * Clear all items from the buffer */ clear() { this.head = 0; this.tail = 0; this.count = 0; } /** * Get the number of items currently in the buffer * @returns Current item count */ get length() { return this.count; } /** * Get the maximum capacity of the buffer * @returns Buffer capacity */ get size() { return this.capacity; } /** * Check if buffer is empty * @returns True if empty */ isEmpty() { return this.count === 0; } /** * Check if buffer is full * @returns True if full */ isFull() { return this.count === this.capacity; } /** * Get an item at a specific index (0 = oldest, length-1 = newest) * @param index Index to retrieve * @returns Item at index or undefined if out of range */ get(index) { if (index < 0 || index >= this.count) { return void 0; } const actualIndex = (this.tail + index) % this.capacity; return this.buffer[actualIndex]; } /** * Get the oldest item without removing it * @returns Oldest item or undefined if buffer is empty */ peek() { if (this.count === 0) { return void 0; } return this.buffer[this.tail]; } /** * Get the newest item * @returns Newest item or undefined if buffer is empty */ peekLast() { if (this.count === 0) { return void 0; } const lastIndex = (this.head - 1 + this.capacity) % this.capacity; return this.buffer[lastIndex]; } /** * Calculate percentile from buffer contents (e.g., 0.5 for median, 0.95 for P95) * @param percentile Percentile value between 0 and 1 * @returns Percentile value or undefined if buffer is empty */ getPercentile(percentile) { if (this.count === 0 || percentile < 0 || percentile > 1) { return void 0; } const sorted = this.toSortedArray((a, b) => a - b); const index = Math.floor(sorted.length * percentile); return sorted[Math.min(index, sorted.length - 1)]; } /** * Get average of numeric buffer contents * @returns Average value or undefined if buffer is empty */ getAverage() { if (this.count === 0) { return void 0; } let sum = 0; for (let i = 0; i < this.count; i++) { const value = this.get(i); sum += Number(value) || 0; } return sum / this.count; } /** * Iterate over buffer contents (oldest to newest) * @param callback Function to call for each item */ forEach(callback) { for (let i = 0; i < this.count; i++) { const item = this.get(i); if (item !== void 0) { callback(item, i); } } } /** * Map buffer contents to a new array * @param callback Function to transform each item * @returns New array of transformed items */ map(callback) { const result = new Array(this.count); for (let i = 0; i < this.count; i++) { const item = this.get(i); if (item !== void 0) { result[i] = callback(item, i); } } return result; } /** * Filter buffer contents * @param predicate Function to test each item * @returns New array of items that pass the test */ filter(predicate) { const result = []; for (let i = 0; i < this.count; i++) { const item = this.get(i); if (item !== void 0 && predicate(item, i)) { result.push(item); } } return result; } /** * Reduce buffer contents to a single value * @param callback Reducer function * @param initialValue Initial value for the accumulator * @returns Reduced value */ reduce(callback, initialValue) { let accumulator = initialValue; for (let i = 0; i < this.count; i++) { const item = this.get(i); if (item !== void 0) { accumulator = callback(accumulator, item, i); } } return accumulator; } }; var DynamicRingBuffer = class { static { __name(this, "DynamicRingBuffer"); } buffer; head = 0; tail = 0; count = 0; // Capacity management currentCapacity; initialCapacity; maxCapacity; minCapacity; // Auto-resize configuration autoResize; resizeThreshold; shrinkThreshold; resizeFactor; // Resize tracking lastResizeTime = 0; resizeCooldown; resizeCount = 0; logger = createLogger({ module: "DynamicRingBuffer" }); /** * Create a dynamic ring buffer with auto-resize capability * @param initialCapacity Starting capacity * @param options Configuration options */ constructor(initialCapacity, options = {}) { if (initialCapacity <= 0) { throw new Error("Initial capacity must be greater than 0"); } this.initialCapacity = initialCapacity; this.currentCapacity = initialCapacity; this.maxCapacity = options.maxCapacity || Math.max(initialCapacity * 10, 1e4); this.minCapacity = options.minCapacity || Math.max(Math.floor(initialCapacity / 2), 10); this.autoResize = options.autoResize ?? true; this.resizeThreshold = options.resizeThreshold ?? 0.85; this.shrinkThreshold = options.shrinkThreshold ?? 0.3; this.resizeFactor = options.resizeFactor ?? 2; this.resizeCooldown = options.resizeCooldown ?? 5e3; this.buffer = new Array(this.currentCapacity); } /** * Add an item to buffer with auto-resize * If buffer is full, triggers resize or overwrites oldest item * @param item Item to add */ push(item) { this.buffer[this.head] = item; this.head = (this.head + 1) % this.currentCapacity; if (this.count < this.currentCapacity) { this.count++; } else { if (this.autoResize && this.shouldResizeUp()) { this.resizeUp(); } else { this.tail = (this.tail + 1) % this.currentCapacity; } } if (this.autoResize && this.shouldResizeDown()) { this.resizeDown(); } } /** * Check if buffer should expand */ shouldResizeUp() { const now = Date.now(); const timeSinceLastResize = now - this.lastResizeTime; return this.count / this.currentCapacity >= this.resizeThreshold && this.currentCapacity < this.maxCapacity && timeSinceLastResize >= this.resizeCooldown; } /** * Check if buffer should shrink */ shouldResizeDown() { const now = Date.now(); const timeSinceLastResize = now - this.lastResizeTime; return this.count / this.currentCapacity <= this.shrinkThreshold && this.currentCapacity > this.minCapacity && timeSinceLastResize >= this.resizeCooldown; } /** * Expand buffer capacity */ resizeUp() { const newCapacity = Math.min(Math.floor(this.currentCapacity * this.resizeFactor), this.maxCapacity); this.logger.info("Expanding ring buffer", { oldCapacity: this.currentCapacity, newCapacity, itemCount: this.count }); const newBuffer = new Array(newCapacity); for (let i = 0; i < this.count; i++) { newBuffer[i] = this.get(i); } this.buffer = newBuffer; this.currentCapacity = newCapacity; this.head = this.count; this.tail = 0; this.lastResizeTime = Date.now(); this.resizeCount++; } /** * Shrink buffer capacity */ resizeDown() { const newCapacity = Math.max(Math.floor(this.currentCapacity / this.resizeFactor), this.minCapacity); this.logger.info("Shrinking ring buffer", { oldCapacity: this.currentCapacity, newCapacity, itemCount: this.count }); const newBuffer = new Array(newCapacity); for (let i = 0; i < this.count; i++) { newBuffer[i] = this.get(i); } this.buffer = newBuffer; this.currentCapacity = newCapacity; this.head = this.count; this.tail = 0; this.lastResizeTime = Date.now(); this.resizeCount++; } /** * Get all items in insertion order (oldest to newest) * @returns Array of items */ toArray() { if (this.count === 0) { return []; } const result = new Array(this.count); let index = this.tail; for (let i = 0; i < this.count; i++) { result[i] = this.buffer[index]; index = (index + 1) % this.currentCapacity; } return result; } /** * Get a sorted copy of buffer contents * @param compareFn Optional comparison function * @returns Sorted array */ toSortedArray(compareFn) { return this.toArray().sort(compareFn); } /** * Clear all items from buffer */ clear() { this.head = 0; this.tail = 0; this.count = 0; if (this.autoResize && this.currentCapacity !== this.initialCapacity) { this.currentCapacity = this.initialCapacity; this.buffer = new Array(this.currentCapacity); } } /** * Get number of items currently in buffer * @returns Current item count */ get length() { return this.count; } /** * Get current capacity of buffer * @returns Current capacity */ get size() { return this.currentCapacity; } /** * Get initial capacity * @returns Initial capacity */ get initialSize() { return this.initialCapacity; } /** * Get maximum capacity * @returns Maximum capacity */ get maxSize() { return this.maxCapacity; } /** * Get minimum capacity * @returns Minimum capacity */ get minSize() { return this.minCapacity; } /** * Check if buffer is empty * @returns True if empty */ isEmpty() { return this.count === 0; } /** * Check if buffer is full * @returns True if full */ isFull() { return this.count === this.currentCapacity; } /** * Get utilization ratio (0-1) * @returns Utilization ratio */ get utilization() { return this.count / this.currentCapacity; } /** * Get an item at a specific index (0 = oldest, length-1 = newest) * @param index Index to retrieve * @returns Item at index or undefined if out of range */ get(index) { if (index < 0 || index >= this.count) { return void 0; } const actualIndex = (this.tail + index) % this.currentCapacity; return this.buffer[actualIndex]; } /** * Get oldest item without removing it * @returns Oldest item or undefined if buffer is empty */ peek() { if (this.count === 0) { return void 0; } return this.buffer[this.tail]; } /** * Get newest item * @returns Newest item or undefined if buffer is empty */ peekLast() { if (this.count === 0) { return void 0; } const lastIndex = (this.head - 1 + this.currentCapacity) % this.currentCapacity; return this.buffer[lastIndex]; } /** * Calculate percentile from buffer contents (e.g., 0.5 for median, 0.95 for P95) * @param percentile Percentile value between 0 and 1 * @returns Percentile value or undefined if buffer is empty */ getPercentile(percentile) { if (this.count === 0 || percentile < 0 || percentile > 1) { return void 0; } const sorted = this.toSortedArray((a, b) => a - b); const index = Math.floor(sorted.length * percentile); return sorted[Math.min(index, sorted.length - 1)]; } /** * Get average of numeric buffer contents * @returns Average value or undefined if buffer is empty */ getAverage() { if (this.count === 0) { return void 0; } let sum = 0; for (let i = 0; i < this.count; i++) { const value = this.get(i); sum += Number(value) || 0; } return sum / this.count; } /** * Iterate over buffer contents (oldest to newest) * @param callback Function to call for each item */ forEach(callback) { for (let i = 0; i < this.count; i++) { const item = this.get(i); if (item !== void 0) { callback(item, i); } } } /** * Map buffer contents to a new array * @param callback Function to transform each item * @returns New array of transformed items */ map(callback) { const result = new Array(this.count); for (let i = 0; i < this.count; i++) { const item = this.get(i); if (item !== void 0) { result[i] = callback(item, i); } } return result; } /** * Filter buffer contents * @param predicate Function to test each item * @returns New array of items that pass test */ filter(predicate) { const result = []; for (let i = 0; i < this.count; i++) { const item = this.get(i); if (item !== void 0 && predicate(item, i)) { result.push(item); } } return result; } /** * Reduce buffer contents to a single value * @param callback Reducer function * @param initialValue Initial value for accumulator * @returns Reduced value */ reduce(callback, initialValue) { let accumulator = initialValue; for (let i = 0; i < this.count; i++) { const item = this.get(i); if (item !== void 0) { accumulator = callback(accumulator, item, i); } } return accumulator; } /** * Manually trigger resize up * @param factor Optional resize factor override */ resizeUpManual(factor) { const oldFactor = this.resizeFactor; if (factor !== void 0) { this.resizeFactor = factor; } if (this.shouldResizeUp()) { this.resizeUp(); } this.resizeFactor = oldFactor; } /** * Manually trigger resize down * @param factor Optional resize factor override */ resizeDownManual(factor) { const oldFactor = this.resizeFactor; if (factor !== void 0) { this.resizeFactor = factor; } if (this.shouldResizeDown()) { this.resizeDown(); } this.resizeFactor = oldFactor; } /** * Get resize statistics */ getStats() { return { resizeCount: this.resizeCount, lastResizeTime: this.lastResizeTime, currentCapacity: this.currentCapacity, utilization: this.utilization, resizeThreshold: this.resizeThreshold, shrinkThreshold: this.shrinkThreshold }; } }; // src/pools/pool.ts var ConnectionPoolStatus = /* @__PURE__ */ (function(ConnectionPoolStatus2) { ConnectionPoolStatus2["HEALTHY"] = "healthy"; ConnectionPoolStatus2["DEGRADED"] = "degraded"; ConnectionPoolStatus2["OVERLOADED"] = "overloaded"; ConnectionPoolStatus2["UNAVAILABLE"] = "unavailable"; return ConnectionPoolStatus2; })({}); var ConnectionPoolEvent = /* @__PURE__ */ (function(ConnectionPoolEvent2) { ConnectionPoolEvent2["CONNECTION_ADDED"] = "connection_added"; ConnectionPoolEvent2["CONNECTION_REMOVED"] = "connection_removed"; ConnectionPoolEvent2["CONNECTION_TIMEOUT"] = "connection_timeout"; ConnectionPoolEvent2["CONNECTION_ERROR"] = "connection_error"; ConnectionPoolEvent2["POOL_LIMIT_REACHED"] = "pool_limit_reached"; ConnectionPoolEvent2["HEALTH_STATUS_CHANGED"] = "health_status_changed"; return ConnectionPoolEvent2; })({}); var ConnectionPoolManager = class { static { __name(this, "ConnectionPoolManager"); } logger = createLogger({ module: "connection_pool" }); config; protocol; startTime = Date.now(); eventListeners = /* @__PURE__ */ new Map(); eventListenerErrors = /* @__PURE__ */ new Map(); // 连接池核心数据 connections = /* @__PURE__ */ new Map(); connectionMetadata = /* @__PURE__ */ new Map(); waitingQueue = []; // 统计和健康状态 metrics; currentHealth; // 性能监控 - 使用环形缓冲区提高性能 latencyBuffer; errorWindow; lastMetricsUpdate = Date.now(); // 定时器引用 - 用于清理 healthCheckInterval; cleanupInterval; constructor(protocol, config = {}) { this.protocol = protocol; this.config = this.validateAndNormalizeConfig(config); this.logger = createLogger({ module: "connection_pool", protocol: this.protocol }); this.latencyBuffer = new RingBuffer(1e3); this.errorWindow = new RingBuffer(500); this.metrics = this.initializeMetrics(); this.currentHealth = this.initializeHealth(); this.logger.info("Connection pool manager initialized", {}, { protocol: this.protocol, config: this.config }); this.startPeriodicTasks(); } /** * 初始化指标 */ 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: "healthy", utilizationRatio: 0, activeConnections: 0, maxConnections: this.config.maxConnections || 1e3, rejectedConnections: 0, averageResponseTime: 0, errorRate: 0, message: "Connection pool is healthy", lastUpdated: Date.now() }; } /** * Validate and normalize configuration */ validateAndNormalizeConfig(config) { const normalized = { maxConnections: config.maxConnections || 1e3, connectionTimeout: config.connectionTimeout || 3e4, keepAliveTimeout: config.keepAliveTimeout || 5e3, requestTimeout: config.requestTimeout || 3e4, headersTimeout: config.headersTimeout || 1e4, ...config }; if (normalized.maxConnections && normalized.maxConnections <= 0) { throw new Error("maxConnections must be positive"); } if (normalized.connectionTimeout && normalized.connectionTimeout <= 0) { throw new Error("connectionTimeout must be positive"); } return normalized; } /** * 申请连接 */ async requestConnection(options = {}) { const startTime = Date.now(); const timeout = options.timeout || this.config.connectionTimeout || 3e4; try { if (!this.canAcceptConnection()) { this.currentHealth.rejectedConnections++; this.emitEvent("pool_limit_reached", { currentConnections: this.getActiveConnectionCount(), maxConnections: this.config.maxConnections }); return { connection: null, success: false, error: new Error("Connection pool limit reached"), waitTime: Date.now() - startTime }; } const availableConnection = await this.getAvailableConnection(); if (availableConnection) { return { connection: availableConnection.connection, success: true, waitTime: Date.now() - startTime, connectionId: availableConnection.id }; } const newConnection = await this.createNewConnection(options); if (newConnection) { await this.addConnection(newConnection.connection, newConnection.metadata); return { connection: newConnection.connection, success: true, waitTime: Date.now() - startTime, connectionId: newConnection.id }; } return new Promise((resolve2, reject) => { const timeoutHandle = setTimeout(() => { const index = this.waitingQueue.findIndex((item) => item.resolve === resolve2); if (index >= 0) { this.waitingQueue.splice(index, 1); } resolve2({ connection: null, success: false, error: new Error("Connection request timeout"), waitTime: Date.now() - startTime }); }, timeout); const queueItem = { resolve: /* @__PURE__ */ __name((result) => { clearTimeout(timeoutHandle); resolve2(result); }, "resolve"), reject: /* @__PURE__ */ __name((error) => { clearTimeout(timeoutHandle); reject(error); }, "reject"), options, timestamp: startTime }; const priorityWeight = { low: 1, normal: 2, high: 3 }; const newPriority = priorityWeight[options.priority || "normal"]; let insertIndex = this.waitingQueue.length; for (let i = 0; i < this.waitingQueue.length; i++) { const existingPriority = priorityWeight[this.waitingQueue[i].options.priority || "normal"]; if (newPriority > existingPriority) { insertIndex = i; break; } } this.waitingQueue.splice(insertIndex, 0, queueItem); }); } catch (error) { this.recordConnectionEvent("error", { error }); return { connection: null, success: false, error, waitTime: Date.now() - startTime }; } } /** * 释放连接 */ async releaseConnection(connection, options = {}) { const traceId = generateTraceId(); try { const connectionId = this.findConnectionId(connection); if (!connectionId) { this.logger.warn("Attempting to release unknown connection", { traceId }); return false; } if (options.destroy || options.error) { await this.removeConnection(connection, options.error?.message || "Explicitly destroyed"); this.logger.debug("Connection destroyed", { traceId }, { connectionId }); } else { this.markConnectionAvailable(connectionId); this.logger.debug("Connection released and marked available", { traceId }, { connectionId }); } await this.processWaitingQueue(); return true; } catch (error) { this.logger.error("Failed to release connection", { traceId }, error); return false; } } /** * Add connection to the pool */ async addConnection(connection, metadata = {}) { const connectionId = this.generateConnectionId(); try { if (!this.validateConnection(connection)) { throw new Error("Invalid connection"); } this.connections.set(connectionId, connection); this.connectionMetadata.set(connectionId, { ...metadata, id: connectionId, createdAt: Date.now(), lastUsed: Date.now(), available: true }); this.recordConnectionEvent("added", { connectionId, metadata }); this.emitEvent("connection_added", { connectionId, connection }); this.logger.debug("Connection added to pool", {}, { connectionId }); return true; } catch (error) { this.logger.error("Failed to add connection to pool", {}, error); return false; } } /** * Remove connection from pool */ async removeConnection(connection, reason) { const connectionId = this.findConnectionId(connection); if (!connectionId) return; try { await this.cleanupConnection(connection); this.connections.delete(connectionId); this.connectionMetadata.delete(connectionId); this.recordConnectionEvent("removed", { connectionId, reason }); this.emitEvent("connection_removed", { connectionId, reason }); this.logger.debug("Connection removed from pool", {}, { connectionId, reason }); } catch (error) { this.logger.error("Error removing connection from pool", {}, error); } } /** * Get active connection count */ getActiveConnectionCount() { return this.connections.size; } /** * Close all connections */ async closeAllConnections(timeout = 5e3) { const traceId = generateTraceId(); this.logger.info("Closing all connections", { traceId }, { activeConnections: this.connections.size }); const closePromises = []; for (const [connectionId, connection] of this.connections) { closePromises.push(this.removeConnection(connection, "Pool shutdown").catch((error) => { this.logger.error("Error closing connection", { traceId }, { connectionId, error }); })); } try { await Promise.race([ Promise.all(closePromises), new Promise((_, reject) => setTimeout(() => reject(new Error("Close timeout")), timeout)) ]); } catch (error) { this.logger.warn("Some connections failed to close gracefully", { traceId }, error); } this.connections.clear(); this.connectionMetadata.clear(); this.logger.info("All connections closed", { traceId }); } /** * 创建新连接 */ async createNewConnection(options) { const result = await this.createProtocolConnection(options); if (result) { return { connection: result.connection, id: this.generateConnectionId(), metadata: result.metadata }; } return null; } /** * Check if new connections can be accepted */ canAcceptConnection() { const maxConnections = this.config.maxConnections; if (!maxConnections) return true; const currentConnections = this.getActiveConnectionCount(); return currentConnections < maxConnections; } /** * Update connection pool health status */ updateHealthStatus() { const activeConnections = this.getActiveConnectionCount(); const maxConnections = this.config.maxConnections || Infinity; const utilizationRatio = maxConnections === Infinity ? 0 : activeConnections / maxConnections; let status = "healthy"; let message = "Connection pool is healthy"; if (utilizationRatio > 0.95) { status = "overloaded"; message = "Connection pool is overloaded"; } else if (utilizationRatio > 0.8) { status = "degraded"; message = "Connection pool is under high load"; } const oldStatus = this.currentHealth.status; this.currentHealth = { ...this.currentHealth, status, utilizationRatio, activeConnections, maxConnections: maxConnections === Infinity ? 0 : maxConnections, message, lastUpdated: Date.now() }; this.metrics.health = this.currentHealth; if (oldStatus !== status) { this.emitEvent("health_status_changed", { oldStatus, newStatus: status, health: this.currentHealth }); } } /** * Get connection pool health status */ getHealth() { this.updateHealthStatus(); return { ...this.currentHealth }; } /** * Get connection pool metrics */ getMetrics() { const uptime = Date.now() - this.startTime; this.updatePerformanceMetrics(); return { ...this.metrics, uptime, health: this.getHealth(), activeConnections: this.getActiveConnectionCount() }; } /** * Get connection pool configuration */ getConfig() { return { ...this.config }; } /** * Update connection pool configuration */ async updateConfig(newConfig) { const traceId = generateTraceId(); try { this.logger.info("Updating connection pool configuration", { traceId }, { oldConfig: this.config, newConfig }); const updatedConfig = this.validateAndNormalizeConfig({ ...this.config, ...newConfig }); Object.assign(this.config, updatedConfig);