mirror of
https://github.com/woodchen-ink/proxy-go.git
synced 2025-07-18 08:31:55 +08:00
feat(metrics): 优化指标数据清理和持久化策略
- 调整指标清理机制,智能保留高频路径和引用来源统计数据 - 修改清理任务频率为15分钟,并立即执行首次清理 - 优化指标存储服务保存间隔为30分钟,减少IO操作 - 在清理和保存过程中添加内存使用情况日志 - 强制执行垃圾回收,减少内存占用 - 移除部分冗余的性能指标统计项目
This commit is contained in:
parent
22c0d2e301
commit
10aef5e73e
@ -468,8 +468,10 @@ func (c *Collector) validateLoadedData() error {
|
||||
return true
|
||||
})
|
||||
|
||||
if totalPathRequests != statusCodeTotal {
|
||||
return fmt.Errorf("path stats total (%d) does not match status code total (%d)",
|
||||
// 由于我们限制了路径统计的收集数量,路径统计总数可能小于状态码统计总数
|
||||
// 因此,我们只需要确保路径统计总数不超过状态码统计总数即可
|
||||
if float64(totalPathRequests) > float64(statusCodeTotal)*1.1 { // 允许10%的误差
|
||||
return fmt.Errorf("path stats total (%d) significantly exceeds status code total (%d)",
|
||||
totalPathRequests, statusCodeTotal)
|
||||
}
|
||||
|
||||
@ -583,7 +585,10 @@ func simplifyReferer(referer string) string {
|
||||
// startCleanupTask 启动定期清理任务
|
||||
func (c *Collector) startCleanupTask() {
|
||||
go func() {
|
||||
ticker := time.NewTicker(1 * time.Hour) // 每小时清理一次
|
||||
// 先立即执行一次清理
|
||||
c.cleanupOldData()
|
||||
|
||||
ticker := time.NewTicker(15 * time.Minute) // 每15分钟清理一次
|
||||
defer ticker.Stop()
|
||||
|
||||
for range ticker.C {
|
||||
@ -596,32 +601,102 @@ func (c *Collector) startCleanupTask() {
|
||||
func (c *Collector) cleanupOldData() {
|
||||
log.Printf("[Metrics] 开始清理旧数据...")
|
||||
|
||||
// 清理路径统计 - 只保留有请求的路径
|
||||
// 清理路径统计 - 只保留有请求且请求数较多的路径
|
||||
var pathsToRemove []string
|
||||
var pathsCount int
|
||||
var totalRequests int64
|
||||
|
||||
// 先收集所有路径及其请求数
|
||||
type pathInfo struct {
|
||||
path string
|
||||
count int64
|
||||
}
|
||||
var paths []pathInfo
|
||||
|
||||
c.pathStats.Range(func(key, value interface{}) bool {
|
||||
path := key.(string)
|
||||
stats := value.(*models.PathMetrics)
|
||||
if stats.GetRequestCount() == 0 {
|
||||
pathsToRemove = append(pathsToRemove, path)
|
||||
}
|
||||
count := stats.GetRequestCount()
|
||||
pathsCount++
|
||||
totalRequests += count
|
||||
paths = append(paths, pathInfo{path, count})
|
||||
return true
|
||||
})
|
||||
|
||||
// 按请求数排序
|
||||
sort.Slice(paths, func(i, j int) bool {
|
||||
return paths[i].count > paths[j].count
|
||||
})
|
||||
|
||||
// 只保留前100个请求数最多的路径,或者请求数占总请求数1%以上的路径
|
||||
threshold := totalRequests / 100 // 1%的阈值
|
||||
if threshold < 10 {
|
||||
threshold = 10 // 至少保留请求数>=10的路径
|
||||
}
|
||||
|
||||
// 标记要删除的路径
|
||||
for _, pi := range paths {
|
||||
if len(paths)-len(pathsToRemove) <= 100 {
|
||||
// 已经只剩下100个路径了,不再删除
|
||||
break
|
||||
}
|
||||
|
||||
if pi.count < threshold {
|
||||
pathsToRemove = append(pathsToRemove, pi.path)
|
||||
}
|
||||
}
|
||||
|
||||
// 删除标记的路径
|
||||
for _, path := range pathsToRemove {
|
||||
c.pathStats.Delete(path)
|
||||
}
|
||||
|
||||
// 清理引用来源统计 - 只保留有请求的引用来源
|
||||
// 清理引用来源统计 - 类似地处理
|
||||
var referersToRemove []string
|
||||
var referersCount int
|
||||
var totalRefererRequests int64
|
||||
|
||||
// 先收集所有引用来源及其请求数
|
||||
type refererInfo struct {
|
||||
referer string
|
||||
count int64
|
||||
}
|
||||
var referers []refererInfo
|
||||
|
||||
c.refererStats.Range(func(key, value interface{}) bool {
|
||||
referer := key.(string)
|
||||
stats := value.(*models.PathMetrics)
|
||||
if stats.GetRequestCount() == 0 {
|
||||
referersToRemove = append(referersToRemove, referer)
|
||||
}
|
||||
count := stats.GetRequestCount()
|
||||
referersCount++
|
||||
totalRefererRequests += count
|
||||
referers = append(referers, refererInfo{referer, count})
|
||||
return true
|
||||
})
|
||||
|
||||
// 按请求数排序
|
||||
sort.Slice(referers, func(i, j int) bool {
|
||||
return referers[i].count > referers[j].count
|
||||
})
|
||||
|
||||
// 只保留前50个请求数最多的引用来源,或者请求数占总请求数2%以上的引用来源
|
||||
refThreshold := totalRefererRequests / 50 // 2%的阈值
|
||||
if refThreshold < 5 {
|
||||
refThreshold = 5 // 至少保留请求数>=5的引用来源
|
||||
}
|
||||
|
||||
// 标记要删除的引用来源
|
||||
for _, ri := range referers {
|
||||
if len(referers)-len(referersToRemove) <= 50 {
|
||||
// 已经只剩下50个引用来源了,不再删除
|
||||
break
|
||||
}
|
||||
|
||||
if ri.count < refThreshold {
|
||||
referersToRemove = append(referersToRemove, ri.referer)
|
||||
}
|
||||
}
|
||||
|
||||
// 删除标记的引用来源
|
||||
for _, referer := range referersToRemove {
|
||||
c.refererStats.Delete(referer)
|
||||
}
|
||||
@ -654,5 +729,15 @@ func (c *Collector) cleanupOldData() {
|
||||
}
|
||||
c.bandwidthStats.Unlock()
|
||||
|
||||
log.Printf("[Metrics] 清理完成: 删除了 %d 个路径, %d 个引用来源", len(pathsToRemove), len(referersToRemove))
|
||||
// 强制进行一次GC
|
||||
runtime.GC()
|
||||
|
||||
// 打印内存使用情况
|
||||
var mem runtime.MemStats
|
||||
runtime.ReadMemStats(&mem)
|
||||
|
||||
log.Printf("[Metrics] 清理完成: 删除了 %d/%d 个路径, %d/%d 个引用来源, 当前内存使用: %s",
|
||||
len(pathsToRemove), pathsCount,
|
||||
len(referersToRemove), referersCount,
|
||||
utils.FormatBytes(int64(mem.Alloc)))
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ func InitMetricsStorage(cfg *config.Config) error {
|
||||
|
||||
// 创建指标存储服务
|
||||
dataDir := filepath.Join("data", "metrics")
|
||||
saveInterval := 5 * time.Minute // 默认5分钟保存一次
|
||||
saveInterval := 30 * time.Minute // 默认30分钟保存一次,减少IO操作
|
||||
|
||||
metricsStorage = NewMetricsStorage(GetCollector(), dataDir, saveInterval)
|
||||
|
||||
@ -30,7 +30,7 @@ func InitMetricsStorage(cfg *config.Config) error {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("[Metrics] 指标存储服务已初始化")
|
||||
log.Printf("[Metrics] 指标存储服务已初始化,保存间隔: %v", saveInterval)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,8 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"proxy-go/internal/models"
|
||||
"proxy-go/internal/utils"
|
||||
"runtime"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
@ -106,12 +108,10 @@ func (ms *MetricsStorage) SaveMetrics() error {
|
||||
|
||||
// 保存基本指标 - 只保存必要的字段
|
||||
basicMetrics := map[string]interface{}{
|
||||
"uptime": stats["uptime"],
|
||||
"total_bytes": stats["total_bytes"],
|
||||
"avg_response_time": stats["avg_response_time"],
|
||||
"requests_per_second": stats["requests_per_second"],
|
||||
"bytes_per_second": stats["bytes_per_second"],
|
||||
"save_time": time.Now().Format(time.RFC3339),
|
||||
"uptime": stats["uptime"],
|
||||
"total_bytes": stats["total_bytes"],
|
||||
"avg_response_time": stats["avg_response_time"],
|
||||
"save_time": time.Now().Format(time.RFC3339),
|
||||
}
|
||||
|
||||
// 单独保存延迟统计,避免嵌套结构导致的内存占用
|
||||
@ -154,7 +154,15 @@ func (ms *MetricsStorage) SaveMetrics() error {
|
||||
ms.lastSaveTime = time.Now()
|
||||
ms.mutex.Unlock()
|
||||
|
||||
log.Printf("[MetricsStorage] 指标数据保存完成,耗时: %v", time.Since(start))
|
||||
// 强制进行一次GC
|
||||
runtime.GC()
|
||||
|
||||
// 打印内存使用情况
|
||||
var mem runtime.MemStats
|
||||
runtime.ReadMemStats(&mem)
|
||||
|
||||
log.Printf("[MetricsStorage] 指标数据保存完成,耗时: %v, 内存使用: %s",
|
||||
time.Since(start), utils.FormatBytes(int64(mem.Alloc)))
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -314,7 +322,15 @@ func (ms *MetricsStorage) LoadMetrics() error {
|
||||
}
|
||||
ms.mutex.Unlock()
|
||||
|
||||
log.Printf("[MetricsStorage] 指标数据加载完成,耗时: %v", time.Since(start))
|
||||
// 强制进行一次GC
|
||||
runtime.GC()
|
||||
|
||||
// 打印内存使用情况
|
||||
var mem runtime.MemStats
|
||||
runtime.ReadMemStats(&mem)
|
||||
|
||||
log.Printf("[MetricsStorage] 指标数据加载完成,耗时: %v, 内存使用: %s",
|
||||
time.Since(start), utils.FormatBytes(int64(mem.Alloc)))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user