feat:实现位置广播系统

- 添加位置广播核心控制器和服务
- 实现健康检查和位置同步功能
- 添加WebSocket实时位置更新支持
- 完善位置广播的测试覆盖
This commit is contained in:
moyin
2026-01-08 23:05:52 +08:00
parent 6924416bbd
commit c31cbe559d
27 changed files with 12212 additions and 0 deletions

View File

@@ -0,0 +1,658 @@
/**
* 性能监控中间件
*
* 功能描述:
* - 监控WebSocket事件处理的性能指标
* - 收集响应时间、吞吐量等关键数据
* - 提供实时性能统计和报告
* - 支持性能预警和异常检测
*
* 职责分离:
* - 性能收集:记录事件处理的时间和资源消耗
* - 数据分析:计算平均值、百分位数等统计指标
* - 监控报警:检测性能异常和瓶颈
* - 报告生成:提供详细的性能分析报告
*
* 技术实现:
* - 高精度计时使用process.hrtime进行精确测量
* - 内存优化:循环缓冲区存储历史数据
* - 异步处理:不影响正常业务流程
* - 统计算法:实时计算各种性能指标
*
* 最近修改:
* - 2026-01-08: 代码重构 - 提取魔法数字为常量,优化代码质量 (修改者: moyin)
*
* @author moyin
* @version 1.1.0
* @since 2026-01-08
* @lastModified 2026-01-08
*/
import { Injectable, Logger } from '@nestjs/common';
import { Socket } from 'socket.io';
/**
* 性能指标接口
*/
interface PerformanceMetric {
/** 事件名称 */
eventName: string;
/** 处理时间(毫秒) */
duration: number;
/** 时间戳 */
timestamp: number;
/** 用户ID */
userId?: string;
/** Socket ID */
socketId: string;
/** 是否成功 */
success: boolean;
/** 错误信息 */
error?: string;
}
/**
* 事件统计信息
*/
export interface EventStats {
/** 事件名称 */
eventName: string;
/** 总请求数 */
totalRequests: number;
/** 成功请求数 */
successRequests: number;
/** 失败请求数 */
failedRequests: number;
/** 平均响应时间 */
avgDuration: number;
/** 最小响应时间 */
minDuration: number;
/** 最大响应时间 */
maxDuration: number;
/** 95百分位响应时间 */
p95Duration: number;
/** 99百分位响应时间 */
p99Duration: number;
/** 每秒请求数 */
requestsPerSecond: number;
/** 成功率 */
successRate: number;
}
/**
* 系统性能概览
*/
export interface SystemPerformance {
/** 总连接数 */
totalConnections: number;
/** 活跃连接数 */
activeConnections: number;
/** 总事件数 */
totalEvents: number;
/** 平均响应时间 */
avgResponseTime: number;
/** 系统吞吐量(事件/秒) */
throughput: number;
/** 错误率 */
errorRate: number;
/** 内存使用情况 */
memoryUsage: {
used: number;
total: number;
percentage: number;
};
/** 统计时间戳 */
timestamp: number;
}
/**
* 性能预警配置
*/
interface AlertConfig {
/** 响应时间阈值(毫秒) */
responseTimeThreshold: number;
/** 错误率阈值(百分比) */
errorRateThreshold: number;
/** 吞吐量下限 */
throughputThreshold: number;
/** 内存使用率阈值 */
memoryThreshold: number;
/** 是否启用预警 */
enabled: boolean;
}
@Injectable()
export class PerformanceMonitorMiddleware {
private readonly logger = new Logger(PerformanceMonitorMiddleware.name);
/** 性能指标缓存最大数量 */
private static readonly MAX_METRICS = 10000;
/** 统计更新间隔(毫秒) */
private static readonly STATS_UPDATE_INTERVAL = 10000;
/** 清理间隔(毫秒) */
private static readonly CLEANUP_INTERVAL = 300000;
/** 响应时间阈值(毫秒) */
private static readonly RESPONSE_TIME_THRESHOLD = 1000;
/** 错误率阈值(百分比) */
private static readonly ERROR_RATE_THRESHOLD = 5;
/** 吞吐量阈值(事件/秒) */
private static readonly THROUGHPUT_THRESHOLD = 10;
/** 内存使用率阈值(百分比) */
private static readonly MEMORY_THRESHOLD = 80;
/** 时间转换常量 */
private static readonly MILLISECONDS_PER_SECOND = 1000;
private static readonly SECONDS_PER_MINUTE = 60;
private static readonly MINUTES_PER_HOUR = 60;
private static readonly HOURS_PER_DAY = 24;
/** 百分位数计算常量 */
private static readonly PERCENTILE_95 = 95;
private static readonly PERCENTILE_99 = 99;
/** 精度计算常量 */
private static readonly PRECISION_MULTIPLIER = 100;
private static readonly HIGH_PRECISION_MULTIPLIER = 10000;
/** 内存单位转换 */
private static readonly BYTES_PER_KB = 1024;
private static readonly KB_PER_MB = 1024;
/** 性能趋势间隔(分钟) */
private static readonly TREND_INTERVAL_MINUTES = 5;
/** 窗口数据保留倍数 */
private static readonly WINDOW_RETENTION_MULTIPLIER = 10;
/** 报告默认时间范围(小时) */
private static readonly DEFAULT_REPORT_HOURS = 1;
/** 慢事件默认限制数量 */
private static readonly DEFAULT_SLOW_EVENTS_LIMIT = 10;
/** 性能指标缓存(循环缓冲区) */
private readonly metrics: PerformanceMetric[] = [];
private readonly maxMetrics = PerformanceMonitorMiddleware.MAX_METRICS;
private metricsIndex = 0;
/** 事件统计缓存 */
private readonly eventStats = new Map<string, EventStats>();
/** 连接统计 */
private connectionCount = 0;
private activeConnections = new Set<string>();
/** 预警配置 */
private alertConfig: AlertConfig = {
responseTimeThreshold: PerformanceMonitorMiddleware.RESPONSE_TIME_THRESHOLD,
errorRateThreshold: PerformanceMonitorMiddleware.ERROR_RATE_THRESHOLD,
throughputThreshold: PerformanceMonitorMiddleware.THROUGHPUT_THRESHOLD,
memoryThreshold: PerformanceMonitorMiddleware.MEMORY_THRESHOLD,
enabled: true,
};
constructor() {
// 定期更新统计信息
setInterval(() => {
this.updateEventStats();
this.checkAlerts();
}, PerformanceMonitorMiddleware.STATS_UPDATE_INTERVAL);
// 定期清理过期数据
setInterval(() => {
this.cleanupOldMetrics();
}, PerformanceMonitorMiddleware.CLEANUP_INTERVAL);
}
/**
* 开始监控事件处理
*
* @param eventName 事件名称
* @param client WebSocket客户端
* @returns 监控上下文
*/
startMonitoring(eventName: string, client: Socket): { startTime: [number, number]; eventName: string; client: Socket } {
const startTime = process.hrtime();
// 记录连接
this.activeConnections.add(client.id);
return { startTime, eventName, client };
}
/**
* 结束监控并记录指标
*
* @param context 监控上下文
* @param success 是否成功
* @param error 错误信息
*/
endMonitoring(
context: { startTime: [number, number]; eventName: string; client: Socket },
success: boolean = true,
error?: string,
): void {
const endTime = process.hrtime(context.startTime);
const duration = endTime[0] * PerformanceMonitorMiddleware.MILLISECONDS_PER_SECOND + endTime[1] / (PerformanceMonitorMiddleware.MILLISECONDS_PER_SECOND * PerformanceMonitorMiddleware.MILLISECONDS_PER_SECOND);
const metric: PerformanceMetric = {
eventName: context.eventName,
duration,
timestamp: Date.now(),
userId: (context.client as any).userId,
socketId: context.client.id,
success,
error,
};
this.recordMetric(metric);
}
/**
* 记录连接事件
*
* @param client WebSocket客户端
* @param connected 是否连接
*/
recordConnection(client: Socket, connected: boolean): void {
if (connected) {
this.connectionCount++;
this.activeConnections.add(client.id);
} else {
this.activeConnections.delete(client.id);
}
this.logger.debug('连接状态变更', {
socketId: client.id,
connected,
totalConnections: this.connectionCount,
activeConnections: this.activeConnections.size,
});
}
/**
* 获取事件统计信息
*
* @param eventName 事件名称
* @returns 统计信息
*/
getEventStats(eventName?: string): EventStats[] {
if (eventName) {
const stats = this.eventStats.get(eventName);
return stats ? [stats] : [];
}
return Array.from(this.eventStats.values());
}
/**
* 获取系统性能概览
*
* @returns 系统性能信息
*/
getSystemPerformance(): SystemPerformance {
const now = Date.now();
const recentMetrics = this.getRecentMetrics(PerformanceMonitorMiddleware.SECONDS_PER_MINUTE * PerformanceMonitorMiddleware.MILLISECONDS_PER_SECOND); // 最近1分钟的数据
const totalEvents = recentMetrics.length;
const successfulEvents = recentMetrics.filter(m => m.success).length;
const avgResponseTime = totalEvents > 0
? recentMetrics.reduce((sum, m) => sum + m.duration, 0) / totalEvents
: 0;
const throughput = totalEvents / PerformanceMonitorMiddleware.SECONDS_PER_MINUTE; // 每秒事件数
const errorRate = totalEvents > 0 ? ((totalEvents - successfulEvents) / totalEvents) * PerformanceMonitorMiddleware.PRECISION_MULTIPLIER : 0;
// 获取内存使用情况
const memUsage = process.memoryUsage();
const memoryUsage = {
used: Math.round(memUsage.heapUsed / PerformanceMonitorMiddleware.BYTES_PER_KB / PerformanceMonitorMiddleware.KB_PER_MB), // MB
total: Math.round(memUsage.heapTotal / PerformanceMonitorMiddleware.BYTES_PER_KB / PerformanceMonitorMiddleware.KB_PER_MB), // MB
percentage: Math.round((memUsage.heapUsed / memUsage.heapTotal) * PerformanceMonitorMiddleware.PRECISION_MULTIPLIER),
};
return {
totalConnections: this.connectionCount,
activeConnections: this.activeConnections.size,
totalEvents,
avgResponseTime: Math.round(avgResponseTime * PerformanceMonitorMiddleware.PRECISION_MULTIPLIER) / PerformanceMonitorMiddleware.PRECISION_MULTIPLIER,
throughput: Math.round(throughput * PerformanceMonitorMiddleware.PRECISION_MULTIPLIER) / PerformanceMonitorMiddleware.PRECISION_MULTIPLIER,
errorRate: Math.round(errorRate * PerformanceMonitorMiddleware.PRECISION_MULTIPLIER) / PerformanceMonitorMiddleware.PRECISION_MULTIPLIER,
memoryUsage,
timestamp: now,
};
}
/**
* 获取性能报告
*
* @param timeRange 时间范围(毫秒)
* @returns 性能报告
*/
getPerformanceReport(timeRange: number = PerformanceMonitorMiddleware.DEFAULT_REPORT_HOURS * PerformanceMonitorMiddleware.MINUTES_PER_HOUR * PerformanceMonitorMiddleware.SECONDS_PER_MINUTE * PerformanceMonitorMiddleware.MILLISECONDS_PER_SECOND): any {
const metrics = this.getRecentMetrics(timeRange);
const eventGroups = this.groupMetricsByEvent(metrics);
const report = {
timeRange,
totalMetrics: metrics.length,
systemPerformance: this.getSystemPerformance(),
eventStats: this.getEventStats(),
topSlowEvents: this.getTopSlowEvents(metrics, PerformanceMonitorMiddleware.DEFAULT_SLOW_EVENTS_LIMIT),
errorSummary: this.getErrorSummary(metrics),
performanceTrends: this.getPerformanceTrends(metrics),
timestamp: Date.now(),
};
return report;
}
/**
* 更新预警配置
*
* @param config 新配置
*/
updateAlertConfig(config: Partial<AlertConfig>): void {
this.alertConfig = { ...this.alertConfig, ...config };
this.logger.log('性能预警配置已更新', {
config: this.alertConfig,
timestamp: new Date().toISOString(),
});
}
/**
* 清理性能数据
*/
clearMetrics(): void {
this.metrics.length = 0;
this.metricsIndex = 0;
this.eventStats.clear();
this.logger.log('性能监控数据已清理', {
timestamp: new Date().toISOString(),
});
}
/**
* 记录性能指标
*
* @param metric 性能指标
* @private
*/
private recordMetric(metric: PerformanceMetric): void {
// 使用循环缓冲区存储指标
this.metrics[this.metricsIndex] = metric;
this.metricsIndex = (this.metricsIndex + 1) % this.maxMetrics;
// 记录慢请求
if (metric.duration > this.alertConfig.responseTimeThreshold) {
this.logger.warn('检测到慢请求', {
eventName: metric.eventName,
duration: metric.duration,
userId: metric.userId,
socketId: metric.socketId,
threshold: this.alertConfig.responseTimeThreshold,
});
}
// 记录错误
if (!metric.success) {
this.logger.error('事件处理失败', {
eventName: metric.eventName,
error: metric.error,
userId: metric.userId,
socketId: metric.socketId,
duration: metric.duration,
});
}
}
/**
* 更新事件统计信息
*
* @private
*/
private updateEventStats(): void {
const recentMetrics = this.getRecentMetrics(60000); // 最近1分钟
const eventGroups = this.groupMetricsByEvent(recentMetrics);
for (const [eventName, metrics] of eventGroups.entries()) {
const durations = metrics.map(m => m.duration).sort((a, b) => a - b);
const successCount = metrics.filter(m => m.success).length;
const stats: EventStats = {
eventName,
totalRequests: metrics.length,
successRequests: successCount,
failedRequests: metrics.length - successCount,
avgDuration: Math.round((durations.reduce((sum, d) => sum + d, 0) / durations.length) * PerformanceMonitorMiddleware.PRECISION_MULTIPLIER) / PerformanceMonitorMiddleware.PRECISION_MULTIPLIER,
minDuration: durations[0] || 0,
maxDuration: durations[durations.length - 1] || 0,
p95Duration: this.getPercentile(durations, PerformanceMonitorMiddleware.PERCENTILE_95),
p99Duration: this.getPercentile(durations, PerformanceMonitorMiddleware.PERCENTILE_99),
requestsPerSecond: Math.round((metrics.length / PerformanceMonitorMiddleware.SECONDS_PER_MINUTE) * PerformanceMonitorMiddleware.PRECISION_MULTIPLIER) / PerformanceMonitorMiddleware.PRECISION_MULTIPLIER,
successRate: Math.round((successCount / metrics.length) * PerformanceMonitorMiddleware.HIGH_PRECISION_MULTIPLIER) / PerformanceMonitorMiddleware.PRECISION_MULTIPLIER,
};
this.eventStats.set(eventName, stats);
}
}
/**
* 检查性能预警
*
* @private
*/
private checkAlerts(): void {
if (!this.alertConfig.enabled) {
return;
}
const systemPerf = this.getSystemPerformance();
// 检查响应时间
if (systemPerf.avgResponseTime > this.alertConfig.responseTimeThreshold) {
this.logger.warn('响应时间过高预警', {
current: systemPerf.avgResponseTime,
threshold: this.alertConfig.responseTimeThreshold,
timestamp: new Date().toISOString(),
});
}
// 检查错误率
if (systemPerf.errorRate > this.alertConfig.errorRateThreshold) {
this.logger.warn('错误率过高预警', {
current: systemPerf.errorRate,
threshold: this.alertConfig.errorRateThreshold,
timestamp: new Date().toISOString(),
});
}
// 检查吞吐量
if (systemPerf.throughput < this.alertConfig.throughputThreshold) {
this.logger.warn('吞吐量过低预警', {
current: systemPerf.throughput,
threshold: this.alertConfig.throughputThreshold,
timestamp: new Date().toISOString(),
});
}
// 检查内存使用
if (systemPerf.memoryUsage.percentage > this.alertConfig.memoryThreshold) {
this.logger.warn('内存使用率过高预警', {
current: systemPerf.memoryUsage.percentage,
threshold: this.alertConfig.memoryThreshold,
used: systemPerf.memoryUsage.used,
total: systemPerf.memoryUsage.total,
timestamp: new Date().toISOString(),
});
}
}
/**
* 获取最近的性能指标
*
* @param timeRange 时间范围(毫秒)
* @returns 性能指标列表
* @private
*/
private getRecentMetrics(timeRange: number): PerformanceMetric[] {
const now = Date.now();
const cutoff = now - timeRange;
return this.metrics.filter(metric => metric && metric.timestamp > cutoff);
}
/**
* 按事件名称分组指标
*
* @param metrics 性能指标列表
* @returns 分组后的指标
* @private
*/
private groupMetricsByEvent(metrics: PerformanceMetric[]): Map<string, PerformanceMetric[]> {
const groups = new Map<string, PerformanceMetric[]>();
for (const metric of metrics) {
if (!groups.has(metric.eventName)) {
groups.set(metric.eventName, []);
}
groups.get(metric.eventName)!.push(metric);
}
return groups;
}
/**
* 计算百分位数
*
* @param values 数值数组(已排序)
* @param percentile 百分位数
* @returns 百分位值
* @private
*/
private getPercentile(values: number[], percentile: number): number {
if (values.length === 0) return 0;
const index = Math.ceil((percentile / PerformanceMonitorMiddleware.PRECISION_MULTIPLIER) * values.length) - 1;
return Math.round(values[Math.max(0, index)] * PerformanceMonitorMiddleware.PRECISION_MULTIPLIER) / PerformanceMonitorMiddleware.PRECISION_MULTIPLIER;
}
/**
* 获取最慢的事件
*
* @param metrics 性能指标
* @param limit 限制数量
* @returns 最慢事件列表
* @private
*/
private getTopSlowEvents(metrics: PerformanceMetric[], limit: number): PerformanceMetric[] {
return metrics
.sort((a, b) => b.duration - a.duration)
.slice(0, limit);
}
/**
* 获取错误摘要
*
* @param metrics 性能指标
* @returns 错误摘要
* @private
*/
private getErrorSummary(metrics: PerformanceMetric[]): any {
const errors = metrics.filter(m => !m.success);
const errorGroups = new Map<string, number>();
for (const error of errors) {
const key = error.error || 'Unknown Error';
errorGroups.set(key, (errorGroups.get(key) || 0) + 1);
}
return {
totalErrors: errors.length,
errorRate: metrics.length > 0 ? (errors.length / metrics.length) * PerformanceMonitorMiddleware.PRECISION_MULTIPLIER : 0,
errorTypes: Array.from(errorGroups.entries()).map(([error, count]) => ({ error, count })),
};
}
/**
* 获取性能趋势
*
* @param metrics 性能指标
* @returns 性能趋势数据
* @private
*/
private getPerformanceTrends(metrics: PerformanceMetric[]): any {
// 按5分钟间隔分组
const intervals = new Map<number, PerformanceMetric[]>();
const intervalSize = PerformanceMonitorMiddleware.TREND_INTERVAL_MINUTES * PerformanceMonitorMiddleware.SECONDS_PER_MINUTE * PerformanceMonitorMiddleware.MILLISECONDS_PER_SECOND;
for (const metric of metrics) {
const interval = Math.floor(metric.timestamp / intervalSize) * intervalSize;
if (!intervals.has(interval)) {
intervals.set(interval, []);
}
intervals.get(interval)!.push(metric);
}
return Array.from(intervals.entries()).map(([interval, intervalMetrics]) => ({
timestamp: interval,
avgDuration: intervalMetrics.reduce((sum, m) => sum + m.duration, 0) / intervalMetrics.length,
requestCount: intervalMetrics.length,
errorCount: intervalMetrics.filter(m => !m.success).length,
}));
}
/**
* 清理过期指标
*
* @private
*/
private cleanupOldMetrics(): void {
const cutoff = Date.now() - (PerformanceMonitorMiddleware.HOURS_PER_DAY * PerformanceMonitorMiddleware.MINUTES_PER_HOUR * PerformanceMonitorMiddleware.SECONDS_PER_MINUTE * PerformanceMonitorMiddleware.MILLISECONDS_PER_SECOND);
let cleanedCount = 0;
for (let i = 0; i < this.metrics.length; i++) {
if (this.metrics[i] && this.metrics[i].timestamp < cutoff) {
delete this.metrics[i];
cleanedCount++;
}
}
if (cleanedCount > 0) {
this.logger.debug('清理过期性能指标', {
cleanedCount,
remainingCount: this.metrics.filter(m => m).length,
timestamp: new Date().toISOString(),
});
}
}
}
/**
* 性能监控装饰器
*
* 使用示例:
* ```typescript
* @PerformanceMonitor('position_update')
* @SubscribeMessage('position_update')
* async handlePositionUpdate(@ConnectedSocket() client: AuthenticatedSocket, @MessageBody() message: PositionUpdateMessage) {
* // 处理位置更新
* }
* ```
*/
export function PerformanceMonitor(eventName?: string) {
return function (_target: any, propertyName: string, descriptor: PropertyDescriptor) {
const method = descriptor.value;
const finalEventName = eventName || propertyName;
descriptor.value = async function (...args: any[]) {
const client = args[0] as Socket;
const performanceMonitor = new PerformanceMonitorMiddleware();
const context = performanceMonitor.startMonitoring(finalEventName, client);
try {
const result = await method.apply(this, args);
performanceMonitor.endMonitoring(context, true);
return result;
} catch (error) {
performanceMonitor.endMonitoring(context, false, error instanceof Error ? error.message : String(error));
throw error;
}
};
};
}