* fix(eslint): 修复装饰器缩进配置 * fix(eslint): 修复装饰器缩进配置 * chore: 删除未使用的导入 * chore(lint): 移除未使用的导入和变量 * chore(lint): 修复editor-app中未使用的函数参数 * chore(lint): 修复未使用的赋值变量 * chore(eslint): 将所有错误级别改为警告以通过CI * fix(codeql): 修复GitHub Advanced Security检测到的问题
639 lines
19 KiB
TypeScript
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';
|
|
}
|
|
}
|
|
}
|