/** * 心跳管理器 * 负责管理网络连接的心跳检测,包括延迟测算和连接健康检测 */ import { createLogger } from '@esengine/ecs-framework'; import { MessageType } from '../types/NetworkTypes'; /** * 心跳配置 */ export interface HeartbeatConfig { interval: number; // 心跳间隔(毫秒) timeout: number; // 心跳超时(毫秒) maxMissedHeartbeats: number; // 最大丢失心跳数 enableLatencyMeasurement: boolean; // 是否启用延迟测量 } /** * 心跳状态 */ export interface HeartbeatStatus { isHealthy: boolean; lastHeartbeat: number; latency?: number; missedHeartbeats: number; averageLatency?: number; packetLoss?: number; } /** * 心跳事件接口 */ export interface HeartbeatEvents { heartbeatSent: (timestamp: number) => void; heartbeatReceived: (latency: number) => void; heartbeatTimeout: (missedCount: number) => void; healthStatusChanged: (isHealthy: boolean) => void; } /** * 心跳消息接口 */ export interface HeartbeatMessage { type: MessageType.HEARTBEAT; clientTime: number; serverTime?: number; sequence?: number; } /** * 心跳管理器 */ export class HeartbeatManager { private logger = createLogger('HeartbeatManager'); private config: HeartbeatConfig; private status: HeartbeatStatus; private eventHandlers: Partial = {}; // 定时器 private heartbeatTimer?: number; private timeoutTimer?: number; // 延迟测量 private pendingPings: Map = new Map(); private latencyHistory: number[] = []; private sequence = 0; // 统计信息 private sentCount = 0; private receivedCount = 0; /** * 发送心跳回调 */ private sendHeartbeat?: (message: HeartbeatMessage) => void; /** * 构造函数 */ constructor(config: Partial = {}) { this.config = { interval: 30000, // 30秒 timeout: 60000, // 60秒 maxMissedHeartbeats: 3, // 最大丢失3次 enableLatencyMeasurement: true, ...config }; this.status = { isHealthy: true, lastHeartbeat: Date.now(), missedHeartbeats: 0 }; } /** * 启动心跳 */ start(sendCallback: (message: HeartbeatMessage) => void): void { this.sendHeartbeat = sendCallback; this.startHeartbeatTimer(); this.logger.info('心跳管理器已启动'); } /** * 停止心跳 */ stop(): void { this.stopHeartbeatTimer(); this.stopTimeoutTimer(); this.pendingPings.clear(); this.logger.info('心跳管理器已停止'); } /** * 处理接收到的心跳响应 */ handleHeartbeatResponse(message: HeartbeatMessage): void { const now = Date.now(); this.status.lastHeartbeat = now; this.receivedCount++; // 重置丢失心跳计数 this.status.missedHeartbeats = 0; // 计算延迟 if (this.config.enableLatencyMeasurement && message.sequence !== undefined) { const sentTime = this.pendingPings.get(message.sequence); if (sentTime) { const latency = now - sentTime; this.updateLatency(latency); this.pendingPings.delete(message.sequence); this.eventHandlers.heartbeatReceived?.(latency); } } // 更新健康状态 this.updateHealthStatus(true); // 停止超时定时器 this.stopTimeoutTimer(); } /** * 处理心跳超时 */ handleHeartbeatTimeout(): void { this.status.missedHeartbeats++; this.logger.warn(`心跳超时,丢失次数: ${this.status.missedHeartbeats}`); // 触发超时事件 this.eventHandlers.heartbeatTimeout?.(this.status.missedHeartbeats); // 检查是否达到最大丢失次数 if (this.status.missedHeartbeats >= this.config.maxMissedHeartbeats) { this.updateHealthStatus(false); } } /** * 获取心跳状态 */ getStatus(): HeartbeatStatus { return { ...this.status }; } /** * 获取统计信息 */ getStats() { const packetLoss = this.sentCount > 0 ? ((this.sentCount - this.receivedCount) / this.sentCount) * 100 : 0; return { sentCount: this.sentCount, receivedCount: this.receivedCount, packetLoss, averageLatency: this.status.averageLatency, currentLatency: this.status.latency, isHealthy: this.status.isHealthy, missedHeartbeats: this.status.missedHeartbeats, latencyHistory: [...this.latencyHistory] }; } /** * 设置事件处理器 */ on(event: K, handler: HeartbeatEvents[K]): void { this.eventHandlers[event] = handler; } /** * 移除事件处理器 */ off(event: K): void { delete this.eventHandlers[event]; } /** * 手动发送心跳 */ sendHeartbeatNow(): void { this.doSendHeartbeat(); } /** * 更新配置 */ updateConfig(newConfig: Partial): void { Object.assign(this.config, newConfig); this.logger.info('心跳配置已更新:', newConfig); // 重启定时器以应用新配置 if (this.heartbeatTimer) { this.stop(); if (this.sendHeartbeat) { this.start(this.sendHeartbeat); } } } /** * 启动心跳定时器 */ private startHeartbeatTimer(): void { this.heartbeatTimer = window.setInterval(() => { this.doSendHeartbeat(); }, this.config.interval); } /** * 停止心跳定时器 */ private stopHeartbeatTimer(): void { if (this.heartbeatTimer) { clearInterval(this.heartbeatTimer); this.heartbeatTimer = undefined; } } /** * 启动超时定时器 */ private startTimeoutTimer(): void { this.timeoutTimer = window.setTimeout(() => { this.handleHeartbeatTimeout(); }, this.config.timeout); } /** * 停止超时定时器 */ private stopTimeoutTimer(): void { if (this.timeoutTimer) { clearTimeout(this.timeoutTimer); this.timeoutTimer = undefined; } } /** * 执行发送心跳 */ private doSendHeartbeat(): void { if (!this.sendHeartbeat) { this.logger.error('心跳发送回调未设置'); return; } const now = Date.now(); const sequence = this.config.enableLatencyMeasurement ? ++this.sequence : undefined; const message: HeartbeatMessage = { type: MessageType.HEARTBEAT, clientTime: now, sequence }; try { this.sendHeartbeat(message); this.sentCount++; // 记录发送时间用于延迟计算 if (sequence !== undefined) { this.pendingPings.set(sequence, now); // 清理过期的pending pings this.cleanupPendingPings(); } // 启动超时定时器 this.stopTimeoutTimer(); this.startTimeoutTimer(); this.eventHandlers.heartbeatSent?.(now); } catch (error) { this.logger.error('发送心跳失败:', error); } } /** * 更新延迟信息 */ private updateLatency(latency: number): void { this.status.latency = latency; // 保存延迟历史(最多100个样本) this.latencyHistory.push(latency); if (this.latencyHistory.length > 100) { this.latencyHistory.shift(); } // 计算平均延迟 this.status.averageLatency = this.latencyHistory.reduce((sum, lat) => sum + lat, 0) / this.latencyHistory.length; this.logger.debug(`延迟更新: ${latency}ms, 平均: ${this.status.averageLatency?.toFixed(1)}ms`); } /** * 更新健康状态 */ private updateHealthStatus(isHealthy: boolean): void { if (this.status.isHealthy !== isHealthy) { this.status.isHealthy = isHealthy; this.logger.info(`连接健康状态变更: ${isHealthy ? '健康' : '不健康'}`); this.eventHandlers.healthStatusChanged?.(isHealthy); } } /** * 清理过期的pending pings */ private cleanupPendingPings(): void { const now = Date.now(); const timeout = this.config.timeout * 2; // 清理超过2倍超时时间的记录 for (const [sequence, sentTime] of this.pendingPings) { if (now - sentTime > timeout) { this.pendingPings.delete(sequence); } } } /** * 重置统计信息 */ resetStats(): void { this.sentCount = 0; this.receivedCount = 0; this.latencyHistory.length = 0; this.status.averageLatency = undefined; this.status.latency = undefined; this.status.missedHeartbeats = 0; this.pendingPings.clear(); this.logger.info('心跳统计信息已重置'); } /** * 检查连接是否健康 */ isConnectionHealthy(): boolean { const now = Date.now(); const timeSinceLastHeartbeat = now - this.status.lastHeartbeat; return this.status.isHealthy && timeSinceLastHeartbeat <= this.config.timeout && this.status.missedHeartbeats < this.config.maxMissedHeartbeats; } /** * 获取建议的重连延迟 */ getReconnectDelay(): number { // 基于丢失心跳次数计算重连延迟 const baseDelay = this.config.interval; const multiplier = Math.min(Math.pow(2, this.status.missedHeartbeats), 8); return baseDelay * multiplier; } }