proxy-go/internal/cache/extension_matcher.go

217 lines
5.5 KiB
Go

package cache
import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
"log"
"proxy-go/internal/config"
"proxy-go/internal/utils"
"sync"
"time"
)
// ExtensionMatcherCacheItem 扩展名匹配器缓存项
type ExtensionMatcherCacheItem struct {
Matcher *utils.ExtensionMatcher
Hash string // 配置的哈希值,用于检测配置变化
CreatedAt time.Time
LastUsed time.Time
UseCount int64
}
// ExtensionMatcherCache 扩展名匹配器缓存管理器
type ExtensionMatcherCache struct {
cache sync.Map
maxAge time.Duration
cleanupTick time.Duration
stopCleanup chan struct{}
mu sync.RWMutex
}
// NewExtensionMatcherCache 创建新的扩展名匹配器缓存
func NewExtensionMatcherCache() *ExtensionMatcherCache {
emc := &ExtensionMatcherCache{
maxAge: 10 * time.Minute, // 缓存10分钟
cleanupTick: 2 * time.Minute, // 每2分钟清理一次
stopCleanup: make(chan struct{}),
}
// 启动清理协程
go emc.startCleanup()
return emc
}
// generateConfigHash 生成配置的哈希值
func (emc *ExtensionMatcherCache) generateConfigHash(rules []config.ExtensionRule) string {
// 将规则序列化为JSON
data, err := json.Marshal(rules)
if err != nil {
// 如果序列化失败,使用时间戳作为哈希
return hex.EncodeToString([]byte(time.Now().String()))
}
// 计算SHA256哈希
hash := sha256.Sum256(data)
return hex.EncodeToString(hash[:])
}
// GetOrCreate 获取或创建扩展名匹配器
func (emc *ExtensionMatcherCache) GetOrCreate(pathKey string, rules []config.ExtensionRule) *utils.ExtensionMatcher {
// 如果没有规则,直接创建新的匹配器
if len(rules) == 0 {
return utils.NewExtensionMatcher(rules)
}
// 生成配置哈希
configHash := emc.generateConfigHash(rules)
// 尝试从缓存获取
if value, ok := emc.cache.Load(pathKey); ok {
item := value.(*ExtensionMatcherCacheItem)
// 检查配置是否变化
if item.Hash == configHash {
// 配置未变化,更新使用信息
emc.mu.Lock()
item.LastUsed = time.Now()
item.UseCount++
emc.mu.Unlock()
log.Printf("[ExtensionMatcherCache] HIT %s (使用次数: %d)", pathKey, item.UseCount)
return item.Matcher
} else {
// 配置已变化,删除旧缓存
emc.cache.Delete(pathKey)
log.Printf("[ExtensionMatcherCache] CONFIG_CHANGED %s", pathKey)
}
}
// 创建新的匹配器
matcher := utils.NewExtensionMatcher(rules)
// 创建缓存项
item := &ExtensionMatcherCacheItem{
Matcher: matcher,
Hash: configHash,
CreatedAt: time.Now(),
LastUsed: time.Now(),
UseCount: 1,
}
// 存储到缓存
emc.cache.Store(pathKey, item)
log.Printf("[ExtensionMatcherCache] NEW %s (规则数量: %d)", pathKey, len(rules))
return matcher
}
// InvalidatePath 使指定路径的缓存失效
func (emc *ExtensionMatcherCache) InvalidatePath(pathKey string) {
if _, ok := emc.cache.LoadAndDelete(pathKey); ok {
log.Printf("[ExtensionMatcherCache] INVALIDATED %s", pathKey)
}
}
// InvalidateAll 清空所有缓存
func (emc *ExtensionMatcherCache) InvalidateAll() {
count := 0
emc.cache.Range(func(key, value interface{}) bool {
emc.cache.Delete(key)
count++
return true
})
log.Printf("[ExtensionMatcherCache] INVALIDATED_ALL (清理了 %d 个缓存项)", count)
}
// GetStats 获取缓存统计信息
func (emc *ExtensionMatcherCache) GetStats() ExtensionMatcherCacheStats {
stats := ExtensionMatcherCacheStats{
MaxAge: int64(emc.maxAge.Minutes()),
CleanupTick: int64(emc.cleanupTick.Minutes()),
}
emc.cache.Range(func(key, value interface{}) bool {
item := value.(*ExtensionMatcherCacheItem)
stats.TotalItems++
stats.TotalUseCount += item.UseCount
// 计算平均年龄
age := time.Since(item.CreatedAt)
stats.AverageAge += int64(age.Minutes())
return true
})
if stats.TotalItems > 0 {
stats.AverageAge /= int64(stats.TotalItems)
}
return stats
}
// ExtensionMatcherCacheStats 扩展名匹配器缓存统计信息
type ExtensionMatcherCacheStats struct {
TotalItems int `json:"total_items"` // 缓存项数量
TotalUseCount int64 `json:"total_use_count"` // 总使用次数
AverageAge int64 `json:"average_age"` // 平均年龄(分钟)
MaxAge int64 `json:"max_age"` // 最大缓存时间(分钟)
CleanupTick int64 `json:"cleanup_tick"` // 清理间隔(分钟)
}
// startCleanup 启动清理协程
func (emc *ExtensionMatcherCache) startCleanup() {
ticker := time.NewTicker(emc.cleanupTick)
defer ticker.Stop()
for {
select {
case <-ticker.C:
emc.cleanup()
case <-emc.stopCleanup:
return
}
}
}
// cleanup 清理过期的缓存项
func (emc *ExtensionMatcherCache) cleanup() {
now := time.Now()
expiredKeys := make([]interface{}, 0)
// 收集过期的键
emc.cache.Range(func(key, value interface{}) bool {
item := value.(*ExtensionMatcherCacheItem)
if now.Sub(item.LastUsed) > emc.maxAge {
expiredKeys = append(expiredKeys, key)
}
return true
})
// 删除过期的缓存项
for _, key := range expiredKeys {
emc.cache.Delete(key)
}
if len(expiredKeys) > 0 {
log.Printf("[ExtensionMatcherCache] CLEANUP 清理了 %d 个过期缓存项", len(expiredKeys))
}
}
// Stop 停止缓存管理器
func (emc *ExtensionMatcherCache) Stop() {
close(emc.stopCleanup)
}
// UpdateConfig 更新缓存配置
func (emc *ExtensionMatcherCache) UpdateConfig(maxAge, cleanupTick time.Duration) {
emc.mu.Lock()
defer emc.mu.Unlock()
emc.maxAge = maxAge
emc.cleanupTick = cleanupTick
log.Printf("[ExtensionMatcherCache] CONFIG_UPDATED maxAge=%v cleanupTick=%v", maxAge, cleanupTick)
}