添加 cache/cache.go
This commit is contained in:
170
cache/cache.go
vendored
Normal file
170
cache/cache.go
vendored
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
// cache/cache.go
|
||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/hex"
|
||||||
|
"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,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启动清理协程
|
||||||
|
go mc.cleanup()
|
||||||
|
|
||||||
|
return mc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mc *MemoryCache) Get(key string) (*CacheEntry, bool) {
|
||||||
|
val, ok := mc.entries.Load(key)
|
||||||
|
if !ok {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
entry := val.(*CacheEntry)
|
||||||
|
|
||||||
|
// 检查是否过期
|
||||||
|
if time.Now().After(entry.ExpiresAt) {
|
||||||
|
mc.Delete(key)
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mc *MemoryCache) Set(key string, data []byte, headers map[string]string) bool {
|
||||||
|
size := int64(len(data))
|
||||||
|
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
mc.entries.Store(key, entry)
|
||||||
|
mc.currentSize += size
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mc *MemoryCache) GenerateKey(url string) string {
|
||||||
|
hash := sha256.Sum256([]byte(url))
|
||||||
|
return hex.EncodeToString(hash[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user