// +---------------------------------------------------------------------- declare(strict_types=1); namespace ywxapp\service; use think\facade\Cache; /** * 插件性能监控服务 * * 功能特性: * - 监控插件执行时间 * - 监控插件内存使用 * - 记录插件调用次数 * - 检测性能异常 */ class AddonPerformanceMonitor { /** 监控数据缓存前缀 */ const CACHE_PREFIX = 'addon_perf_'; /** 监控数据缓存时间(秒) */ const CACHE_EXPIRE = 300; /** 性能阈值(秒) */ const PERFORMANCE_THRESHOLD = 2.0; /** 内存阈值(字节) */ const MEMORY_THRESHOLD = 10485760; // 10MB /** * 监控插件执行性能 * * @param string $addon 插件名称 * @param string $action 操作类型 * @param callable $callback 要执行的回调 * @return mixed */ public static function measure(string $addon, string $action, callable $callback) { $startTime = microtime(true); $startMemory = memory_get_usage(); $success = true; $error = null; try { $result = $callback(); return $result; } catch (\Exception $e) { $success = false; $error = $e->getMessage(); throw $e; } finally { $endTime = microtime(true); $endMemory = memory_get_usage(); $performanceData = [ 'addon' => $addon, 'action' => $action, 'execution_time' => $endTime - $startTime, 'memory_usage' => $endMemory - $startMemory, 'peak_memory' => memory_get_peak_usage(), 'timestamp' => time(), 'success' => $success, 'error' => $error ]; // 保存性能数据 self::savePerformanceData($addon, $performanceData); // 性能异常检测 self::checkPerformanceAnomaly($addon, $performanceData); } } /** * 保存性能数据 * * @param string $addon 插件名称 * @param array $data 性能数据 */ private static function savePerformanceData(string $addon, array $data): void { try { $cacheKey = self::CACHE_PREFIX . $addon; $existingData = Cache::get($cacheKey, []); // 只保留最近100条记录 $existingData[] = $data; if (count($existingData) > 100) { $existingData = array_slice($existingData, -100); } Cache::set($cacheKey, $existingData, self::CACHE_EXPIRE); } catch (\Throwable $e) { // 监控落库失败绝不影响业务响应 \think\facade\Log::warning('插件性能数据写入失败:' . $e->getMessage()); } } /** * 检测性能异常 * * @param string $addon 插件名称 * @param array $data 性能数据 */ private static function checkPerformanceAnomaly(string $addon, array $data): void { $anomalies = []; // 执行时间检测 if ($data['execution_time'] > self::PERFORMANCE_THRESHOLD) { $anomalies[] = sprintf( "插件 %s 的 %s 操作执行时间过长:%.3f秒", $addon, $data['action'], $data['execution_time'] ); } // 内存使用检测 if ($data['memory_usage'] > self::MEMORY_THRESHOLD) { $anomalies[] = sprintf( "插件 %s 的 %s 操作内存使用过多:%.2fMB", $addon, $data['action'], $data['memory_usage'] / 1048576 ); } // 记录异常 if (!empty($anomalies)) { foreach ($anomalies as $anomaly) { \think\facade\Log::warning('插件性能异常:' . $anomaly); } } } /** * 获取插件性能统计 * * @param string $addon 插件名称 * @return array */ public static function getPerformanceStats(string $addon): array { $cacheKey = self::CACHE_PREFIX . $addon; $data = Cache::get($cacheKey, []); if (empty($data)) { return [ 'total_calls' => 0, 'avg_execution_time' => 0, 'max_execution_time' => 0, 'avg_memory_usage' => 0, 'max_memory_usage' => 0, 'success_rate' => 0, 'recent_performance' => [] ]; } $totalCalls = count($data); $totalExecutionTime = array_sum(array_column($data, 'execution_time')); $maxExecutionTime = max(array_column($data, 'execution_time')); $totalMemoryUsage = array_sum(array_column($data, 'memory_usage')); $maxMemoryUsage = max(array_column($data, 'memory_usage')); $successCount = count(array_filter($data, function($item) { return $item['success'] === true; })); // 最近10次性能数据 $recentPerformance = array_slice($data, -10); return [ 'total_calls' => $totalCalls, 'avg_execution_time' => $totalCalls > 0 ? $totalExecutionTime / $totalCalls : 0, 'max_execution_time' => $maxExecutionTime, 'avg_memory_usage' => $totalCalls > 0 ? $totalMemoryUsage / $totalCalls : 0, 'max_memory_usage' => $maxMemoryUsage, 'success_rate' => $totalCalls > 0 ? ($successCount / $totalCalls) * 100 : 0, 'recent_performance' => $recentPerformance ]; } /** * 获取所有插件性能概览 * * @return array */ public static function getAllPerformanceOverview(): array { $overview = []; $addon = \think\facade\Config::get('addon', []); foreach ($addon as $addon) { if (isset($addon['name'])) { $overview[$addon['name']] = self::getPerformanceStats($addon['name']); } } return $overview; } /** * 清除性能监控数据 * * @param string|null $addon 插件名称,为null时清除所有 */ public static function clearPerformanceData(?string $addon = null): void { if ($addon === null) { // 清除所有插件的性能数据 $addon = \think\facade\Config::get('addon', []); foreach ($addon as $addonInfo) { if (isset($addonInfo['name'])) { Cache::delete(self::CACHE_PREFIX . $addonInfo['name']); } } } else { Cache::delete(self::CACHE_PREFIX . $addon); } } /** * 获取性能问题插件列表 * * @return array */ public static function getProblematicaddon(): array { $problematic = []; $overview = self::getAllPerformanceOverview(); foreach ($overview as $addon => $stats) { $issues = []; if ($stats['avg_execution_time'] > self::PERFORMANCE_THRESHOLD) { $issues[] = '平均执行时间过长'; } if ($stats['avg_memory_usage'] > self::MEMORY_THRESHOLD) { $issues[] = '平均内存使用过多'; } // 仅在存在采样数据时判定成功率,避免“无数据=0%”的假阳性 if ($stats['total_calls'] > 0 && $stats['success_rate'] < 90) { $issues[] = '成功率过低'; } if (!empty($issues)) { $problematic[$addon] = [ 'issues' => $issues, 'stats' => $stats ]; } } return $problematic; } }