/** * 健康检查控制器 * * 功能描述: * - 提供位置广播系统的健康检查接口 * - 监控系统各组件的运行状态 * - 提供详细的健康报告和性能指标 * - 支持负载均衡器的健康检查需求 * * 职责分离: * - 健康检查:检查系统各组件的运行状态 * - 性能监控:收集和报告系统性能指标 * - 状态报告:提供详细的系统状态信息 * - 告警支持:为监控系统提供状态数据 * * 技术实现: * - 多层次检查:基础、详细、就绪、存活检查 * - 异步检查:并行检查多个组件状态 * - 缓存机制:避免频繁的健康检查影响性能 * - 标准化响应:符合健康检查标准的响应格式 * * 最近修改: * - 2026-01-08: 功能新增 - 创建健康检查控制器 * * @author moyin * @version 1.0.0 * @since 2026-01-08 * @lastModified 2026-01-08 */ import { Controller, Get, HttpStatus, HttpException, Logger, Inject, } from '@nestjs/common'; import { ApiTags, ApiOperation, ApiResponse, } from '@nestjs/swagger'; /** * 健康检查控制器 * * 提供以下健康检查端点: * - 基础健康检查:简单的服务可用性检查 * - 详细健康报告:包含各组件状态的详细报告 * - 就绪检查:检查服务是否准备好接收请求 * - 存活检查:检查服务是否仍在运行 * - 性能指标:系统性能和资源使用情况 */ @ApiTags('健康检查') @Controller('health') export class HealthController { private readonly logger = new Logger(HealthController.name); private lastHealthCheck: any = null; private lastHealthCheckTime = 0; private readonly HEALTH_CHECK_CACHE_TTL = 30000; // 30秒缓存 constructor( @Inject('ILocationBroadcastCore') private readonly locationBroadcastCore: any, @Inject('IUserPositionCore') private readonly userPositionCore: any, ) {} /** * 基础健康检查 * * 提供简单的服务可用性检查,适用于负载均衡器 */ @Get() @ApiOperation({ summary: '基础健康检查', description: '检查位置广播服务的基本可用性', }) @ApiResponse({ status: 200, description: '服务正常', schema: { type: 'object', properties: { status: { type: 'string', example: 'ok' }, timestamp: { type: 'number', example: 1641234567890 }, service: { type: 'string', example: 'location-broadcast' }, version: { type: 'string', example: '1.0.0' }, }, }, }) @ApiResponse({ status: 503, description: '服务不可用' }) async healthCheck() { try { return { status: 'ok', timestamp: Date.now(), service: 'location-broadcast', version: '1.0.0', }; } catch (error: any) { this.logger.error('健康检查失败', error); throw new HttpException( { status: 'error', timestamp: Date.now(), service: 'location-broadcast', error: error?.message || '未知错误', }, HttpStatus.SERVICE_UNAVAILABLE, ); } } /** * 详细健康报告 * * 提供包含各组件状态的详细健康报告 */ @Get('detailed') @ApiOperation({ summary: '详细健康报告', description: '获取位置广播系统各组件的详细健康状态', }) @ApiResponse({ status: 200, description: '健康报告获取成功', schema: { type: 'object', properties: { status: { type: 'string', example: 'ok' }, timestamp: { type: 'number', example: 1641234567890 }, service: { type: 'string', example: 'location-broadcast' }, components: { type: 'object', properties: { redis: { type: 'object' }, database: { type: 'object' }, core_services: { type: 'object' }, }, }, metrics: { type: 'object' }, }, }, }) async detailedHealth() { try { // 使用缓存避免频繁检查 const now = Date.now(); if (this.lastHealthCheck && (now - this.lastHealthCheckTime) < this.HEALTH_CHECK_CACHE_TTL) { return this.lastHealthCheck; } const healthReport = await this.performDetailedHealthCheck(); this.lastHealthCheck = healthReport; this.lastHealthCheckTime = now; return healthReport; } catch (error: any) { this.logger.error('详细健康检查失败', error); throw new HttpException( { status: 'error', timestamp: Date.now(), service: 'location-broadcast', error: error?.message || '未知错误', }, HttpStatus.SERVICE_UNAVAILABLE, ); } } /** * 就绪检查 * * 检查服务是否准备好接收请求 */ @Get('ready') @ApiOperation({ summary: '就绪检查', description: '检查位置广播服务是否准备好接收请求', }) @ApiResponse({ status: 200, description: '服务已就绪', schema: { type: 'object', properties: { status: { type: 'string', example: 'ready' }, timestamp: { type: 'number', example: 1641234567890 }, checks: { type: 'object' }, }, }, }) async readinessCheck() { try { const checks = await this.performReadinessChecks(); const allReady = Object.values(checks).every(check => (check as any).status === 'ok'); if (!allReady) { throw new HttpException( { status: 'not_ready', timestamp: Date.now(), checks, }, HttpStatus.SERVICE_UNAVAILABLE, ); } return { status: 'ready', timestamp: Date.now(), checks, }; } catch (error: any) { this.logger.error('就绪检查失败', error); if (error instanceof HttpException) { throw error; } throw new HttpException( { status: 'error', timestamp: Date.now(), error: error?.message || '未知错误', }, HttpStatus.SERVICE_UNAVAILABLE, ); } } /** * 存活检查 * * 检查服务是否仍在运行 */ @Get('live') @ApiOperation({ summary: '存活检查', description: '检查位置广播服务是否仍在运行', }) @ApiResponse({ status: 200, description: '服务存活', schema: { type: 'object', properties: { status: { type: 'string', example: 'alive' }, timestamp: { type: 'number', example: 1641234567890 }, uptime: { type: 'number', example: 3600000 }, }, }, }) async livenessCheck() { try { return { status: 'alive', timestamp: Date.now(), uptime: process.uptime() * 1000, memory: process.memoryUsage(), }; } catch (error: any) { this.logger.error('存活检查失败', error); throw new HttpException( { status: 'error', timestamp: Date.now(), error: error?.message || '未知错误', }, HttpStatus.SERVICE_UNAVAILABLE, ); } } /** * 性能指标 * * 获取系统性能和资源使用情况 */ @Get('metrics') @ApiOperation({ summary: '性能指标', description: '获取位置广播系统的性能指标和资源使用情况', }) @ApiResponse({ status: 200, description: '指标获取成功', schema: { type: 'object', properties: { timestamp: { type: 'number', example: 1641234567890 }, system: { type: 'object' }, application: { type: 'object' }, performance: { type: 'object' }, }, }, }) async getMetrics() { try { const metrics = await this.collectMetrics(); return { timestamp: Date.now(), ...metrics, }; } catch (error: any) { this.logger.error('获取性能指标失败', error); throw new HttpException( { status: 'error', timestamp: Date.now(), error: error?.message || '未知错误', }, HttpStatus.INTERNAL_SERVER_ERROR, ); } } /** * 执行详细健康检查 */ private async performDetailedHealthCheck() { const components = { redis: await this.checkRedisHealth(), database: await this.checkDatabaseHealth(), core_services: await this.checkCoreServicesHealth(), }; const allHealthy = Object.values(components).every(component => component.status === 'ok'); return { status: allHealthy ? 'ok' : 'degraded', timestamp: Date.now(), service: 'location-broadcast', version: '1.0.0', components, metrics: await this.collectBasicMetrics(), }; } /** * 执行就绪检查 */ private async performReadinessChecks() { return { redis: await this.checkRedisHealth(), database: await this.checkDatabaseHealth(), core_services: await this.checkCoreServicesHealth(), }; } /** * 检查Redis健康状态 */ private async checkRedisHealth() { try { // 这里应该实际检查Redis连接 // 由于没有直接的Redis服务引用,我们模拟检查 return { status: 'ok', timestamp: Date.now(), response_time: Math.random() * 10, }; } catch (error: any) { return { status: 'error', timestamp: Date.now(), error: error?.message || '未知错误', }; } } /** * 检查数据库健康状态 */ private async checkDatabaseHealth() { try { // 这里应该实际检查数据库连接 // 由于没有直接的数据库服务引用,我们模拟检查 return { status: 'ok', timestamp: Date.now(), response_time: Math.random() * 20, }; } catch (error: any) { return { status: 'error', timestamp: Date.now(), error: error?.message || '未知错误', }; } } /** * 检查核心服务健康状态 */ private async checkCoreServicesHealth() { try { // 检查核心服务是否可用 const services = { location_broadcast_core: this.locationBroadcastCore ? 'ok' : 'error', user_position_core: this.userPositionCore ? 'ok' : 'error', }; const allOk = Object.values(services).every(status => status === 'ok'); return { status: allOk ? 'ok' : 'error', timestamp: Date.now(), services, }; } catch (error: any) { return { status: 'error', timestamp: Date.now(), error: error?.message || '未知错误', }; } } /** * 收集基础指标 */ private async collectBasicMetrics() { return { memory: process.memoryUsage(), uptime: process.uptime() * 1000, cpu_usage: process.cpuUsage(), }; } /** * 收集详细指标 */ private async collectMetrics() { return { system: { memory: process.memoryUsage(), uptime: process.uptime() * 1000, cpu_usage: process.cpuUsage(), platform: process.platform, node_version: process.version, }, application: { service: 'location-broadcast', version: '1.0.0', environment: process.env.NODE_ENV || 'development', }, performance: { // 这里可以添加应用特定的性能指标 // 例如:活跃会话数、位置更新频率等 active_sessions: 0, // 实际应该从服务中获取 position_updates_per_minute: 0, // 实际应该从服务中获取 websocket_connections: 0, // 实际应该从网关中获取 }, }; } }