mirror of
https://github.com/Minidoracat/mcp-feedback-enhanced.git
synced 2025-07-27 10:42:25 +08:00
291 lines
11 KiB
Python
291 lines
11 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
壓縮性能監控工具
|
|
================
|
|
|
|
監控 Gzip 壓縮的性能效果,包括壓縮比率、響應時間和文件大小統計。
|
|
提供實時性能數據和優化建議。
|
|
"""
|
|
|
|
import time
|
|
import threading
|
|
from typing import Dict, List, Optional, Tuple
|
|
from dataclasses import dataclass, field
|
|
from datetime import datetime, timedelta
|
|
import json
|
|
|
|
|
|
@dataclass
|
|
class CompressionMetrics:
|
|
"""壓縮指標數據類"""
|
|
timestamp: datetime
|
|
path: str
|
|
original_size: int
|
|
compressed_size: int
|
|
compression_ratio: float
|
|
response_time: float
|
|
content_type: str
|
|
was_compressed: bool
|
|
|
|
|
|
@dataclass
|
|
class CompressionSummary:
|
|
"""壓縮摘要統計"""
|
|
total_requests: int = 0
|
|
compressed_requests: int = 0
|
|
total_original_bytes: int = 0
|
|
total_compressed_bytes: int = 0
|
|
average_compression_ratio: float = 0.0
|
|
average_response_time: float = 0.0
|
|
compression_percentage: float = 0.0
|
|
bandwidth_saved: int = 0
|
|
top_compressed_paths: List[Tuple[str, float]] = field(default_factory=list)
|
|
|
|
|
|
class CompressionMonitor:
|
|
"""壓縮性能監控器"""
|
|
|
|
def __init__(self, max_metrics: int = 1000):
|
|
self.max_metrics = max_metrics
|
|
self.metrics: List[CompressionMetrics] = []
|
|
self.lock = threading.Lock()
|
|
self._start_time = datetime.now()
|
|
|
|
# 路徑統計
|
|
self.path_stats: Dict[str, Dict] = {}
|
|
|
|
# 內容類型統計
|
|
self.content_type_stats: Dict[str, Dict] = {}
|
|
|
|
def record_request(self,
|
|
path: str,
|
|
original_size: int,
|
|
compressed_size: int,
|
|
response_time: float,
|
|
content_type: str = "",
|
|
was_compressed: bool = False):
|
|
"""記錄請求的壓縮數據"""
|
|
|
|
compression_ratio = 0.0
|
|
if original_size > 0 and was_compressed:
|
|
compression_ratio = (1 - compressed_size / original_size) * 100
|
|
|
|
metric = CompressionMetrics(
|
|
timestamp=datetime.now(),
|
|
path=path,
|
|
original_size=original_size,
|
|
compressed_size=compressed_size,
|
|
compression_ratio=compression_ratio,
|
|
response_time=response_time,
|
|
content_type=content_type,
|
|
was_compressed=was_compressed
|
|
)
|
|
|
|
with self.lock:
|
|
self.metrics.append(metric)
|
|
|
|
# 限制記錄數量
|
|
if len(self.metrics) > self.max_metrics:
|
|
self.metrics = self.metrics[-self.max_metrics:]
|
|
|
|
# 更新路徑統計
|
|
self._update_path_stats(metric)
|
|
|
|
# 更新內容類型統計
|
|
self._update_content_type_stats(metric)
|
|
|
|
def _update_path_stats(self, metric: CompressionMetrics):
|
|
"""更新路徑統計"""
|
|
path = metric.path
|
|
if path not in self.path_stats:
|
|
self.path_stats[path] = {
|
|
'requests': 0,
|
|
'compressed_requests': 0,
|
|
'total_original_bytes': 0,
|
|
'total_compressed_bytes': 0,
|
|
'total_response_time': 0.0,
|
|
'best_compression_ratio': 0.0
|
|
}
|
|
|
|
stats = self.path_stats[path]
|
|
stats['requests'] += 1
|
|
stats['total_original_bytes'] += metric.original_size
|
|
stats['total_compressed_bytes'] += metric.compressed_size
|
|
stats['total_response_time'] += metric.response_time
|
|
|
|
if metric.was_compressed:
|
|
stats['compressed_requests'] += 1
|
|
stats['best_compression_ratio'] = max(
|
|
stats['best_compression_ratio'],
|
|
metric.compression_ratio
|
|
)
|
|
|
|
def _update_content_type_stats(self, metric: CompressionMetrics):
|
|
"""更新內容類型統計"""
|
|
content_type = metric.content_type or 'unknown'
|
|
if content_type not in self.content_type_stats:
|
|
self.content_type_stats[content_type] = {
|
|
'requests': 0,
|
|
'compressed_requests': 0,
|
|
'total_original_bytes': 0,
|
|
'total_compressed_bytes': 0,
|
|
'average_compression_ratio': 0.0
|
|
}
|
|
|
|
stats = self.content_type_stats[content_type]
|
|
stats['requests'] += 1
|
|
stats['total_original_bytes'] += metric.original_size
|
|
stats['total_compressed_bytes'] += metric.compressed_size
|
|
|
|
if metric.was_compressed:
|
|
stats['compressed_requests'] += 1
|
|
|
|
# 重新計算平均壓縮比
|
|
if stats['total_original_bytes'] > 0:
|
|
stats['average_compression_ratio'] = (
|
|
1 - stats['total_compressed_bytes'] / stats['total_original_bytes']
|
|
) * 100
|
|
|
|
def get_summary(self, time_window: Optional[timedelta] = None) -> CompressionSummary:
|
|
"""獲取壓縮摘要統計"""
|
|
with self.lock:
|
|
metrics = self.metrics
|
|
|
|
# 如果指定時間窗口,過濾數據
|
|
if time_window:
|
|
cutoff_time = datetime.now() - time_window
|
|
metrics = [m for m in metrics if m.timestamp >= cutoff_time]
|
|
|
|
if not metrics:
|
|
return CompressionSummary()
|
|
|
|
total_requests = len(metrics)
|
|
compressed_requests = sum(1 for m in metrics if m.was_compressed)
|
|
total_original_bytes = sum(m.original_size for m in metrics)
|
|
total_compressed_bytes = sum(m.compressed_size for m in metrics)
|
|
total_response_time = sum(m.response_time for m in metrics)
|
|
|
|
# 計算統計數據
|
|
compression_percentage = (compressed_requests / total_requests * 100) if total_requests > 0 else 0
|
|
average_compression_ratio = 0.0
|
|
bandwidth_saved = 0
|
|
|
|
if total_original_bytes > 0:
|
|
average_compression_ratio = (1 - total_compressed_bytes / total_original_bytes) * 100
|
|
bandwidth_saved = total_original_bytes - total_compressed_bytes
|
|
|
|
average_response_time = total_response_time / total_requests if total_requests > 0 else 0
|
|
|
|
# 獲取壓縮效果最好的路徑
|
|
top_compressed_paths = self._get_top_compressed_paths()
|
|
|
|
return CompressionSummary(
|
|
total_requests=total_requests,
|
|
compressed_requests=compressed_requests,
|
|
total_original_bytes=total_original_bytes,
|
|
total_compressed_bytes=total_compressed_bytes,
|
|
average_compression_ratio=average_compression_ratio,
|
|
average_response_time=average_response_time,
|
|
compression_percentage=compression_percentage,
|
|
bandwidth_saved=bandwidth_saved,
|
|
top_compressed_paths=top_compressed_paths
|
|
)
|
|
|
|
def _get_top_compressed_paths(self, limit: int = 5) -> List[Tuple[str, float]]:
|
|
"""獲取壓縮效果最好的路徑"""
|
|
path_ratios = []
|
|
|
|
for path, stats in self.path_stats.items():
|
|
if stats['compressed_requests'] > 0 and stats['total_original_bytes'] > 0:
|
|
compression_ratio = (
|
|
1 - stats['total_compressed_bytes'] / stats['total_original_bytes']
|
|
) * 100
|
|
path_ratios.append((path, compression_ratio))
|
|
|
|
# 按壓縮比排序
|
|
path_ratios.sort(key=lambda x: x[1], reverse=True)
|
|
return path_ratios[:limit]
|
|
|
|
def get_path_stats(self) -> Dict[str, Dict]:
|
|
"""獲取路徑統計"""
|
|
with self.lock:
|
|
return self.path_stats.copy()
|
|
|
|
def get_content_type_stats(self) -> Dict[str, Dict]:
|
|
"""獲取內容類型統計"""
|
|
with self.lock:
|
|
return self.content_type_stats.copy()
|
|
|
|
def get_recent_metrics(self, limit: int = 100) -> List[CompressionMetrics]:
|
|
"""獲取最近的指標數據"""
|
|
with self.lock:
|
|
return self.metrics[-limit:] if self.metrics else []
|
|
|
|
def reset_stats(self):
|
|
"""重置統計數據"""
|
|
with self.lock:
|
|
self.metrics.clear()
|
|
self.path_stats.clear()
|
|
self.content_type_stats.clear()
|
|
self._start_time = datetime.now()
|
|
|
|
def export_stats(self) -> Dict:
|
|
"""導出統計數據為字典格式"""
|
|
summary = self.get_summary()
|
|
|
|
return {
|
|
'summary': {
|
|
'total_requests': summary.total_requests,
|
|
'compressed_requests': summary.compressed_requests,
|
|
'compression_percentage': round(summary.compression_percentage, 2),
|
|
'average_compression_ratio': round(summary.average_compression_ratio, 2),
|
|
'bandwidth_saved_mb': round(summary.bandwidth_saved / (1024 * 1024), 2),
|
|
'average_response_time_ms': round(summary.average_response_time * 1000, 2),
|
|
'monitoring_duration_hours': round(
|
|
(datetime.now() - self._start_time).total_seconds() / 3600, 2
|
|
)
|
|
},
|
|
'top_compressed_paths': [
|
|
{'path': path, 'compression_ratio': round(ratio, 2)}
|
|
for path, ratio in summary.top_compressed_paths
|
|
],
|
|
'path_stats': {
|
|
path: {
|
|
'requests': stats['requests'],
|
|
'compression_percentage': round(
|
|
stats['compressed_requests'] / stats['requests'] * 100, 2
|
|
) if stats['requests'] > 0 else 0,
|
|
'average_response_time_ms': round(
|
|
stats['total_response_time'] / stats['requests'] * 1000, 2
|
|
) if stats['requests'] > 0 else 0,
|
|
'bandwidth_saved_kb': round(
|
|
(stats['total_original_bytes'] - stats['total_compressed_bytes']) / 1024, 2
|
|
)
|
|
}
|
|
for path, stats in self.path_stats.items()
|
|
},
|
|
'content_type_stats': {
|
|
content_type: {
|
|
'requests': stats['requests'],
|
|
'compression_percentage': round(
|
|
stats['compressed_requests'] / stats['requests'] * 100, 2
|
|
) if stats['requests'] > 0 else 0,
|
|
'average_compression_ratio': round(stats['average_compression_ratio'], 2)
|
|
}
|
|
for content_type, stats in self.content_type_stats.items()
|
|
}
|
|
}
|
|
|
|
|
|
# 全域監控器實例
|
|
_compression_monitor: Optional[CompressionMonitor] = None
|
|
|
|
|
|
def get_compression_monitor() -> CompressionMonitor:
|
|
"""獲取全域壓縮監控器實例"""
|
|
global _compression_monitor
|
|
if _compression_monitor is None:
|
|
_compression_monitor = CompressionMonitor()
|
|
return _compression_monitor
|