koatty_serve
Version:
Provide http1/2/3, websocket, gRPC server for Koatty.
347 lines (280 loc) • 10.6 kB
Markdown
## 相关知识
### [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 可读取相关历史经验。