chore: 重写初始提交(清空历史,整理后全量提交)
This commit is contained in:
@@ -0,0 +1,268 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | YwxApp [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2026-2036 http://ywxapp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: ywxapp<admin@ywxapp.cn>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user