UNPKG

koatty_serve

Version:

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

347 lines (280 loc) 10.6 kB
## 相关知识 ### [experience] 经验: 问题:strictFunctionTypes 要求装饰器参数类型完全匹配 → 解 **解决方案**: 问题:strictFunctionTypes 要求装饰器参数类型完全匹配 → 解决:使用 object 替代 Function,使用 string | symbol 处理 propertyKey,符合 TypeScript 标准签名 ### [experience] 经验: 问题:测试类定义在函数内部时 TypeScript 不生成 design:par **解决方案**: 问题:测试类定义在函数内部时 TypeScript 不生成 design:paramtypes → 解决:使用 container.savePropertyData 手动设置依赖元数据 ### [experience] 经验: 决策:渐进式开启 TypeScript strict mode → 原因:分三步 **解决方案**: 决策:渐进式开启 TypeScript strict mode → 原因:分三步(strictNullChecks → strictPropertyInitialization+noImplicitThis → strict: true),逐步修复错误,降低风险 ## 可能相关 - [experience] 经验: 经验:教训:Prototype scope Lazy Proxy 每次访问构造新 - [problem] 经验: 问题:load_json 只捕获 json.JSONDecodeError 和 --- ## 项目代码模式检索 ### 1. ConnectionPoolManager 定时器管理的最佳实践 **模式来源**: `src/utils/timer-manager.ts`, `src/pools/pool.ts` **关键实现**: ```typescript // TimerManager 统一管理所有定时器,避免资源泄漏 export class TimerManager { private timers: Map<string, TimerInfo> = new Map(); private taskQueues: Map<TimerFrequency, TimerTask[]> = new Map(); // 定时器频率层次 enum TimerFrequency { HIGH = 5000, // 5秒 - 关键健康检查 MEDIUM = 30000, // 30秒 - 连接清理、ping检查 LOW = 60000 // 60秒 - 深度健康检查、统计更新 } // 添加优化定时器 - 合并相同频率的定时器 addOptimizedTimer(task: TimerTask): string { const frequency = this.selectOptimalFrequency(task.interval); // 按优先级排序执行 tasks.sort((a, b) => priorityOrder[b.priority] - priorityOrder[a.priority]); } // 销毁时清理所有定时器 destroy(): void { this.clearConsolidatedTimers(); this.taskQueues.clear(); this.clearAllTimers(); } } ``` **最佳实践**: 1. 使用 `TimerManager` 统一管理所有定时器,而非直接使用 `setInterval/setTimeout` 2. 定时器使用 `unref()` 确保不阻止进程退出 3. 按频率分层合并定时器,减少物理定时器数量 4. 在 `destroy()` 中清理所有定时器,避免内存泄漏 5. 定时器回调使用 try-catch 包装,防止异常影响其他任务 **使用示例** (`src/server/base.ts:85`): ```typescript constructor(protected app: KoattyApplication, options: T) { this.timerManager = new TimerManager(); } protected setupPeriodicCleanup(): void { this.timerManager.addTimer('base_cleanup', () => { // 清理逻辑 }, 30000); } ``` --- ### 2. TypeScript 构造函数初始化时序问题 **模式来源**: `src/server/base.ts`, `src/pools/pool.ts` **关键原则**: 1. **构造函数中的初始化顺序**: ```typescript // src/server/base.ts:74-105 constructor(protected app: KoattyApplication, options: T) { // Step 1: 深拷贝 options,确保 ext 对象也被正确复制 this.options = { ...options, ext: options.ext ? { ...options.ext } : undefined }; // Step 2: 初始化基本属性 this.protocol = options.protocol; this.status = 0; this.serverId = generateServerId(options.protocol); // Step 3: 初始化定时器管理器(在其他依赖之前) this.timerManager = new TimerManager(); // Step 4: 设置日志上下文 this.logger = createLogger({ module: actualModule, protocol: options.protocol }); // Step 5: 最后调用模板方法初始化 this.initializeServer(); // 此方法内部会使用已初始化的属性 } ``` 2. **使用 Definitely Assigned Assertion (`!`) 处理延迟初始化**: ```typescript // src/server/ws.ts:26 protected connectionPool!: WebSocketConnectionPoolManager; // 在 initializeConnectionPool() 中初始化 protected initializeConnectionPool(): void { this.connectionPool = new WebSocketConnectionPoolManager(this.options.connectionPool); } ``` 3. **抽象方法与模板方法模式**: ```typescript // 基类定义初始化流程 protected initializeServer(): void { this.initializeConnectionPool(); // 子类实现 this.createProtocolServer(); // 子类实现 this.configureServerOptions(); // 子类实现 this.setupConnectionPoolEventListeners(); // 公共逻辑 this.setupPeriodicCleanup(); // 公共逻辑 this.performProtocolSpecificInitialization(); // 子类可选实现 } // 子类只需实现具体步骤 protected abstract initializeConnectionPool(): void; protected abstract createProtocolServer(): void; ``` **注意事项**: - 避免在构造函数中调用可能被子类重写的方法(除非是模板方法模式) - 使用 `!` 断言明确表示属性会在构造后立即初始化 - 深拷贝复杂配置对象,防止外部修改影响内部状态 --- ### 3. Node.js 优雅关闭模式 **模式来源**: `src/server/base.ts:192-314` **完整关闭流程**: ```typescript async gracefulShutdown(options: GracefulShutdownOptions = {}): Promise<ShutdownResult> { if (this.isShuttingDown) { throw new Error('Graceful shutdown already in progress'); } this.isShuttingDown = true; try { // Step 1: 停止接受新连接 await this.stopAcceptingNewConnections(traceId); // Step 2: 等待现有连接完成 await this.waitForConnectionCompletion(waitTimeout, traceId); // Step 3: 强制关闭剩余连接 await this.forceCloseRemainingConnections(traceId); // Step 4: 停止监控和清理 this.stopMonitoringAndCleanup(traceId); this.timerManager.destroy(); // 清理定时器 return { status: 'completed', ... }; } catch (error) { return { status: 'failed', ... }; } finally { this.isShuttingDown = false; } } ``` **带超时的异步操作执行**: ```typescript // src/server/base.ts:319-339 private async executeWithTimeout<T>( fn: () => Promise<T>, timeout: number, stepName: string ): Promise<T> { return new Promise<T>((resolve, reject) => { const timer = setTimeout(() => { reject(new Error(`Step '${stepName}' timed out after ${timeout}ms`)); }, timeout); fn() .then(result => { clearTimeout(timer); resolve(result); }) .catch(error => { clearTimeout(timer); reject(error); }); }); } ``` **连接池关闭模式** (`src/pools/pool.ts:420-450`): ```typescript async closeAllConnections(timeout: number = 5000): Promise<void> { const closePromises: Promise<void>[] = []; for (const [connectionId, connection] of this.connections) { closePromises.push( this.removeConnection(connection, 'Pool shutdown').catch(error => { this.logger.error('Error closing connection', {}, { connectionId, error }); }) ); } await Promise.race([ Promise.all(closePromises), new Promise((_, reject) => setTimeout(() => reject(new Error('Close timeout')), timeout) ) ]); this.connections.clear(); this.connectionMetadata.clear(); } ``` **优雅关闭结果类型**: ```typescript interface ShutdownResult { status: 'completed' | 'failed' | 'forced'; totalTime: number; completedSteps: string[]; failedSteps: Array<{ step: string; error: string; timestamp: number; }>; } ``` --- ### 4. 连接池缓存键稳定性 **模式来源**: `src/pools/pool.ts`, `src/pools/factory.ts` **连接ID生成**: ```typescript // src/pools/pool.ts:622-624 protected generateConnectionId(): string { return `${this.protocol}_${Date.now()}_${Math.random().toString(36).substr(2, 8)}`; } ``` **连接查找**: ```typescript // src/pools/pool.ts:626-631 protected findConnectionId(connection: T): string | null { for (const [id, conn] of this.connections) { if (conn === connection) return id; } return null; } ``` **连接池工厂缓存** (`src/pools/factory.ts:15-86`): ```typescript export class ConnectionPoolFactory { private static instances = new Map<string, ConnectionPoolManager>(); private static registered = new Map<string, new (config: ConnectionPoolConfig) => ConnectionPoolManager>(); // 注册协议实现 static register<T extends ConnectionPoolManager>( protocol: string, implementation: new (config: ConnectionPoolConfig) => T ): void { this.registered.set(protocol.toLowerCase(), implementation); } // 获取或创建实例(单例模式) static getOrCreate(protocol: string, config: ConnectionPoolConfig = {}): ConnectionPoolManager { const normalizedProtocol = protocol.toLowerCase(); if (this.instances.has(normalizedProtocol)) { return this.instances.get(normalizedProtocol)!; } const instance = this.create(normalizedProtocol, config); this.instances.set(normalizedProtocol, instance); return instance; } // 销毁特定协议的连接池 static async destroy(protocol: string): Promise<void> { const normalizedProtocol = protocol.toLowerCase(); const instance = this.instances.get(normalizedProtocol); if (instance) { await instance.destroy(); this.instances.delete(normalizedProtocol); } } } ``` **键稳定性最佳实践**: 1. 使用 `protocol.toLowerCase()` 规范化协议名称 2. 连接ID包含:协议名 + 时间戳 + 随机字符串,确保唯一性 3. 使用 Map 而非 Object 存储连接,避免原型链问题 4. 连接元数据与连接分开存储,便于管理 **连接元数据结构**: ```typescript // src/pools/pool.ts:365-372 this.connectionMetadata.set(connectionId, { ...metadata, id: connectionId, createdAt: Date.now(), lastUsed: Date.now(), available: true }); ``` --- ## 总结 ### 关键文件引用 - 定时器管理: `src/utils/timer-manager.ts` - 连接池基类: `src/pools/pool.ts` - 连接池工厂: `src/pools/factory.ts` - 服务器基类: `src/server/base.ts` - gRPC服务器: `src/server/grpc.ts` - WebSocket服务器: `src/server/ws.ts` ### 核心设计原则 1. **统一资源管理**: 使用 `TimerManager` 管理所有定时器 2. **模板方法模式**: 基类定义流程骨架,子类实现具体步骤 3. **优雅关闭**: 分步骤执行,每步带超时保护 4. **键稳定性**: 规范化键名,使用唯一ID,分离存储 .knowledge-context.md 已写入,coder 可读取相关历史经验。