Files
go-pixelnebula/cache/monitor.go
2025-03-19 21:10:19 +08:00

215 lines
5.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package cache
import (
"sync"
"time"
)
// MonitorOptions 缓存监控选项
type MonitorOptions struct {
Enabled bool // 是否启用监控
SampleInterval time.Duration // 采样间隔时间
AdjustInterval time.Duration // 调整间隔时间
MinSize int // 最小缓存大小
MaxSize int // 最大缓存大小
TargetHitRate float64 // 目标命中率
SizeGrowthFactor float64 // 缓存大小增长因子
SizeShrinkFactor float64 // 缓存大小收缩因子
ExpirationFactor float64 // 过期时间调整因子
}
// DefaultMonitorOptions 默认监控选项
var DefaultMonitorOptions = MonitorOptions{
Enabled: true,
SampleInterval: time.Minute, // 每分钟采样一次
AdjustInterval: time.Minute * 10, // 每10分钟调整一次
MinSize: 50, // 最小缓存大小
MaxSize: 1000, // 最大缓存大小
TargetHitRate: 0.8, // 目标命中率80%
SizeGrowthFactor: 1.2, // 增长20%
SizeShrinkFactor: 0.8, // 收缩20%
ExpirationFactor: 1.5, // 过期时间调整因子
}
// CacheStats 缓存统计信息
type CacheStats struct {
Size int // 当前缓存大小
Hits int // 命中次数
Misses int // 未命中次数
HitRate float64 // 命中率
MemoryUsage int64 // 内存使用量(字节)
LastAdjusted time.Time // 最后调整时间
SamplesCount int // 样本数量
AvgAccessTime float64 // 平均访问时间(纳秒)
}
// Monitor 缓存监控器
type Monitor struct {
options MonitorOptions
cache *PNCache
stats CacheStats
sampleHistory []CacheStats
mutex sync.RWMutex
stopChan chan struct{}
isRunning bool
}
// NewMonitor 创建一个新的缓存监控器
func NewMonitor(cache *PNCache, options MonitorOptions) *Monitor {
return &Monitor{
options: options,
cache: cache,
sampleHistory: make([]CacheStats, 0, 100), // 预分配100个样本的容量
stopChan: make(chan struct{}),
isRunning: false,
}
}
// Start 启动监控器
func (m *Monitor) Start() {
if !m.options.Enabled || m.isRunning {
return
}
m.mutex.Lock()
m.isRunning = true
m.mutex.Unlock()
go m.monitorRoutine()
}
// Stop 停止监控器
func (m *Monitor) Stop() {
if !m.isRunning {
return
}
m.mutex.Lock()
m.isRunning = false
m.mutex.Unlock()
m.stopChan <- struct{}{}
}
// GetStats 获取当前缓存统计信息
func (m *Monitor) GetStats() CacheStats {
m.mutex.RLock()
defer m.mutex.RUnlock()
return m.stats
}
// monitorRoutine 监控例程
func (m *Monitor) monitorRoutine() {
sampleTicker := time.NewTicker(m.options.SampleInterval)
adjustTicker := time.NewTicker(m.options.AdjustInterval)
defer sampleTicker.Stop()
defer adjustTicker.Stop()
for {
select {
case <-m.stopChan:
return
case <-sampleTicker.C:
m.collectSample()
case <-adjustTicker.C:
m.adjustCache()
}
}
}
// collectSample 收集缓存样本
func (m *Monitor) collectSample() {
m.mutex.Lock()
defer m.mutex.Unlock()
// 获取缓存统计信息
hits, misses, hitRate := m.cache.Stats()
size := m.cache.Size()
// 估算内存使用量(简化计算,实际应用中可能需要更精确的方法)
memoryUsage := int64(size * 1024) // 假设每个缓存项平均占用1KB
// 创建新的统计样本
newStat := CacheStats{
Size: size,
Hits: hits,
Misses: misses,
HitRate: hitRate,
MemoryUsage: memoryUsage,
LastAdjusted: time.Now(),
}
// 添加到历史样本
m.sampleHistory = append(m.sampleHistory, newStat)
// 限制历史样本数量保留最近的100个样本
if len(m.sampleHistory) > 100 {
m.sampleHistory = m.sampleHistory[len(m.sampleHistory)-100:]
}
// 更新当前统计信息
m.stats = newStat
m.stats.SamplesCount = len(m.sampleHistory)
}
// adjustCache 根据统计信息调整缓存
func (m *Monitor) adjustCache() {
m.mutex.Lock()
defer m.mutex.Unlock()
// 如果样本数量不足,不进行调整
if len(m.sampleHistory) < 5 {
return
}
// 计算平均命中率
totalHitRate := 0.0
for _, stat := range m.sampleHistory {
totalHitRate += stat.HitRate
}
avgHitRate := totalHitRate / float64(len(m.sampleHistory))
// 获取当前缓存选项
cacheOptions := m.cache.GetOptions()
// 根据命中率调整缓存大小
newSize := cacheOptions.Size
if avgHitRate < m.options.TargetHitRate {
// 命中率低于目标,增加缓存大小
newSize = int(float64(newSize) * m.options.SizeGrowthFactor)
// 确保不超过最大大小
if newSize > m.options.MaxSize {
newSize = m.options.MaxSize
}
} else if avgHitRate > m.options.TargetHitRate+0.1 && m.stats.Size > m.options.MinSize {
// 命中率远高于目标且缓存大小大于最小值,可以适当减小缓存
newSize = int(float64(newSize) * m.options.SizeShrinkFactor)
// 确保不小于最小大小
if newSize < m.options.MinSize {
newSize = m.options.MinSize
}
}
// 根据访问模式调整过期时间
newExpiration := cacheOptions.Expiration
if avgHitRate < m.options.TargetHitRate {
// 命中率低,增加过期时间
newExpiration = time.Duration(float64(newExpiration) * m.options.ExpirationFactor)
} else if avgHitRate > m.options.TargetHitRate+0.1 {
// 命中率高,可以适当减少过期时间
newExpiration = time.Duration(float64(newExpiration) / m.options.ExpirationFactor)
}
// 应用新的缓存选项
if newSize != cacheOptions.Size || newExpiration != cacheOptions.Expiration {
cacheOptions.Size = newSize
cacheOptions.Expiration = newExpiration
m.cache.UpdateOptions(cacheOptions)
// 更新最后调整时间
m.stats.LastAdjusted = time.Now()
}
}