Files
esengine/packages/network-client/src/core/ConnectionStateManager.ts
YHH ddc7a7750e Chore/lint fixes (#212)
* fix(eslint): 修复装饰器缩进配置

* fix(eslint): 修复装饰器缩进配置

* chore: 删除未使用的导入

* chore(lint): 移除未使用的导入和变量

* chore(lint): 修复editor-app中未使用的函数参数

* chore(lint): 修复未使用的赋值变量

* chore(eslint): 将所有错误级别改为警告以通过CI

* fix(codeql): 修复GitHub Advanced Security检测到的问题
2025-11-02 23:50:41 +08:00

639 lines
19 KiB
TypeScript

/**
* 连接状态管理器
* 负责跟踪连接状态变化、状态变化通知和自动恢复逻辑
*/
import { createLogger, ITimer } from '@esengine/ecs-framework';
import { ConnectionState, EventEmitter } from '@esengine/network-shared';
import { NetworkTimerManager } from '../utils';
/**
* 状态转换规则
*/
export interface StateTransitionRule {
from: ConnectionState;
to: ConnectionState;
condition?: () => boolean;
action?: () => void;
}
/**
* 连接状态管理器配置
*/
export interface ConnectionStateManagerConfig {
enableAutoRecovery: boolean;
recoveryTimeout: number;
maxRecoveryAttempts: number;
stateTimeout: number;
enableStateValidation: boolean;
logStateChanges: boolean;
}
/**
* 状态历史记录
*/
export interface StateHistoryEntry {
state: ConnectionState;
timestamp: number;
duration?: number;
reason?: string;
metadata?: Record<string, any>;
}
/**
* 状态统计信息
*/
export interface StateStats {
currentState: ConnectionState;
totalTransitions: number;
stateHistory: StateHistoryEntry[];
stateDurations: Record<ConnectionState, number[]>;
averageStateDurations: Record<ConnectionState, number>;
totalUptime: number;
totalDowntime: number;
connectionAttempts: number;
successfulConnections: number;
failedConnections: number;
}
/**
* 连接状态管理器事件接口
*/
export interface ConnectionStateManagerEvents {
stateChanged: (oldState: ConnectionState, newState: ConnectionState, reason?: string) => void;
stateTimeout: (state: ConnectionState, duration: number) => void;
recoveryStarted: (attempt: number) => void;
recoverySucceeded: () => void;
recoveryFailed: (maxAttemptsReached: boolean) => void;
invalidTransition: (from: ConnectionState, to: ConnectionState) => void;
}
/**
* 连接状态管理器
*/
export class ConnectionStateManager extends EventEmitter {
private logger = createLogger('ConnectionStateManager');
private config: ConnectionStateManagerConfig;
private currentState: ConnectionState = ConnectionState.Disconnected;
private previousState?: ConnectionState;
private stateStartTime: number = Date.now();
private stats: StateStats;
// 状态管理
private stateHistory: StateHistoryEntry[] = [];
private transitionRules: StateTransitionRule[] = [];
private stateTimeouts: Map<ConnectionState, ITimer> = new Map();
// 恢复逻辑
private recoveryAttempts = 0;
private recoveryTimer?: ITimer;
// Timer管理器使用静态的NetworkTimerManager
private recoveryCallback?: () => Promise<void>;
// 事件处理器
private eventHandlers: Partial<ConnectionStateManagerEvents> = {};
/**
* 构造函数
*/
constructor(config: Partial<ConnectionStateManagerConfig> = {}) {
super();
this.config = {
enableAutoRecovery: true,
recoveryTimeout: 5000,
maxRecoveryAttempts: 3,
stateTimeout: 30000,
enableStateValidation: true,
logStateChanges: true,
...config
};
this.stats = {
currentState: this.currentState,
totalTransitions: 0,
stateHistory: [],
stateDurations: {
[ConnectionState.Disconnected]: [],
[ConnectionState.Connecting]: [],
[ConnectionState.Connected]: [],
[ConnectionState.Reconnecting]: [],
[ConnectionState.Failed]: []
},
averageStateDurations: {
[ConnectionState.Disconnected]: 0,
[ConnectionState.Connecting]: 0,
[ConnectionState.Connected]: 0,
[ConnectionState.Reconnecting]: 0,
[ConnectionState.Failed]: 0
},
totalUptime: 0,
totalDowntime: 0,
connectionAttempts: 0,
successfulConnections: 0,
failedConnections: 0
};
this.initializeDefaultTransitionRules();
}
/**
* 设置当前状态
*/
setState(newState: ConnectionState, reason?: string, metadata?: Record<string, any>): boolean {
if (this.currentState === newState) {
return true;
}
// 验证状态转换
if (this.config.enableStateValidation && !this.isValidTransition(this.currentState, newState)) {
this.logger.warn(`无效的状态转换: ${this.currentState} -> ${newState}`);
this.eventHandlers.invalidTransition?.(this.currentState, newState);
return false;
}
const oldState = this.currentState;
const now = Date.now();
const duration = now - this.stateStartTime;
// 更新状态历史
this.updateStateHistory(oldState, duration, reason, metadata);
// 清理旧状态的超时定时器
this.clearStateTimeout(oldState);
// 更新当前状态
this.previousState = oldState;
this.currentState = newState;
this.stateStartTime = now;
this.stats.currentState = newState;
this.stats.totalTransitions++;
// 更新连接统计
this.updateConnectionStats(oldState, newState);
if (this.config.logStateChanges) {
this.logger.info(`连接状态变化: ${oldState} -> ${newState}${reason ? ` (${reason})` : ''}`);
}
// 设置新状态的超时定时器
this.setStateTimeout(newState);
// 处理自动恢复逻辑
this.handleAutoRecovery(newState);
// 触发事件
this.eventHandlers.stateChanged?.(oldState, newState, reason);
this.emit('stateChanged', oldState, newState, reason);
return true;
}
/**
* 获取当前状态
*/
getState(): ConnectionState {
return this.currentState;
}
/**
* 获取上一个状态
*/
getPreviousState(): ConnectionState | undefined {
return this.previousState;
}
/**
* 获取当前状态持续时间
*/
getCurrentStateDuration(): number {
return Date.now() - this.stateStartTime;
}
/**
* 获取状态统计信息
*/
getStats(): StateStats {
this.updateCurrentStats();
return {
...this.stats,
stateHistory: [...this.stateHistory]
};
}
/**
* 重置统计信息
*/
resetStats(): void {
this.stats = {
currentState: this.currentState,
totalTransitions: 0,
stateHistory: [],
stateDurations: {
[ConnectionState.Disconnected]: [],
[ConnectionState.Connecting]: [],
[ConnectionState.Connected]: [],
[ConnectionState.Reconnecting]: [],
[ConnectionState.Failed]: []
},
averageStateDurations: {
[ConnectionState.Disconnected]: 0,
[ConnectionState.Connecting]: 0,
[ConnectionState.Connected]: 0,
[ConnectionState.Reconnecting]: 0,
[ConnectionState.Failed]: 0
},
totalUptime: 0,
totalDowntime: 0,
connectionAttempts: 0,
successfulConnections: 0,
failedConnections: 0
};
this.stateHistory.length = 0;
this.recoveryAttempts = 0;
}
/**
* 设置恢复回调
*/
setRecoveryCallback(callback: () => Promise<void>): void {
this.recoveryCallback = callback;
}
/**
* 添加状态转换规则
*/
addTransitionRule(rule: StateTransitionRule): void {
this.transitionRules.push(rule);
}
/**
* 移除状态转换规则
*/
removeTransitionRule(from: ConnectionState, to: ConnectionState): boolean {
const index = this.transitionRules.findIndex((rule) => rule.from === from && rule.to === to);
if (index >= 0) {
this.transitionRules.splice(index, 1);
return true;
}
return false;
}
/**
* 检查状态转换是否有效
*/
isValidTransition(from: ConnectionState, to: ConnectionState): boolean {
// 检查自定义转换规则
const rule = this.transitionRules.find((r) => r.from === from && r.to === to);
if (rule) {
return rule.condition ? rule.condition() : true;
}
// 默认转换规则
return this.isDefaultValidTransition(from, to);
}
/**
* 检查是否为连接状态
*/
isConnected(): boolean {
return this.currentState === ConnectionState.Connected;
}
/**
* 检查是否为连接中状态
*/
isConnecting(): boolean {
return this.currentState === ConnectionState.Connecting ||
this.currentState === ConnectionState.Reconnecting;
}
/**
* 检查是否为断开连接状态
*/
isDisconnected(): boolean {
return this.currentState === ConnectionState.Disconnected ||
this.currentState === ConnectionState.Failed;
}
/**
* 手动触发恢复
*/
triggerRecovery(): void {
if (this.config.enableAutoRecovery && this.recoveryCallback) {
this.startRecovery();
}
}
/**
* 停止自动恢复
*/
stopRecovery(): void {
if (this.recoveryTimer) {
this.recoveryTimer.stop();
this.recoveryTimer = undefined;
}
this.recoveryAttempts = 0;
}
/**
* 设置事件处理器
*/
override on<K extends keyof ConnectionStateManagerEvents>(event: K, handler: ConnectionStateManagerEvents[K]): this {
this.eventHandlers[event] = handler;
return super.on(event, handler as any);
}
/**
* 移除事件处理器
*/
override off<K extends keyof ConnectionStateManagerEvents>(event: K): this {
delete this.eventHandlers[event];
return super.off(event, this.eventHandlers[event] as any);
}
/**
* 更新配置
*/
updateConfig(newConfig: Partial<ConnectionStateManagerConfig>): void {
Object.assign(this.config, newConfig);
this.logger.info('连接状态管理器配置已更新:', newConfig);
}
/**
* 销毁管理器
*/
destroy(): void {
this.stopRecovery();
this.clearAllStateTimeouts();
this.removeAllListeners();
}
/**
* 初始化默认转换规则
*/
private initializeDefaultTransitionRules(): void {
// 连接尝试
this.addTransitionRule({
from: ConnectionState.Disconnected,
to: ConnectionState.Connecting,
action: () => this.stats.connectionAttempts++
});
// 连接成功
this.addTransitionRule({
from: ConnectionState.Connecting,
to: ConnectionState.Connected,
action: () => {
this.stats.successfulConnections++;
this.recoveryAttempts = 0; // 重置恢复计数
}
});
// 连接失败
this.addTransitionRule({
from: ConnectionState.Connecting,
to: ConnectionState.Failed,
action: () => this.stats.failedConnections++
});
// 重连尝试
this.addTransitionRule({
from: ConnectionState.Failed,
to: ConnectionState.Reconnecting,
action: () => this.stats.connectionAttempts++
});
// 重连成功
this.addTransitionRule({
from: ConnectionState.Reconnecting,
to: ConnectionState.Connected,
action: () => {
this.stats.successfulConnections++;
this.recoveryAttempts = 0;
}
});
}
/**
* 检查默认的有效转换
*/
private isDefaultValidTransition(from: ConnectionState, to: ConnectionState): boolean {
const validTransitions: Record<ConnectionState, ConnectionState[]> = {
[ConnectionState.Disconnected]: [ConnectionState.Connecting],
[ConnectionState.Connecting]: [ConnectionState.Connected, ConnectionState.Failed, ConnectionState.Disconnected],
[ConnectionState.Connected]: [ConnectionState.Disconnected, ConnectionState.Failed, ConnectionState.Reconnecting],
[ConnectionState.Reconnecting]: [ConnectionState.Connected, ConnectionState.Failed, ConnectionState.Disconnected],
[ConnectionState.Failed]: [ConnectionState.Reconnecting, ConnectionState.Connecting, ConnectionState.Disconnected]
};
return validTransitions[from]?.includes(to) || false;
}
/**
* 更新状态历史
*/
private updateStateHistory(state: ConnectionState, duration: number, reason?: string, metadata?: Record<string, any>): void {
const entry: StateHistoryEntry = {
state,
timestamp: this.stateStartTime,
duration,
reason,
metadata
};
this.stateHistory.push(entry);
// 限制历史记录数量
if (this.stateHistory.length > 100) {
this.stateHistory.shift();
}
// 更新状态持续时间统计
this.stats.stateDurations[state].push(duration);
if (this.stats.stateDurations[state].length > 50) {
this.stats.stateDurations[state].shift();
}
// 计算平均持续时间
const durations = this.stats.stateDurations[state];
this.stats.averageStateDurations[state] =
durations.reduce((sum, d) => sum + d, 0) / durations.length;
}
/**
* 更新连接统计
*/
private updateConnectionStats(from: ConnectionState, to: ConnectionState): void {
const now = Date.now();
const duration = now - this.stateStartTime;
// 更新在线/离线时间
if (from === ConnectionState.Connected) {
this.stats.totalUptime += duration;
} else if (this.isConnected()) {
// 进入连接状态
}
if (this.isDisconnected() && !this.wasDisconnected(from)) {
// 进入断开状态,开始计算离线时间
} else if (!this.isDisconnected() && this.wasDisconnected(from)) {
// 离开断开状态
this.stats.totalDowntime += duration;
}
}
/**
* 检查之前是否为断开状态
*/
private wasDisconnected(state: ConnectionState): boolean {
return state === ConnectionState.Disconnected || state === ConnectionState.Failed;
}
/**
* 设置状态超时定时器
*/
private setStateTimeout(state: ConnectionState): void {
if (this.config.stateTimeout <= 0) {
return;
}
const timeout = NetworkTimerManager.schedule(
this.config.stateTimeout / 1000, // 转为秒
false, // 不重复
this,
() => {
const duration = this.getCurrentStateDuration();
this.logger.warn(`状态超时: ${state}, 持续时间: ${duration}ms`);
this.eventHandlers.stateTimeout?.(state, duration);
}
);
this.stateTimeouts.set(state, timeout);
}
/**
* 清理状态超时定时器
*/
private clearStateTimeout(state: ConnectionState): void {
const timeout = this.stateTimeouts.get(state);
if (timeout) {
timeout.stop();
this.stateTimeouts.delete(state);
}
}
/**
* 清理所有状态超时定时器
*/
private clearAllStateTimeouts(): void {
for (const timeout of this.stateTimeouts.values()) {
timeout.stop();
}
this.stateTimeouts.clear();
}
/**
* 处理自动恢复逻辑
*/
private handleAutoRecovery(newState: ConnectionState): void {
if (!this.config.enableAutoRecovery) {
return;
}
// 检查是否需要开始恢复
if (newState === ConnectionState.Failed || newState === ConnectionState.Disconnected) {
if (this.previousState === ConnectionState.Connected || this.previousState === ConnectionState.Connecting) {
this.startRecovery();
}
}
}
/**
* 开始恢复过程
*/
private startRecovery(): void {
if (this.recoveryAttempts >= this.config.maxRecoveryAttempts) {
this.logger.warn(`已达到最大恢复尝试次数: ${this.config.maxRecoveryAttempts}`);
this.eventHandlers.recoveryFailed?.(true);
return;
}
if (!this.recoveryCallback) {
this.logger.error('恢复回调未设置');
return;
}
this.recoveryAttempts++;
this.logger.info(`开始自动恢复 (第 ${this.recoveryAttempts} 次)`);
this.eventHandlers.recoveryStarted?.(this.recoveryAttempts);
this.recoveryTimer = NetworkTimerManager.schedule(
this.config.recoveryTimeout / 1000, // 转为秒
false, // 不重复
this,
async () => {
try {
await this.recoveryCallback!();
this.eventHandlers.recoverySucceeded?.();
} catch (error) {
this.logger.error(`自动恢复失败 (第 ${this.recoveryAttempts} 次):`, error);
this.eventHandlers.recoveryFailed?.(false);
// 继续尝试恢复
this.startRecovery();
}
}
);
}
/**
* 更新当前统计信息
*/
private updateCurrentStats(): void {
const now = Date.now();
const currentDuration = now - this.stateStartTime;
if (this.currentState === ConnectionState.Connected) {
this.stats.totalUptime += currentDuration - (this.stats.totalUptime > 0 ? 0 : currentDuration);
}
}
/**
* 获取状态可读名称
*/
getStateDisplayName(state?: ConnectionState): string {
const stateNames: Record<ConnectionState, string> = {
[ConnectionState.Disconnected]: '已断开',
[ConnectionState.Connecting]: '连接中',
[ConnectionState.Connected]: '已连接',
[ConnectionState.Reconnecting]: '重连中',
[ConnectionState.Failed]: '连接失败'
};
return stateNames[state || this.currentState];
}
/**
* 获取连接质量评级
*/
getConnectionQuality(): 'excellent' | 'good' | 'fair' | 'poor' {
const successRate = this.stats.connectionAttempts > 0 ?
this.stats.successfulConnections / this.stats.connectionAttempts : 0;
const averageConnectedTime = this.stats.averageStateDurations[ConnectionState.Connected];
if (successRate > 0.9 && averageConnectedTime > 60000) { // 成功率>90%且平均连接时间>1分钟
return 'excellent';
} else if (successRate > 0.7 && averageConnectedTime > 30000) {
return 'good';
} else if (successRate > 0.5 && averageConnectedTime > 10000) {
return 'fair';
} else {
return 'poor';
}
}
}