传输层实现(客户端/服务端,链接管理和心跳机制,重连机制)

消息序列化(json序列化,消息压缩,消息ID和时间戳)
网络服务器核心(networkserver/基础room/链接状态同步)
网络客户端核心(networkclient/消息队列)
This commit is contained in:
YHH
2025-08-14 23:59:00 +08:00
parent 32092f992d
commit 6730a5d625
29 changed files with 8100 additions and 236 deletions

View File

@@ -9,18 +9,21 @@ export * from './types/TransportTypes';
// 协议消息
export * from './protocols/MessageTypes';
export * from './protocols/MessageManager';
// 核心组件
export * from './components/NetworkIdentity';
// 装饰器系统 (待实现)
// export * from './decorators/SyncVar';
// export * from './decorators/ServerRpc';
// export * from './decorators/ClientRpc';
// export * from './decorators/NetworkComponent';
// 传输层
export * from './transport/HeartbeatManager';
export * from './transport/ErrorHandler';
// 事件系统
export * from './events/NetworkEvents';
// 序列化系统 (待实现)
// export * from './serialization/NetworkSerializer';
// 序列化系统
export * from './serialization/JSONSerializer';
export * from './serialization/MessageCompressor';
// 工具类
export * from './utils';

View File

@@ -0,0 +1,502 @@
/**
* 消息管理器
* 负责消息ID生成、时间戳管理和消息验证
*/
import { createLogger } from '@esengine/ecs-framework';
import { INetworkMessage, MessageType } from '../types/NetworkTypes';
/**
* 消息ID生成器类型
*/
export enum MessageIdGeneratorType {
UUID = 'uuid',
SNOWFLAKE = 'snowflake',
SEQUENTIAL = 'sequential',
TIMESTAMP = 'timestamp'
}
/**
* 消息管理器配置
*/
export interface MessageManagerConfig {
idGenerator: MessageIdGeneratorType;
enableTimestampValidation: boolean;
maxTimestampDrift: number; // 最大时间戳偏移(毫秒)
enableMessageDeduplication: boolean;
deduplicationWindowMs: number; // 去重窗口时间
enableMessageOrdering: boolean;
orderingWindowMs: number; // 排序窗口时间
maxPendingMessages: number; // 最大待处理消息数
}
/**
* 消息验证结果
*/
export interface MessageValidationResult {
isValid: boolean;
errors: string[];
warnings: string[];
}
/**
* 消息统计信息
*/
export interface MessageStats {
totalGenerated: number;
totalValidated: number;
validMessages: number;
invalidMessages: number;
duplicateMessages: number;
outOfOrderMessages: number;
timestampErrors: number;
}
/**
* Snowflake ID生成器
*/
class SnowflakeIdGenerator {
private static readonly EPOCH = 1640995200000; // 2022-01-01 00:00:00 UTC
private static readonly WORKER_ID_BITS = 5;
private static readonly DATACENTER_ID_BITS = 5;
private static readonly SEQUENCE_BITS = 12;
private readonly workerId: number;
private readonly datacenterId: number;
private sequence = 0;
private lastTimestamp = -1;
constructor(workerId: number = 1, datacenterId: number = 1) {
this.workerId = workerId & ((1 << SnowflakeIdGenerator.WORKER_ID_BITS) - 1);
this.datacenterId = datacenterId & ((1 << SnowflakeIdGenerator.DATACENTER_ID_BITS) - 1);
}
generate(): string {
let timestamp = Date.now();
if (timestamp < this.lastTimestamp) {
throw new Error('时钟回拨无法生成ID');
}
if (timestamp === this.lastTimestamp) {
this.sequence = (this.sequence + 1) & ((1 << SnowflakeIdGenerator.SEQUENCE_BITS) - 1);
if (this.sequence === 0) {
// 等待下一毫秒
while (timestamp <= this.lastTimestamp) {
timestamp = Date.now();
}
}
} else {
this.sequence = 0;
}
this.lastTimestamp = timestamp;
const id = ((timestamp - SnowflakeIdGenerator.EPOCH) << 22) |
(this.datacenterId << 17) |
(this.workerId << 12) |
this.sequence;
return id.toString();
}
}
/**
* 消息管理器
*/
export class MessageManager {
private logger = createLogger('MessageManager');
private config: MessageManagerConfig;
private stats: MessageStats;
// ID生成器
private sequentialId = 0;
private snowflakeGenerator: SnowflakeIdGenerator;
// 消息去重和排序
private recentMessageIds: Set<string> = new Set();
private pendingMessages: Map<string, INetworkMessage> = new Map();
private messageSequence: Map<string, number> = new Map();
// 清理定时器
private cleanupTimer?: NodeJS.Timeout;
/**
* 构造函数
*/
constructor(config: Partial<MessageManagerConfig> = {}) {
this.config = {
idGenerator: MessageIdGeneratorType.UUID,
enableTimestampValidation: true,
maxTimestampDrift: 60000, // 1分钟
enableMessageDeduplication: true,
deduplicationWindowMs: 300000, // 5分钟
enableMessageOrdering: false,
orderingWindowMs: 10000, // 10秒
maxPendingMessages: 1000,
...config
};
this.stats = {
totalGenerated: 0,
totalValidated: 0,
validMessages: 0,
invalidMessages: 0,
duplicateMessages: 0,
outOfOrderMessages: 0,
timestampErrors: 0
};
this.snowflakeGenerator = new SnowflakeIdGenerator();
this.startCleanupTimer();
}
/**
* 生成消息ID
*/
generateMessageId(): string {
this.stats.totalGenerated++;
switch (this.config.idGenerator) {
case MessageIdGeneratorType.UUID:
return this.generateUUID();
case MessageIdGeneratorType.SNOWFLAKE:
return this.snowflakeGenerator.generate();
case MessageIdGeneratorType.SEQUENTIAL:
return (++this.sequentialId).toString();
case MessageIdGeneratorType.TIMESTAMP:
return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
default:
return this.generateUUID();
}
}
/**
* 创建网络消息
*/
createMessage<T extends INetworkMessage>(
type: MessageType,
data: any,
senderId: string,
options: {
reliable?: boolean;
priority?: number;
timestamp?: number;
} = {}
): T {
const message: INetworkMessage = {
type,
messageId: this.generateMessageId(),
timestamp: options.timestamp || Date.now(),
senderId,
data,
reliable: options.reliable,
priority: options.priority
};
return message as T;
}
/**
* 验证消息
*/
validateMessage(message: INetworkMessage, senderId?: string): MessageValidationResult {
this.stats.totalValidated++;
const errors: string[] = [];
const warnings: string[] = [];
// 基础字段验证
if (!message.messageId) {
errors.push('消息ID不能为空');
}
if (!message.type) {
errors.push('消息类型不能为空');
} else if (!Object.values(MessageType).includes(message.type)) {
errors.push(`无效的消息类型: ${message.type}`);
}
if (!message.timestamp) {
errors.push('时间戳不能为空');
}
if (!message.senderId) {
errors.push('发送者ID不能为空');
}
// 发送者验证
if (senderId && message.senderId !== senderId) {
errors.push('消息发送者ID不匹配');
}
// 时间戳验证
if (this.config.enableTimestampValidation && message.timestamp) {
const now = Date.now();
const drift = Math.abs(now - message.timestamp);
if (drift > this.config.maxTimestampDrift) {
errors.push(`时间戳偏移过大: ${drift}ms > ${this.config.maxTimestampDrift}ms`);
this.stats.timestampErrors++;
}
if (message.timestamp > now + 10000) { // 未来10秒以上
warnings.push('消息时间戳来自未来');
}
}
// 消息去重验证
if (this.config.enableMessageDeduplication) {
if (this.recentMessageIds.has(message.messageId)) {
errors.push('重复的消息ID');
this.stats.duplicateMessages++;
} else {
this.recentMessageIds.add(message.messageId);
}
}
const isValid = errors.length === 0;
if (isValid) {
this.stats.validMessages++;
} else {
this.stats.invalidMessages++;
}
return {
isValid,
errors,
warnings
};
}
/**
* 处理消息排序
*/
processMessageOrdering(message: INetworkMessage): INetworkMessage[] {
if (!this.config.enableMessageOrdering) {
return [message];
}
const senderId = message.senderId;
const currentSequence = this.messageSequence.get(senderId) || 0;
// 检查消息是否按顺序到达
const messageTimestamp = message.timestamp;
const expectedSequence = currentSequence + 1;
// 简单的时间戳排序逻辑
if (messageTimestamp >= expectedSequence) {
// 消息按顺序到达
this.messageSequence.set(senderId, messageTimestamp);
return this.flushPendingMessages(senderId).concat([message]);
} else {
// 消息乱序,暂存
this.pendingMessages.set(message.messageId, message);
this.stats.outOfOrderMessages++;
// 检查是否超出最大待处理数量
if (this.pendingMessages.size > this.config.maxPendingMessages) {
this.logger.warn('待处理消息数量过多,清理旧消息');
this.cleanupOldPendingMessages();
}
return [];
}
}
/**
* 获取统计信息
*/
getStats(): MessageStats {
return { ...this.stats };
}
/**
* 重置统计信息
*/
resetStats(): void {
this.stats = {
totalGenerated: 0,
totalValidated: 0,
validMessages: 0,
invalidMessages: 0,
duplicateMessages: 0,
outOfOrderMessages: 0,
timestampErrors: 0
};
}
/**
* 更新配置
*/
updateConfig(newConfig: Partial<MessageManagerConfig>): void {
const oldConfig = { ...this.config };
Object.assign(this.config, newConfig);
// 如果去重配置改变,清理相关数据
if (!this.config.enableMessageDeduplication && oldConfig.enableMessageDeduplication) {
this.recentMessageIds.clear();
}
// 如果排序配置改变,清理相关数据
if (!this.config.enableMessageOrdering && oldConfig.enableMessageOrdering) {
this.pendingMessages.clear();
this.messageSequence.clear();
}
this.logger.info('消息管理器配置已更新:', newConfig);
}
/**
* 销毁管理器
*/
destroy(): void {
if (this.cleanupTimer) {
clearInterval(this.cleanupTimer);
this.cleanupTimer = undefined;
}
this.recentMessageIds.clear();
this.pendingMessages.clear();
this.messageSequence.clear();
}
/**
* 生成UUID
*/
private generateUUID(): string {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
const r = Math.random() * 16 | 0;
const v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
/**
* 刷新待处理消息
*/
private flushPendingMessages(senderId: string): INetworkMessage[] {
const flushedMessages: INetworkMessage[] = [];
const messagesToRemove: string[] = [];
for (const [messageId, message] of this.pendingMessages) {
if (message.senderId === senderId) {
flushedMessages.push(message);
messagesToRemove.push(messageId);
}
}
// 移除已处理的消息
messagesToRemove.forEach(id => this.pendingMessages.delete(id));
// 按时间戳排序
flushedMessages.sort((a, b) => a.timestamp - b.timestamp);
return flushedMessages;
}
/**
* 清理过期的待处理消息
*/
private cleanupOldPendingMessages(): void {
const now = Date.now();
const messagesToRemove: string[] = [];
for (const [messageId, message] of this.pendingMessages) {
if (now - message.timestamp > this.config.orderingWindowMs) {
messagesToRemove.push(messageId);
}
}
messagesToRemove.forEach(id => this.pendingMessages.delete(id));
if (messagesToRemove.length > 0) {
this.logger.debug(`清理了 ${messagesToRemove.length} 个过期的待处理消息`);
}
}
/**
* 启动清理定时器
*/
private startCleanupTimer(): void {
this.cleanupTimer = setInterval(() => {
this.performCleanup();
}, 60000); // 每分钟清理一次
}
/**
* 执行清理操作
*/
private performCleanup(): void {
const now = Date.now();
// 清理过期的消息ID用于去重
if (this.config.enableMessageDeduplication) {
// 由于Set没有时间戳我们定期清理所有ID
// 这是一个简化实现,实际项目中可以使用更复杂的数据结构
if (this.recentMessageIds.size > 10000) {
this.recentMessageIds.clear();
this.logger.debug('清理了过期的消息ID缓存');
}
}
// 清理过期的待处理消息
if (this.config.enableMessageOrdering) {
this.cleanupOldPendingMessages();
}
}
/**
* 获取消息处理报告
*/
getProcessingReport() {
const totalProcessed = this.stats.validMessages + this.stats.invalidMessages;
const validRate = totalProcessed > 0 ? (this.stats.validMessages / totalProcessed) * 100 : 0;
const duplicateRate = totalProcessed > 0 ? (this.stats.duplicateMessages / totalProcessed) * 100 : 0;
return {
stats: this.getStats(),
validationRate: validRate,
duplicateRate: duplicateRate,
pendingMessagesCount: this.pendingMessages.size,
cachedMessageIdsCount: this.recentMessageIds.size,
recommendation: this.generateRecommendation(validRate, duplicateRate)
};
}
/**
* 生成优化建议
*/
private generateRecommendation(validRate: number, duplicateRate: number): string {
if (validRate < 90) {
return '消息验证失败率较高,建议检查消息格式和发送逻辑';
} else if (duplicateRate > 5) {
return '重复消息较多,建议检查客户端重发逻辑或调整去重窗口';
} else if (this.pendingMessages.size > this.config.maxPendingMessages * 0.8) {
return '待处理消息过多,建议优化网络或调整排序窗口';
} else {
return '消息处理正常';
}
}
/**
* 批量验证消息
*/
validateMessageBatch(messages: INetworkMessage[], senderId?: string): MessageValidationResult[] {
return messages.map(message => this.validateMessage(message, senderId));
}
/**
* 获取消息年龄(毫秒)
*/
getMessageAge(message: INetworkMessage): number {
return Date.now() - message.timestamp;
}
/**
* 检查消息是否过期
*/
isMessageExpired(message: INetworkMessage, maxAge: number = 300000): boolean {
return this.getMessageAge(message) > maxAge;
}
}

View File

@@ -0,0 +1,550 @@
/**
* JSON序列化器
* 提供高性能的消息序列化和反序列化功能,包括类型安全检查
*/
import { createLogger } from '@esengine/ecs-framework';
import { INetworkMessage, MessageType } from '../types/NetworkTypes';
/**
* 序列化器配置
*/
export interface SerializerConfig {
enableTypeChecking: boolean;
enableCompression: boolean;
maxMessageSize: number;
enableProfiling: boolean;
customSerializers?: Map<string, ICustomSerializer>;
}
/**
* 自定义序列化器接口
*/
export interface ICustomSerializer {
serialize(data: any): any;
deserialize(data: any): any;
canHandle(data: any): boolean;
}
/**
* 序列化结果
*/
export interface SerializationResult {
data: string | Buffer;
size: number;
compressionRatio?: number;
serializationTime: number;
}
/**
* 反序列化结果
*/
export interface DeserializationResult<T = any> {
data: T;
deserializationTime: number;
isValid: boolean;
errors?: string[];
}
/**
* 序列化统计信息
*/
export interface SerializationStats {
totalSerialized: number;
totalDeserialized: number;
totalBytes: number;
averageSerializationTime: number;
averageDeserializationTime: number;
averageMessageSize: number;
errorCount: number;
compressionSavings: number;
}
/**
* JSON序列化器
*/
export class JSONSerializer {
private logger = createLogger('JSONSerializer');
private config: SerializerConfig;
private stats: SerializationStats;
// 性能分析
private serializationTimes: number[] = [];
private deserializationTimes: number[] = [];
private messageSizes: number[] = [];
/**
* 构造函数
*/
constructor(config: Partial<SerializerConfig> = {}) {
this.config = {
enableTypeChecking: true,
enableCompression: false,
maxMessageSize: 1024 * 1024, // 1MB
enableProfiling: false,
...config
};
this.stats = {
totalSerialized: 0,
totalDeserialized: 0,
totalBytes: 0,
averageSerializationTime: 0,
averageDeserializationTime: 0,
averageMessageSize: 0,
errorCount: 0,
compressionSavings: 0
};
}
/**
* 序列化消息
*/
serialize<T extends INetworkMessage>(message: T): SerializationResult {
const startTime = performance.now();
try {
// 类型检查
if (this.config.enableTypeChecking) {
this.validateMessage(message);
}
// 预处理消息
const processedMessage = this.preprocessMessage(message);
// 序列化
let serializedData: string;
// 使用自定义序列化器
const customSerializer = this.findCustomSerializer(processedMessage);
if (customSerializer) {
serializedData = JSON.stringify(customSerializer.serialize(processedMessage));
} else {
serializedData = JSON.stringify(processedMessage, this.createReplacer());
}
// 检查大小限制
if (serializedData.length > this.config.maxMessageSize) {
throw new Error(`消息大小超过限制: ${serializedData.length} > ${this.config.maxMessageSize}`);
}
const endTime = performance.now();
const serializationTime = endTime - startTime;
// 更新统计
this.updateSerializationStats(serializedData.length, serializationTime);
return {
data: serializedData,
size: serializedData.length,
serializationTime
};
} catch (error) {
this.stats.errorCount++;
this.logger.error('序列化失败:', error);
throw error;
}
}
/**
* 反序列化消息
*/
deserialize<T extends INetworkMessage>(data: string | ArrayBuffer): DeserializationResult<T> {
const startTime = performance.now();
try {
// 转换数据格式
const jsonString = data instanceof ArrayBuffer ? new TextDecoder().decode(data) :
typeof data === 'string' ? data : String(data);
// 解析JSON
const parsedData = JSON.parse(jsonString, this.createReviver());
// 类型检查
const validationResult = this.config.enableTypeChecking ?
this.validateParsedMessage(parsedData) : { isValid: true, errors: [] };
// 后处理消息
const processedMessage = this.postprocessMessage(parsedData);
const endTime = performance.now();
const deserializationTime = endTime - startTime;
// 更新统计
this.updateDeserializationStats(deserializationTime);
return {
data: processedMessage as T,
deserializationTime,
isValid: validationResult.isValid,
errors: validationResult.errors
};
} catch (error) {
this.stats.errorCount++;
this.logger.error('反序列化失败:', error);
return {
data: {} as T,
deserializationTime: performance.now() - startTime,
isValid: false,
errors: [error instanceof Error ? error.message : '未知错误']
};
}
}
/**
* 批量序列化
*/
serializeBatch<T extends INetworkMessage>(messages: T[]): SerializationResult {
const startTime = performance.now();
try {
const batchData = {
type: 'batch',
messages: messages.map(msg => {
if (this.config.enableTypeChecking) {
this.validateMessage(msg);
}
return this.preprocessMessage(msg);
}),
timestamp: Date.now()
};
const serializedData = JSON.stringify(batchData, this.createReplacer());
if (serializedData.length > this.config.maxMessageSize) {
throw new Error(`批量消息大小超过限制: ${serializedData.length} > ${this.config.maxMessageSize}`);
}
const endTime = performance.now();
const serializationTime = endTime - startTime;
this.updateSerializationStats(serializedData.length, serializationTime);
return {
data: serializedData,
size: serializedData.length,
serializationTime
};
} catch (error) {
this.stats.errorCount++;
this.logger.error('批量序列化失败:', error);
throw error;
}
}
/**
* 批量反序列化
*/
deserializeBatch<T extends INetworkMessage>(data: string | ArrayBuffer): DeserializationResult<T[]> {
const result = this.deserialize<any>(data);
if (!result.isValid || !result.data.messages) {
return {
data: [],
deserializationTime: result.deserializationTime,
isValid: false,
errors: ['无效的批量消息格式']
};
}
const messages = result.data.messages.map((msg: any) => this.postprocessMessage(msg));
return {
data: messages as T[],
deserializationTime: result.deserializationTime,
isValid: true
};
}
/**
* 获取统计信息
*/
getStats(): SerializationStats {
return { ...this.stats };
}
/**
* 重置统计信息
*/
resetStats(): void {
this.stats = {
totalSerialized: 0,
totalDeserialized: 0,
totalBytes: 0,
averageSerializationTime: 0,
averageDeserializationTime: 0,
averageMessageSize: 0,
errorCount: 0,
compressionSavings: 0
};
this.serializationTimes.length = 0;
this.deserializationTimes.length = 0;
this.messageSizes.length = 0;
}
/**
* 添加自定义序列化器
*/
addCustomSerializer(name: string, serializer: ICustomSerializer): void {
if (!this.config.customSerializers) {
this.config.customSerializers = new Map();
}
this.config.customSerializers.set(name, serializer);
}
/**
* 移除自定义序列化器
*/
removeCustomSerializer(name: string): boolean {
return this.config.customSerializers?.delete(name) || false;
}
/**
* 更新配置
*/
updateConfig(newConfig: Partial<SerializerConfig>): void {
Object.assign(this.config, newConfig);
this.logger.info('序列化器配置已更新:', newConfig);
}
/**
* 验证消息格式
*/
private validateMessage(message: INetworkMessage): void {
if (!message.type || !message.messageId || !message.timestamp) {
throw new Error('消息格式无效:缺少必需字段');
}
if (!Object.values(MessageType).includes(message.type)) {
throw new Error(`无效的消息类型: ${message.type}`);
}
if (typeof message.timestamp !== 'number' || message.timestamp <= 0) {
throw new Error('无效的时间戳');
}
}
/**
* 验证解析后的消息
*/
private validateParsedMessage(data: any): { isValid: boolean; errors: string[] } {
const errors: string[] = [];
if (!data || typeof data !== 'object') {
errors.push('消息必须是对象');
} else {
if (!data.type) errors.push('缺少消息类型');
if (!data.messageId) errors.push('缺少消息ID');
if (!data.timestamp) errors.push('缺少时间戳');
if (!data.senderId) errors.push('缺少发送者ID');
}
return {
isValid: errors.length === 0,
errors
};
}
/**
* 预处理消息(序列化前)
*/
private preprocessMessage(message: INetworkMessage): any {
// 克隆消息以避免修改原始对象
const processed = { ...message };
// 处理特殊数据类型
if (processed.data) {
processed.data = this.serializeSpecialTypes(processed.data);
}
return processed;
}
/**
* 后处理消息(反序列化后)
*/
private postprocessMessage(data: any): any {
if (data.data) {
data.data = this.deserializeSpecialTypes(data.data);
}
return data;
}
/**
* 序列化特殊类型
*/
private serializeSpecialTypes(data: any): any {
if (data instanceof Date) {
return { __type: 'Date', value: data.toISOString() };
} else if (data instanceof Map) {
return { __type: 'Map', value: Array.from(data.entries()) };
} else if (data instanceof Set) {
return { __type: 'Set', value: Array.from(data) };
} else if (ArrayBuffer.isView(data)) {
return { __type: 'TypedArray', value: Array.from(data as any), constructor: data.constructor.name };
} else if (data && typeof data === 'object') {
const result: any = {};
for (const [key, value] of Object.entries(data)) {
result[key] = this.serializeSpecialTypes(value);
}
return result;
}
return data;
}
/**
* 反序列化特殊类型
*/
private deserializeSpecialTypes(data: any): any {
if (data && typeof data === 'object' && data.__type) {
switch (data.__type) {
case 'Date':
return new Date(data.value);
case 'Map':
return new Map(data.value);
case 'Set':
return new Set(data.value);
case 'TypedArray':
const constructor = (globalThis as any)[data.constructor];
return constructor ? new constructor(data.value) : data.value;
}
} else if (data && typeof data === 'object') {
const result: any = {};
for (const [key, value] of Object.entries(data)) {
result[key] = this.deserializeSpecialTypes(value);
}
return result;
}
return data;
}
/**
* 创建JSON.stringify替换函数
*/
private createReplacer() {
return (key: string, value: any) => {
// 处理循环引用
if (value && typeof value === 'object') {
if (value.__serializing) {
return '[Circular Reference]';
}
value.__serializing = true;
}
return value;
};
}
/**
* 创建JSON.parse恢复函数
*/
private createReviver() {
return (key: string, value: any) => {
// 清理序列化标记
if (value && typeof value === 'object') {
delete value.__serializing;
}
return value;
};
}
/**
* 查找自定义序列化器
*/
private findCustomSerializer(data: any): ICustomSerializer | undefined {
if (!this.config.customSerializers) {
return undefined;
}
for (const serializer of this.config.customSerializers.values()) {
if (serializer.canHandle(data)) {
return serializer;
}
}
return undefined;
}
/**
* 更新序列化统计
*/
private updateSerializationStats(size: number, time: number): void {
this.stats.totalSerialized++;
this.stats.totalBytes += size;
this.serializationTimes.push(time);
this.messageSizes.push(size);
// 保持最近1000个样本
if (this.serializationTimes.length > 1000) {
this.serializationTimes.shift();
}
if (this.messageSizes.length > 1000) {
this.messageSizes.shift();
}
// 计算平均值
this.stats.averageSerializationTime =
this.serializationTimes.reduce((sum, t) => sum + t, 0) / this.serializationTimes.length;
this.stats.averageMessageSize =
this.messageSizes.reduce((sum, s) => sum + s, 0) / this.messageSizes.length;
}
/**
* 更新反序列化统计
*/
private updateDeserializationStats(time: number): void {
this.stats.totalDeserialized++;
this.deserializationTimes.push(time);
// 保持最近1000个样本
if (this.deserializationTimes.length > 1000) {
this.deserializationTimes.shift();
}
// 计算平均值
this.stats.averageDeserializationTime =
this.deserializationTimes.reduce((sum, t) => sum + t, 0) / this.deserializationTimes.length;
}
/**
* 获取性能分析报告
*/
getPerformanceReport() {
return {
stats: this.getStats(),
serializationTimes: [...this.serializationTimes],
deserializationTimes: [...this.deserializationTimes],
messageSizes: [...this.messageSizes],
percentiles: {
serialization: this.calculatePercentiles(this.serializationTimes),
deserialization: this.calculatePercentiles(this.deserializationTimes),
messageSize: this.calculatePercentiles(this.messageSizes)
}
};
}
/**
* 计算百分位数
*/
private calculatePercentiles(values: number[]) {
if (values.length === 0) return {};
const sorted = [...values].sort((a, b) => a - b);
const n = sorted.length;
return {
p50: sorted[Math.floor(n * 0.5)],
p90: sorted[Math.floor(n * 0.9)],
p95: sorted[Math.floor(n * 0.95)],
p99: sorted[Math.floor(n * 0.99)]
};
}
}

View File

@@ -0,0 +1,498 @@
/**
* 消息压缩器
* 提供多种压缩算法选择和压缩率统计
*/
import { createLogger } from '@esengine/ecs-framework';
import * as zlib from 'zlib';
import { promisify } from 'util';
/**
* 压缩算法类型
*/
export enum CompressionAlgorithm {
NONE = 'none',
GZIP = 'gzip',
DEFLATE = 'deflate',
BROTLI = 'brotli'
}
/**
* 压缩配置
*/
export interface CompressionConfig {
algorithm: CompressionAlgorithm;
level: number; // 压缩级别 (0-9)
threshold: number; // 最小压缩阈值(字节)
enableAsync: boolean; // 是否启用异步压缩
chunkSize: number; // 分块大小
}
/**
* 压缩结果
*/
export interface CompressionResult {
data: Buffer;
originalSize: number;
compressedSize: number;
compressionRatio: number;
compressionTime: number;
algorithm: CompressionAlgorithm;
}
/**
* 压缩统计信息
*/
export interface CompressionStats {
totalCompressed: number;
totalDecompressed: number;
totalOriginalBytes: number;
totalCompressedBytes: number;
averageCompressionRatio: number;
averageCompressionTime: number;
averageDecompressionTime: number;
algorithmUsage: Record<CompressionAlgorithm, number>;
}
/**
* 消息压缩器
*/
export class MessageCompressor {
private logger = createLogger('MessageCompressor');
private config: CompressionConfig;
private stats: CompressionStats;
// 异步压缩函数
private gzipAsync = promisify(zlib.gzip);
private gunzipAsync = promisify(zlib.gunzip);
private deflateAsync = promisify(zlib.deflate);
private inflateAsync = promisify(zlib.inflate);
private brotliCompressAsync = promisify(zlib.brotliCompress);
private brotliDecompressAsync = promisify(zlib.brotliDecompress);
/**
* 构造函数
*/
constructor(config: Partial<CompressionConfig> = {}) {
this.config = {
algorithm: CompressionAlgorithm.GZIP,
level: 6, // 平衡压缩率和速度
threshold: 1024, // 1KB以上才压缩
enableAsync: true,
chunkSize: 64 * 1024, // 64KB分块
...config
};
this.stats = {
totalCompressed: 0,
totalDecompressed: 0,
totalOriginalBytes: 0,
totalCompressedBytes: 0,
averageCompressionRatio: 0,
averageCompressionTime: 0,
averageDecompressionTime: 0,
algorithmUsage: {
[CompressionAlgorithm.NONE]: 0,
[CompressionAlgorithm.GZIP]: 0,
[CompressionAlgorithm.DEFLATE]: 0,
[CompressionAlgorithm.BROTLI]: 0
}
};
}
/**
* 压缩数据
*/
async compress(data: string | Buffer): Promise<CompressionResult> {
const startTime = performance.now();
const inputBuffer = typeof data === 'string' ? Buffer.from(data, 'utf8') : data;
const originalSize = inputBuffer.length;
try {
// 检查是否需要压缩
if (originalSize < this.config.threshold) {
return this.createNoCompressionResult(inputBuffer, originalSize, startTime);
}
let compressedData: Buffer;
const algorithm = this.config.algorithm;
// 根据算法进行压缩
switch (algorithm) {
case CompressionAlgorithm.GZIP:
compressedData = await this.compressGzip(inputBuffer);
break;
case CompressionAlgorithm.DEFLATE:
compressedData = await this.compressDeflate(inputBuffer);
break;
case CompressionAlgorithm.BROTLI:
compressedData = await this.compressBrotli(inputBuffer);
break;
case CompressionAlgorithm.NONE:
default:
return this.createNoCompressionResult(inputBuffer, originalSize, startTime);
}
const endTime = performance.now();
const compressionTime = endTime - startTime;
const compressedSize = compressedData.length;
const compressionRatio = originalSize > 0 ? compressedSize / originalSize : 1;
// 检查压缩效果
if (compressedSize >= originalSize * 0.9) {
// 压缩效果不明显,返回原始数据
this.logger.debug(`压缩效果不佳,返回原始数据。原始: ${originalSize}, 压缩: ${compressedSize}`);
return this.createNoCompressionResult(inputBuffer, originalSize, startTime);
}
const result: CompressionResult = {
data: compressedData,
originalSize,
compressedSize,
compressionRatio,
compressionTime,
algorithm
};
// 更新统计
this.updateCompressionStats(result);
return result;
} catch (error) {
this.logger.error('压缩失败:', error);
return this.createNoCompressionResult(inputBuffer, originalSize, startTime);
}
}
/**
* 解压缩数据
*/
async decompress(data: Buffer, algorithm: CompressionAlgorithm): Promise<Buffer> {
const startTime = performance.now();
try {
if (algorithm === CompressionAlgorithm.NONE) {
return data;
}
let decompressedData: Buffer;
switch (algorithm) {
case CompressionAlgorithm.GZIP:
decompressedData = await this.decompressGzip(data);
break;
case CompressionAlgorithm.DEFLATE:
decompressedData = await this.decompressDeflate(data);
break;
case CompressionAlgorithm.BROTLI:
decompressedData = await this.decompressBrotli(data);
break;
default:
throw new Error(`不支持的压缩算法: ${algorithm}`);
}
const endTime = performance.now();
const decompressionTime = endTime - startTime;
// 更新统计
this.updateDecompressionStats(decompressionTime);
return decompressedData;
} catch (error) {
this.logger.error('解压缩失败:', error);
throw error;
}
}
/**
* 批量压缩
*/
async compressBatch(dataList: (string | Buffer)[]): Promise<CompressionResult[]> {
const results: CompressionResult[] = [];
if (this.config.enableAsync) {
// 并行压缩
const promises = dataList.map(data => this.compress(data));
return await Promise.all(promises);
} else {
// 串行压缩
for (const data of dataList) {
results.push(await this.compress(data));
}
return results;
}
}
/**
* 自适应压缩
* 根据数据特征自动选择最佳压缩算法
*/
async compressAdaptive(data: string | Buffer): Promise<CompressionResult> {
const inputBuffer = typeof data === 'string' ? Buffer.from(data, 'utf8') : data;
const originalAlgorithm = this.config.algorithm;
try {
// 对小数据进行算法测试
const testSize = Math.min(inputBuffer.length, 4096); // 测试前4KB
const testData = inputBuffer.subarray(0, testSize);
const algorithms = [
CompressionAlgorithm.GZIP,
CompressionAlgorithm.DEFLATE,
CompressionAlgorithm.BROTLI
];
let bestAlgorithm = CompressionAlgorithm.GZIP;
let bestRatio = 1;
// 测试不同算法的压缩效果
for (const algorithm of algorithms) {
try {
this.config.algorithm = algorithm;
const testResult = await this.compress(testData);
if (testResult.compressionRatio < bestRatio) {
bestRatio = testResult.compressionRatio;
bestAlgorithm = algorithm;
}
} catch (error) {
// 忽略测试失败的算法
continue;
}
}
// 使用最佳算法压缩完整数据
this.config.algorithm = bestAlgorithm;
const result = await this.compress(inputBuffer);
this.logger.debug(`自适应压缩选择算法: ${bestAlgorithm}, 压缩率: ${result.compressionRatio.toFixed(3)}`);
return result;
} finally {
// 恢复原始配置
this.config.algorithm = originalAlgorithm;
}
}
/**
* 获取统计信息
*/
getStats(): CompressionStats {
return { ...this.stats };
}
/**
* 重置统计信息
*/
resetStats(): void {
this.stats = {
totalCompressed: 0,
totalDecompressed: 0,
totalOriginalBytes: 0,
totalCompressedBytes: 0,
averageCompressionRatio: 0,
averageCompressionTime: 0,
averageDecompressionTime: 0,
algorithmUsage: {
[CompressionAlgorithm.NONE]: 0,
[CompressionAlgorithm.GZIP]: 0,
[CompressionAlgorithm.DEFLATE]: 0,
[CompressionAlgorithm.BROTLI]: 0
}
};
}
/**
* 更新配置
*/
updateConfig(newConfig: Partial<CompressionConfig>): void {
Object.assign(this.config, newConfig);
this.logger.info('压缩器配置已更新:', newConfig);
}
/**
* 获取压缩建议
*/
getCompressionRecommendation(dataSize: number, dataType: string): CompressionAlgorithm {
// 根据数据大小和类型推荐压缩算法
if (dataSize < this.config.threshold) {
return CompressionAlgorithm.NONE;
}
if (dataType === 'json' || dataType === 'text') {
// 文本数据推荐GZIP
return CompressionAlgorithm.GZIP;
} else if (dataType === 'binary') {
// 二进制数据推荐DEFLATE
return CompressionAlgorithm.DEFLATE;
} else {
// 默认推荐GZIP
return CompressionAlgorithm.GZIP;
}
}
/**
* GZIP压缩
*/
private async compressGzip(data: Buffer): Promise<Buffer> {
if (this.config.enableAsync) {
return await this.gzipAsync(data, { level: this.config.level });
} else {
return zlib.gzipSync(data, { level: this.config.level });
}
}
/**
* GZIP解压缩
*/
private async decompressGzip(data: Buffer): Promise<Buffer> {
if (this.config.enableAsync) {
return await this.gunzipAsync(data);
} else {
return zlib.gunzipSync(data);
}
}
/**
* DEFLATE压缩
*/
private async compressDeflate(data: Buffer): Promise<Buffer> {
if (this.config.enableAsync) {
return await this.deflateAsync(data, { level: this.config.level });
} else {
return zlib.deflateSync(data, { level: this.config.level });
}
}
/**
* DEFLATE解压缩
*/
private async decompressDeflate(data: Buffer): Promise<Buffer> {
if (this.config.enableAsync) {
return await this.inflateAsync(data);
} else {
return zlib.inflateSync(data);
}
}
/**
* BROTLI压缩
*/
private async compressBrotli(data: Buffer): Promise<Buffer> {
const options = {
params: {
[zlib.constants.BROTLI_PARAM_QUALITY]: this.config.level
}
};
if (this.config.enableAsync) {
return await this.brotliCompressAsync(data, options);
} else {
return zlib.brotliCompressSync(data, options);
}
}
/**
* BROTLI解压缩
*/
private async decompressBrotli(data: Buffer): Promise<Buffer> {
if (this.config.enableAsync) {
return await this.brotliDecompressAsync(data);
} else {
return zlib.brotliDecompressSync(data);
}
}
/**
* 创建无压缩结果
*/
private createNoCompressionResult(
data: Buffer,
originalSize: number,
startTime: number
): CompressionResult {
const endTime = performance.now();
const result: CompressionResult = {
data,
originalSize,
compressedSize: originalSize,
compressionRatio: 1,
compressionTime: endTime - startTime,
algorithm: CompressionAlgorithm.NONE
};
this.updateCompressionStats(result);
return result;
}
/**
* 更新压缩统计
*/
private updateCompressionStats(result: CompressionResult): void {
this.stats.totalCompressed++;
this.stats.totalOriginalBytes += result.originalSize;
this.stats.totalCompressedBytes += result.compressedSize;
this.stats.algorithmUsage[result.algorithm]++;
// 计算平均值
this.stats.averageCompressionRatio =
this.stats.totalOriginalBytes > 0 ?
this.stats.totalCompressedBytes / this.stats.totalOriginalBytes : 1;
// 更新平均压缩时间(使用移动平均)
const alpha = 0.1; // 平滑因子
this.stats.averageCompressionTime =
this.stats.averageCompressionTime * (1 - alpha) + result.compressionTime * alpha;
}
/**
* 更新解压缩统计
*/
private updateDecompressionStats(decompressionTime: number): void {
this.stats.totalDecompressed++;
// 更新平均解压缩时间(使用移动平均)
const alpha = 0.1;
this.stats.averageDecompressionTime =
this.stats.averageDecompressionTime * (1 - alpha) + decompressionTime * alpha;
}
/**
* 获取压缩效率报告
*/
getEfficiencyReport() {
const savings = this.stats.totalOriginalBytes - this.stats.totalCompressedBytes;
const savingsPercentage = this.stats.totalOriginalBytes > 0 ?
(savings / this.stats.totalOriginalBytes) * 100 : 0;
return {
totalSavings: savings,
savingsPercentage,
averageCompressionRatio: this.stats.averageCompressionRatio,
averageCompressionTime: this.stats.averageCompressionTime,
averageDecompressionTime: this.stats.averageDecompressionTime,
algorithmUsage: this.stats.algorithmUsage,
recommendation: this.generateRecommendation()
};
}
/**
* 生成优化建议
*/
private generateRecommendation(): string {
const ratio = this.stats.averageCompressionRatio;
const time = this.stats.averageCompressionTime;
if (ratio > 0.8) {
return '压缩效果较差,建议调整算法或提高压缩级别';
} else if (time > 50) {
return '压缩时间较长,建议降低压缩级别或使用更快的算法';
} else if (ratio < 0.3) {
return '压缩效果很好,当前配置最优';
} else {
return '压缩性能正常';
}
}
}

View File

@@ -0,0 +1,461 @@
/**
* 网络错误处理器
* 提供统一的错误处理、分类和恢复策略
*/
import { createLogger } from '@esengine/ecs-framework';
import { NetworkErrorType, INetworkError } from '../types/NetworkTypes';
/**
* 错误严重级别
*/
export enum ErrorSeverity {
Low = 'low', // 低级错误,可以忽略
Medium = 'medium', // 中级错误,需要记录但不影响功能
High = 'high', // 高级错误,影响功能但可以恢复
Critical = 'critical' // 严重错误,需要立即处理
}
/**
* 错误恢复策略
*/
export enum RecoveryStrategy {
Ignore = 'ignore', // 忽略错误
Retry = 'retry', // 重试操作
Reconnect = 'reconnect', // 重新连接
Restart = 'restart', // 重启服务
Escalate = 'escalate' // 上报错误
}
/**
* 错误处理配置
*/
export interface ErrorHandlerConfig {
maxRetryAttempts: number;
retryDelay: number;
enableAutoRecovery: boolean;
enableErrorReporting: boolean;
errorReportingEndpoint?: string;
}
/**
* 错误统计信息
*/
export interface ErrorStats {
totalErrors: number;
errorsByType: Record<NetworkErrorType, number>;
errorsBySeverity: Record<ErrorSeverity, number>;
recoveredErrors: number;
unrecoveredErrors: number;
lastError?: INetworkError;
}
/**
* 错误处理事件
*/
export interface ErrorHandlerEvents {
errorOccurred: (error: INetworkError, severity: ErrorSeverity) => void;
errorRecovered: (error: INetworkError, strategy: RecoveryStrategy) => void;
errorUnrecoverable: (error: INetworkError) => void;
criticalError: (error: INetworkError) => void;
}
/**
* 网络错误处理器
*/
export class ErrorHandler {
private logger = createLogger('ErrorHandler');
private config: ErrorHandlerConfig;
private stats: ErrorStats;
private eventHandlers: Partial<ErrorHandlerEvents> = {};
// 错误恢复状态
private retryAttempts: Map<string, number> = new Map();
private pendingRecoveries: Set<string> = new Set();
// 错误分类规则
private severityRules: Map<NetworkErrorType, ErrorSeverity> = new Map();
private recoveryRules: Map<NetworkErrorType, RecoveryStrategy> = new Map();
/**
* 构造函数
*/
constructor(config: Partial<ErrorHandlerConfig> = {}) {
this.config = {
maxRetryAttempts: 3,
retryDelay: 1000,
enableAutoRecovery: true,
enableErrorReporting: false,
...config
};
this.stats = {
totalErrors: 0,
errorsByType: {} as Record<NetworkErrorType, number>,
errorsBySeverity: {} as Record<ErrorSeverity, number>,
recoveredErrors: 0,
unrecoveredErrors: 0
};
this.initializeDefaultRules();
}
/**
* 处理错误
*/
handleError(error: Error | INetworkError, context?: string): void {
const networkError = this.normalizeError(error, context);
const severity = this.classifyErrorSeverity(networkError);
// 更新统计
this.updateStats(networkError, severity);
this.logger.error(`网络错误 [${severity}]: ${networkError.message}`, {
type: networkError.type,
code: networkError.code,
details: networkError.details,
context
});
// 触发错误事件
this.eventHandlers.errorOccurred?.(networkError, severity);
// 处理严重错误
if (severity === ErrorSeverity.Critical) {
this.eventHandlers.criticalError?.(networkError);
}
// 尝试自动恢复
if (this.config.enableAutoRecovery) {
this.attemptRecovery(networkError, severity);
}
// 错误报告
if (this.config.enableErrorReporting) {
this.reportError(networkError, severity);
}
}
/**
* 设置错误分类规则
*/
setErrorSeverityRule(errorType: NetworkErrorType, severity: ErrorSeverity): void {
this.severityRules.set(errorType, severity);
}
/**
* 设置错误恢复策略
*/
setRecoveryStrategy(errorType: NetworkErrorType, strategy: RecoveryStrategy): void {
this.recoveryRules.set(errorType, strategy);
}
/**
* 获取错误统计
*/
getStats(): ErrorStats {
return { ...this.stats };
}
/**
* 重置统计信息
*/
resetStats(): void {
this.stats = {
totalErrors: 0,
errorsByType: {} as Record<NetworkErrorType, number>,
errorsBySeverity: {} as Record<ErrorSeverity, number>,
recoveredErrors: 0,
unrecoveredErrors: 0
};
this.retryAttempts.clear();
this.pendingRecoveries.clear();
}
/**
* 设置事件处理器
*/
on<K extends keyof ErrorHandlerEvents>(event: K, handler: ErrorHandlerEvents[K]): void {
this.eventHandlers[event] = handler;
}
/**
* 移除事件处理器
*/
off<K extends keyof ErrorHandlerEvents>(event: K): void {
delete this.eventHandlers[event];
}
/**
* 更新配置
*/
updateConfig(newConfig: Partial<ErrorHandlerConfig>): void {
Object.assign(this.config, newConfig);
this.logger.info('错误处理器配置已更新:', newConfig);
}
/**
* 手动标记错误已恢复
*/
markErrorRecovered(errorId: string): void {
this.retryAttempts.delete(errorId);
this.pendingRecoveries.delete(errorId);
this.stats.recoveredErrors++;
}
/**
* 检查错误是否可恢复
*/
isRecoverable(errorType: NetworkErrorType): boolean {
const strategy = this.recoveryRules.get(errorType);
return strategy !== undefined && strategy !== RecoveryStrategy.Ignore;
}
/**
* 标准化错误对象
*/
private normalizeError(error: Error | INetworkError, context?: string): INetworkError {
if ('type' in error && 'message' in error && 'timestamp' in error) {
return error as INetworkError;
}
// 将普通Error转换为INetworkError
return {
type: this.determineErrorType(error),
message: error.message || '未知错误',
code: (error as any).code,
details: {
context,
stack: error.stack,
name: error.name
},
timestamp: Date.now()
};
}
/**
* 确定错误类型
*/
private determineErrorType(error: Error): NetworkErrorType {
const message = error.message.toLowerCase();
if (message.includes('timeout')) {
return NetworkErrorType.TIMEOUT;
} else if (message.includes('connection')) {
return NetworkErrorType.CONNECTION_LOST;
} else if (message.includes('auth')) {
return NetworkErrorType.AUTHENTICATION_FAILED;
} else if (message.includes('permission')) {
return NetworkErrorType.PERMISSION_DENIED;
} else if (message.includes('rate') || message.includes('limit')) {
return NetworkErrorType.RATE_LIMITED;
} else if (message.includes('invalid') || message.includes('format')) {
return NetworkErrorType.INVALID_MESSAGE;
} else {
return NetworkErrorType.UNKNOWN;
}
}
/**
* 分类错误严重程度
*/
private classifyErrorSeverity(error: INetworkError): ErrorSeverity {
// 使用自定义规则
const customSeverity = this.severityRules.get(error.type);
if (customSeverity) {
return customSeverity;
}
// 默认分类规则
switch (error.type) {
case NetworkErrorType.CONNECTION_FAILED:
case NetworkErrorType.CONNECTION_LOST:
return ErrorSeverity.High;
case NetworkErrorType.AUTHENTICATION_FAILED:
case NetworkErrorType.PERMISSION_DENIED:
return ErrorSeverity.Critical;
case NetworkErrorType.TIMEOUT:
case NetworkErrorType.RATE_LIMITED:
return ErrorSeverity.Medium;
case NetworkErrorType.INVALID_MESSAGE:
return ErrorSeverity.Low;
default:
return ErrorSeverity.Medium;
}
}
/**
* 更新统计信息
*/
private updateStats(error: INetworkError, severity: ErrorSeverity): void {
this.stats.totalErrors++;
this.stats.errorsByType[error.type] = (this.stats.errorsByType[error.type] || 0) + 1;
this.stats.errorsBySeverity[severity] = (this.stats.errorsBySeverity[severity] || 0) + 1;
this.stats.lastError = error;
}
/**
* 尝试错误恢复
*/
private attemptRecovery(error: INetworkError, severity: ErrorSeverity): void {
const strategy = this.recoveryRules.get(error.type);
if (!strategy || strategy === RecoveryStrategy.Ignore) {
return;
}
const errorId = this.generateErrorId(error);
// 检查是否已经在恢复中
if (this.pendingRecoveries.has(errorId)) {
return;
}
// 检查重试次数
const retryCount = this.retryAttempts.get(errorId) || 0;
if (retryCount >= this.config.maxRetryAttempts) {
this.stats.unrecoveredErrors++;
this.eventHandlers.errorUnrecoverable?.(error);
return;
}
this.pendingRecoveries.add(errorId);
this.retryAttempts.set(errorId, retryCount + 1);
this.logger.info(`尝试错误恢复: ${strategy} (第 ${retryCount + 1} 次)`, {
errorType: error.type,
strategy
});
// 延迟执行恢复策略
setTimeout(() => {
this.executeRecoveryStrategy(error, strategy, errorId);
}, this.config.retryDelay * (retryCount + 1));
}
/**
* 执行恢复策略
*/
private executeRecoveryStrategy(
error: INetworkError,
strategy: RecoveryStrategy,
errorId: string
): void {
try {
switch (strategy) {
case RecoveryStrategy.Retry:
// 这里应该重试导致错误的操作
// 具体实现需要外部提供重试回调
break;
case RecoveryStrategy.Reconnect:
// 这里应该触发重连
// 具体实现需要外部处理
break;
case RecoveryStrategy.Restart:
// 这里应该重启相关服务
// 具体实现需要外部处理
break;
case RecoveryStrategy.Escalate:
// 上报错误给上层处理
this.logger.error('错误需要上层处理:', error);
break;
}
this.pendingRecoveries.delete(errorId);
this.eventHandlers.errorRecovered?.(error, strategy);
} catch (recoveryError) {
this.logger.error('错误恢复失败:', recoveryError);
this.pendingRecoveries.delete(errorId);
}
}
/**
* 报告错误
*/
private async reportError(error: INetworkError, severity: ErrorSeverity): Promise<void> {
if (!this.config.errorReportingEndpoint) {
return;
}
try {
const report = {
error,
severity,
timestamp: Date.now(),
userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : 'Node.js',
url: typeof window !== 'undefined' ? window.location.href : 'server'
};
// 发送错误报告
await fetch(this.config.errorReportingEndpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(report)
});
} catch (reportError) {
this.logger.error('发送错误报告失败:', reportError);
}
}
/**
* 生成错误ID
*/
private generateErrorId(error: INetworkError): string {
return `${error.type}-${error.code || 'no-code'}-${error.timestamp}`;
}
/**
* 初始化默认规则
*/
private initializeDefaultRules(): void {
// 默认严重程度规则
this.severityRules.set(NetworkErrorType.CONNECTION_FAILED, ErrorSeverity.High);
this.severityRules.set(NetworkErrorType.CONNECTION_LOST, ErrorSeverity.High);
this.severityRules.set(NetworkErrorType.AUTHENTICATION_FAILED, ErrorSeverity.Critical);
this.severityRules.set(NetworkErrorType.PERMISSION_DENIED, ErrorSeverity.Critical);
this.severityRules.set(NetworkErrorType.TIMEOUT, ErrorSeverity.Medium);
this.severityRules.set(NetworkErrorType.RATE_LIMITED, ErrorSeverity.Medium);
this.severityRules.set(NetworkErrorType.INVALID_MESSAGE, ErrorSeverity.Low);
// 默认恢复策略
this.recoveryRules.set(NetworkErrorType.CONNECTION_FAILED, RecoveryStrategy.Reconnect);
this.recoveryRules.set(NetworkErrorType.CONNECTION_LOST, RecoveryStrategy.Reconnect);
this.recoveryRules.set(NetworkErrorType.TIMEOUT, RecoveryStrategy.Retry);
this.recoveryRules.set(NetworkErrorType.RATE_LIMITED, RecoveryStrategy.Retry);
this.recoveryRules.set(NetworkErrorType.INVALID_MESSAGE, RecoveryStrategy.Ignore);
this.recoveryRules.set(NetworkErrorType.AUTHENTICATION_FAILED, RecoveryStrategy.Escalate);
this.recoveryRules.set(NetworkErrorType.PERMISSION_DENIED, RecoveryStrategy.Escalate);
}
/**
* 获取错误趋势分析
*/
getErrorTrends() {
const totalErrors = this.stats.totalErrors;
if (totalErrors === 0) {
return { trend: 'stable', recommendation: '系统运行正常' };
}
const criticalRate = (this.stats.errorsBySeverity[ErrorSeverity.Critical] || 0) / totalErrors;
const recoveryRate = this.stats.recoveredErrors / totalErrors;
if (criticalRate > 0.1) {
return { trend: 'critical', recommendation: '存在严重错误,需要立即处理' };
} else if (recoveryRate < 0.5) {
return { trend: 'degrading', recommendation: '错误恢复率偏低,建议检查恢复策略' };
} else if (totalErrors > 100) {
return { trend: 'high_volume', recommendation: '错误量较大,建议分析根本原因' };
} else {
return { trend: 'stable', recommendation: '错误处理正常' };
}
}
}

