// cache/cache.go package cache import ( "crypto/sha256" "encoding/hex" "strconv" "sync" "time" ) type CacheEntry struct { Data []byte Headers map[string]string CreatedAt time.Time ExpiresAt time.Time Size int64 } type MemoryCache struct { entries sync.Map maxSize int64 currentSize int64 ttl time.Duration mu sync.Mutex } func NewMemoryCache(maxSize int64, ttl time.Duration) *MemoryCache { mc := &MemoryCache{ maxSize: maxSize, ttl: ttl, } // 启动清理协程 if ttl > 0 { go mc.cleanup() } return mc } // Age 返回缓存条目的年龄(秒) func (e *CacheEntry) Age() string { age := time.Since(e.CreatedAt) return strconv.FormatInt(int64(age.Seconds()), 10) } // Get 获取缓存条目 func (mc *MemoryCache) Get(key string) *CacheEntry { val, ok := mc.entries.Load(key) if !ok { return nil } entry := val.(*CacheEntry) // 检查是否过期 if time.Now().After(entry.ExpiresAt) { mc.Delete(key) return nil } return entry } // Set 设置缓存条目 func (mc *MemoryCache) Set(key string, data []byte, headers map[string]string) bool { // 如果禁用缓存(maxSize = 0),直接返回 if mc.maxSize == 0 { return false } size := int64(len(data)) // 如果单个条目超过最大缓存大小,不缓存 if size > mc.maxSize { return false } mc.mu.Lock() defer mc.mu.Unlock() // 检查是否超过最大缓存大小 if mc.currentSize+size > mc.maxSize { // 尝试清理过期条目 mc.evictExpired() // 如果还是不够,使用 LRU 清理 if mc.currentSize+size > mc.maxSize { mc.evictOldest(size) } } now := time.Now() entry := &CacheEntry{ Data: data, Headers: headers, CreatedAt: now, ExpiresAt: now.Add(mc.ttl), Size: size, } // 如果 key 已存在,先删除旧的 if oldVal, exists := mc.entries.Load(key); exists { oldEntry := oldVal.(*CacheEntry) mc.currentSize -= oldEntry.Size } mc.entries.Store(key, entry) mc.currentSize += size return true } // Delete 删除缓存条目 func (mc *MemoryCache) Delete(key string) { val, ok := mc.entries.LoadAndDelete(key) if ok { entry := val.(*CacheEntry) mc.mu.Lock() mc.currentSize -= entry.Size mc.mu.Unlock() } } // Clear 清空所有缓存 func (mc *MemoryCache) Clear() { mc.mu.Lock() defer mc.mu.Unlock() mc.entries.Range(func(key, value interface{}) bool { mc.entries.Delete(key) return true }) mc.currentSize = 0 } // GenerateKey 生成缓存键 func (mc *MemoryCache) GenerateKey(url string) string { hash := sha256.Sum256([]byte(url)) return hex.EncodeToString(hash[:]) } // evictExpired 清理过期条目 func (mc *MemoryCache) evictExpired() { now := time.Now() mc.entries.Range(func(key, value interface{}) bool { entry := value.(*CacheEntry) if now.After(entry.ExpiresAt) { mc.entries.Delete(key) mc.currentSize -= entry.Size } return true }) } // evictOldest 清理最旧的条目 func (mc *MemoryCache) evictOldest(needed int64) { type entryWithKey struct { key string entry *CacheEntry } var entries []entryWithKey mc.entries.Range(func(key, value interface{}) bool { entries = append(entries, entryWithKey{ key: key.(string), entry: value.(*CacheEntry), }) return true }) // 按创建时间排序(最旧的在前) for i := 0; i < len(entries)-1; i++ { for j := i + 1; j < len(entries); j++ { if entries[i].entry.CreatedAt.After(entries[j].entry.CreatedAt) { entries[i], entries[j] = entries[j], entries[i] } } } // 删除最旧的条目直到有足够空间 freed := int64(0) for _, e := range entries { if freed >= needed { break } mc.entries.Delete(e.key) mc.currentSize -= e.entry.Size freed += e.entry.Size } } // cleanup 定期清理过期条目 func (mc *MemoryCache) cleanup() { ticker := time.NewTicker(10 * time.Minute) defer ticker.Stop() for range ticker.C { mc.mu.Lock() mc.evictExpired() mc.mu.Unlock() } } // Stats 返回缓存统计信息 func (mc *MemoryCache) Stats() (entries int, size int64) { count := 0 mc.entries.Range(func(key, value interface{}) bool { count++ return true }) return count, mc.currentSize } // GetStats 返回详细的缓存统计信息 func (mc *MemoryCache) GetStats() map[string]interface{} { entries, size := mc.Stats() utilizationPercent := float64(0) if mc.maxSize > 0 { utilizationPercent = float64(size) / float64(mc.maxSize) * 100 } return map[string]interface{}{ "entries": entries, "size_bytes": size, "size_mb": float64(size) / 1024 / 1024, "max_size_bytes": mc.maxSize, "max_size_mb": float64(mc.maxSize) / 1024 / 1024, "utilization_pct": utilizationPercent, "ttl_seconds": int64(mc.ttl.Seconds()), "enabled": mc.maxSize > 0, } }