mirror of
https://github.com/woodchen-ink/proxy-go.git
synced 2025-07-18 08:31:55 +08:00
- Restructure Collector to use more efficient atomic operations - Remove complex caching and monitoring dependencies - Simplify path and status code tracking with sync.Map - Optimize request logging with a dedicated request queue - Remove unnecessary utility functions and pools - Streamline stats generation and reduce complexity
243 lines
6.3 KiB
Go
243 lines
6.3 KiB
Go
package metrics
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
"proxy-go/internal/config"
|
|
"proxy-go/internal/models"
|
|
"proxy-go/internal/utils"
|
|
"runtime"
|
|
"sort"
|
|
"sync"
|
|
"sync/atomic"
|
|
"time"
|
|
)
|
|
|
|
// Collector 指标收集器
|
|
type Collector struct {
|
|
startTime time.Time
|
|
activeRequests int64
|
|
totalRequests int64
|
|
totalErrors int64
|
|
totalBytes int64
|
|
latencySum int64
|
|
pathStats sync.Map
|
|
statusCodeStats sync.Map
|
|
recentRequests *models.RequestQueue
|
|
config *config.Config
|
|
}
|
|
|
|
var (
|
|
instance *Collector
|
|
once sync.Once
|
|
)
|
|
|
|
// InitCollector 初始化收集器
|
|
func InitCollector(cfg *config.Config) error {
|
|
once.Do(func() {
|
|
instance = &Collector{
|
|
startTime: time.Now(),
|
|
recentRequests: models.NewRequestQueue(1000),
|
|
config: cfg,
|
|
}
|
|
})
|
|
return nil
|
|
}
|
|
|
|
// GetCollector 获取收集器实例
|
|
func GetCollector() *Collector {
|
|
return instance
|
|
}
|
|
|
|
// BeginRequest 开始请求
|
|
func (c *Collector) BeginRequest() {
|
|
atomic.AddInt64(&c.activeRequests, 1)
|
|
}
|
|
|
|
// EndRequest 结束请求
|
|
func (c *Collector) EndRequest() {
|
|
atomic.AddInt64(&c.activeRequests, -1)
|
|
}
|
|
|
|
// RecordRequest 记录请求
|
|
func (c *Collector) RecordRequest(path string, status int, latency time.Duration, bytes int64, clientIP string, r *http.Request) {
|
|
atomic.AddInt64(&c.totalRequests, 1)
|
|
atomic.AddInt64(&c.totalBytes, bytes)
|
|
atomic.AddInt64(&c.latencySum, int64(latency))
|
|
|
|
if status >= 400 {
|
|
atomic.AddInt64(&c.totalErrors, 1)
|
|
}
|
|
|
|
// 更新状态码统计
|
|
statusKey := fmt.Sprintf("%d", status)
|
|
if value, ok := c.statusCodeStats.Load(statusKey); ok {
|
|
atomic.AddInt64(value.(*int64), 1)
|
|
} else {
|
|
var count int64 = 1
|
|
c.statusCodeStats.Store(statusKey, &count)
|
|
}
|
|
|
|
// 更新路径统计
|
|
if pathStats, ok := c.pathStats.Load(path); ok {
|
|
stats := pathStats.(models.PathMetrics)
|
|
atomic.AddInt64(&stats.RequestCount, 1)
|
|
if status >= 400 {
|
|
atomic.AddInt64(&stats.ErrorCount, 1)
|
|
}
|
|
atomic.AddInt64(&stats.TotalLatency, int64(latency))
|
|
atomic.AddInt64(&stats.BytesTransferred, bytes)
|
|
} else {
|
|
stats := models.PathMetrics{
|
|
Path: path,
|
|
RequestCount: 1,
|
|
ErrorCount: int64(map[bool]int{true: 1, false: 0}[status >= 400]),
|
|
TotalLatency: int64(latency),
|
|
BytesTransferred: bytes,
|
|
}
|
|
c.pathStats.Store(path, stats)
|
|
}
|
|
|
|
// 记录最近请求
|
|
c.recentRequests.Push(models.RequestLog{
|
|
Time: time.Now(),
|
|
Path: path,
|
|
Status: status,
|
|
Latency: int64(latency),
|
|
BytesSent: bytes,
|
|
ClientIP: clientIP,
|
|
})
|
|
}
|
|
|
|
// GetStats 获取统计数据
|
|
func (c *Collector) GetStats() map[string]interface{} {
|
|
var mem runtime.MemStats
|
|
runtime.ReadMemStats(&mem)
|
|
|
|
// 计算平均延迟
|
|
avgLatency := float64(0)
|
|
if c.totalRequests > 0 {
|
|
avgLatency = float64(c.latencySum) / float64(c.totalRequests)
|
|
}
|
|
|
|
// 收集状态码统计
|
|
statusCodeStats := make(map[string]int64)
|
|
c.statusCodeStats.Range(func(key, value interface{}) bool {
|
|
statusCodeStats[key.(string)] = atomic.LoadInt64(value.(*int64))
|
|
return true
|
|
})
|
|
|
|
// 收集路径统计
|
|
var pathMetrics []models.PathMetrics
|
|
c.pathStats.Range(func(key, value interface{}) bool {
|
|
stats := value.(models.PathMetrics)
|
|
pathMetrics = append(pathMetrics, stats)
|
|
return true
|
|
})
|
|
|
|
// 按请求数排序
|
|
sort.Slice(pathMetrics, func(i, j int) bool {
|
|
return pathMetrics[i].RequestCount > pathMetrics[j].RequestCount
|
|
})
|
|
|
|
// 只保留前10个
|
|
if len(pathMetrics) > 10 {
|
|
pathMetrics = pathMetrics[:10]
|
|
}
|
|
|
|
// 计算每个路径的平均延迟
|
|
for i := range pathMetrics {
|
|
if pathMetrics[i].RequestCount > 0 {
|
|
avgLatencyMs := float64(pathMetrics[i].TotalLatency) / float64(pathMetrics[i].RequestCount) / float64(time.Millisecond)
|
|
pathMetrics[i].AvgLatency = fmt.Sprintf("%.2fms", avgLatencyMs)
|
|
}
|
|
}
|
|
|
|
return map[string]interface{}{
|
|
"uptime": time.Since(c.startTime).String(),
|
|
"active_requests": atomic.LoadInt64(&c.activeRequests),
|
|
"total_requests": atomic.LoadInt64(&c.totalRequests),
|
|
"total_errors": atomic.LoadInt64(&c.totalErrors),
|
|
"total_bytes": atomic.LoadInt64(&c.totalBytes),
|
|
"num_goroutine": runtime.NumGoroutine(),
|
|
"memory_usage": utils.FormatBytes(int64(mem.Alloc)),
|
|
"avg_response_time": fmt.Sprintf("%.2fms", avgLatency/float64(time.Millisecond)),
|
|
"status_code_stats": statusCodeStats,
|
|
"top_paths": pathMetrics,
|
|
"recent_requests": c.recentRequests.GetAll(),
|
|
}
|
|
}
|
|
|
|
func (c *Collector) SaveMetrics(stats map[string]interface{}) error {
|
|
lastSaveTime = time.Now()
|
|
return nil
|
|
}
|
|
|
|
// LoadRecentStats 简化为只进行数据验证
|
|
func (c *Collector) LoadRecentStats() error {
|
|
start := time.Now()
|
|
log.Printf("Starting to validate stats...")
|
|
|
|
if err := c.validateLoadedData(); err != nil {
|
|
return fmt.Errorf("data validation failed: %v", err)
|
|
}
|
|
|
|
log.Printf("Successfully validated stats in %v", time.Since(start))
|
|
return nil
|
|
}
|
|
|
|
// validateLoadedData 验证当前数据的有效性
|
|
func (c *Collector) validateLoadedData() error {
|
|
// 验证基础指标
|
|
if atomic.LoadInt64(&c.totalRequests) < 0 ||
|
|
atomic.LoadInt64(&c.totalErrors) < 0 ||
|
|
atomic.LoadInt64(&c.totalBytes) < 0 {
|
|
return fmt.Errorf("invalid stats values")
|
|
}
|
|
|
|
// 验证错误数不能大于总请求数
|
|
if atomic.LoadInt64(&c.totalErrors) > atomic.LoadInt64(&c.totalRequests) {
|
|
return fmt.Errorf("total errors exceeds total requests")
|
|
}
|
|
|
|
// 验证状态码统计
|
|
c.statusCodeStats.Range(func(key, value interface{}) bool {
|
|
return atomic.LoadInt64(value.(*int64)) >= 0
|
|
})
|
|
|
|
// 验证路径统计
|
|
var totalPathRequests int64
|
|
c.pathStats.Range(func(_, value interface{}) bool {
|
|
stats := value.(models.PathMetrics)
|
|
if stats.RequestCount < 0 || stats.ErrorCount < 0 {
|
|
return false
|
|
}
|
|
totalPathRequests += stats.RequestCount
|
|
return true
|
|
})
|
|
|
|
// 验证总数一致性
|
|
if totalPathRequests > atomic.LoadInt64(&c.totalRequests) {
|
|
return fmt.Errorf("path stats total exceeds total requests")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetLastSaveTime 实现 interfaces.MetricsCollector 接口
|
|
var lastSaveTime time.Time
|
|
|
|
func (c *Collector) GetLastSaveTime() time.Time {
|
|
return lastSaveTime
|
|
}
|
|
|
|
// CheckDataConsistency 实现 interfaces.MetricsCollector 接口
|
|
func (c *Collector) CheckDataConsistency() error {
|
|
// 简单的数据验证
|
|
if atomic.LoadInt64(&c.totalErrors) > atomic.LoadInt64(&c.totalRequests) {
|
|
return fmt.Errorf("total errors exceeds total requests")
|
|
}
|
|
return nil
|
|
}
|