View File

@@ -0,0 +1,381 @@
/**
* 心跳管理器
* 负责管理网络连接的心跳检测,包括延迟测算和连接健康检测
*/
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<HeartbeatEvents> = {};
// 定时器
private heartbeatTimer?: number;
private timeoutTimer?: number;
// 延迟测量
private pendingPings: Map<number, number> = new Map();
private latencyHistory: number[] = [];
private sequence = 0;
// 统计信息
private sentCount = 0;
private receivedCount = 0;
/**
* 发送心跳回调
*/
private sendHeartbeat?: (message: HeartbeatMessage) => void;
/**
* 构造函数
*/
constructor(config: Partial<HeartbeatConfig> = {}) {
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<K extends keyof HeartbeatEvents>(event: K, handler: HeartbeatEvents[K]): void {
this.eventHandlers[event] = handler;
}
/**
* 移除事件处理器
*/
off<K extends keyof HeartbeatEvents>(event: K): void {
delete this.eventHandlers[event];
}
/**
* 手动发送心跳
*/
sendHeartbeatNow(): void {
this.doSendHeartbeat();
}
/**
* 更新配置
*/
updateConfig(newConfig: Partial<HeartbeatConfig>): 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;
}
}

View File

@@ -23,14 +23,14 @@ export interface ITransport {
* @param clientId 客户端ID
* @param data 数据
*/
send(clientId: string, data: Buffer | string): void;
send(clientId: string, data: ArrayBuffer | string): void;
/**
* 广播数据到所有客户端
* @param data 数据
* @param exclude 排除的客户端ID列表
*/
broadcast(data: Buffer | string, exclude?: string[]): void;
broadcast(data: ArrayBuffer | string, exclude?: string[]): void;
/**
* 监听客户端连接事件
@@ -48,7 +48,7 @@ export interface ITransport {
* 监听消息接收事件
* @param handler 处理函数
*/
onMessage(handler: (clientId: string, data: Buffer | string) => void): void;
onMessage(handler: (clientId: string, data: ArrayBuffer | string) => void): void;
/**
* 监听错误事件
@@ -96,13 +96,13 @@ export interface IClientTransport {
* 发送数据到服务器
* @param data 数据
*/
send(data: Buffer | string): void;
send(data: ArrayBuffer | string): void;
/**
* 监听服务器消息
* @param handler 处理函数
*/
onMessage(handler: (data: Buffer | string) => void): void;
onMessage(handler: (data: ArrayBuffer | string) => void): void;
/**
* 监听连接状态变化

View File

@@ -0,0 +1,85 @@
/**
* 网络层专用的EventEmitter实现
* 继承自core库的Emitter提供简单的事件API
*/
import { Emitter } from '@esengine/ecs-framework';
/**
* 网络事件发射器,专为网络层设计
* 使用字符串或symbol作为事件类型简化API
*/
export class EventEmitter extends Emitter<string | symbol, void> {
constructor() {
super();
}
/**
* 添加事件监听器
* @param event 事件名称
* @param listener 监听函数
*/
public on(event: string | symbol, listener: Function): this {
this.addObserver(event, listener, undefined as void);
return this;
}
/**
* 添加一次性事件监听器
* @param event 事件名称
* @param listener 监听函数
*/
public once(event: string | symbol, listener: Function): this {
const onceWrapper = (...args: any[]) => {
listener.apply(this, args);
this.removeObserver(event, onceWrapper);
};
this.addObserver(event, onceWrapper, undefined as void);
return this;
}
/**
* 移除事件监听器
* @param event 事件名称
* @param listener 监听函数,不传则移除所有
*/
public off(event: string | symbol, listener?: Function): this {
if (listener) {
this.removeObserver(event, listener);
} else {
this.removeAllObservers(event);
}
return this;
}
/**
* 移除事件监听器(别名)
*/
public removeListener(event: string | symbol, listener: Function): this {
return this.off(event, listener);
}
/**
* 移除所有监听器
*/
public removeAllListeners(event?: string | symbol): this {
this.removeAllObservers(event);
return this;
}
/**
* 获取监听器数量
*/
public listenerCount(event: string | symbol): number {
return this.getObserverCount(event);
}
/**
* 发射事件兼容Node.js EventEmitter
* @param event 事件名称
* @param args 事件参数
*/
public override emit(event: string | symbol, ...args: any[]): boolean {
super.emit(event, ...args);
return true;
}
}

View File

@@ -0,0 +1,4 @@
/**
* 网络层工具类
*/
export * from './EventEmitter';