|
@@ -0,0 +1,212 @@
|
|
|
+"""
|
|
|
+全局缓存管理器
|
|
|
+"""
|
|
|
+
|
|
|
+import time
|
|
|
+import threading
|
|
|
+from typing import Any, Optional, Dict
|
|
|
+from collections import OrderedDict
|
|
|
+from .strategies import CacheStrategy, CacheEntry
|
|
|
+from .config import CacheType, CacheConfig
|
|
|
+
|
|
|
+class GlobalCacheManager:
|
|
|
+ """全局缓存管理器"""
|
|
|
+
|
|
|
+ def __init__(self):
|
|
|
+ self._logger = None
|
|
|
+ self._caches: Dict[str, Dict[str, CacheEntry]] = {}
|
|
|
+ self._configs: Dict[str, CacheConfig] = {}
|
|
|
+ self._locks = Dict[str, threading.Lock] = {}
|
|
|
+ self._global_lock = threading.RLock()
|
|
|
+ self._last_cleanup = time.time()
|
|
|
+ self._stats = {"hits": 0, "misses": 0, "evictions": 0, "cleanups": 0}
|
|
|
+
|
|
|
+ @property
|
|
|
+ def logger(self):
|
|
|
+ """延迟初始化 logger 以避免循环导入"""
|
|
|
+ if self._logger is None:
|
|
|
+ from config.logger import setup_logger
|
|
|
+ self._logger = setup_logger()
|
|
|
+ return self._logger
|
|
|
+
|
|
|
+ def _get_cache_name(self, cache_type: CacheType, namespace: str) -> str:
|
|
|
+ """生成缓存名称"""
|
|
|
+ if namespace:
|
|
|
+ return f"{cache_type.value}:{namespace}"
|
|
|
+ return cache_type.value
|
|
|
+
|
|
|
+ def _get_or_create_cache(self, cache_name: str, config: CacheConfig) -> Dict[str, CacheEntry]:
|
|
|
+ """获取或创建缓存"""
|
|
|
+ with self._global_lock:
|
|
|
+ if cache_name not in self._caches:
|
|
|
+ self._caches[cache_name] = {
|
|
|
+ OrderedDict()
|
|
|
+ if config.strategy in [CacheStrategy.LRU, CacheStrategy.TTL_LRU]
|
|
|
+ else {}
|
|
|
+ }
|
|
|
+ self._configs[cache_name] = config
|
|
|
+ self._locks[cache_name] = threading.RLock()
|
|
|
+ return self._caches[cache_name]
|
|
|
+
|
|
|
+ def set(
|
|
|
+ self,
|
|
|
+ cache_type: CacheType,
|
|
|
+ key: str,
|
|
|
+ value: Any,
|
|
|
+ ttl: Optional[float] = None,
|
|
|
+ namespace: str = "",
|
|
|
+ ) -> None:
|
|
|
+ """设置缓存值"""
|
|
|
+ cache_name = self._get_cache_name(cache_type, namespace)
|
|
|
+ config = self._configs.get(cache_name) or CacheConfig.for_type(cache_type)
|
|
|
+ cache = self._get_or_create_cache(cache_name, config)
|
|
|
+
|
|
|
+ # 使用配置的TTL或传入的TTL
|
|
|
+ effective_ttl = ttl if ttl is not None else config.ttl
|
|
|
+
|
|
|
+ with self._locks[cache_name]:
|
|
|
+ # 创建缓存条目
|
|
|
+ entry = CacheEntry(value=value, timestamp=time.time(), ttl=effective_ttl)
|
|
|
+
|
|
|
+ # 处理不同策略
|
|
|
+ if config.strategy in [CacheStrategy.LRU, CacheStrategy.TTL_LRU]:
|
|
|
+ # LRU策略:如果已存在则移动到末尾
|
|
|
+ if key in cache:
|
|
|
+ del cache[key]
|
|
|
+ cache[key] = entry
|
|
|
+
|
|
|
+ # 检查大小限制
|
|
|
+ if config.max_size and len(cache) > config.max_size:
|
|
|
+ # 移除最旧的条目
|
|
|
+ oldest_key = next(iter(cache))
|
|
|
+ del cache[oldest_key]
|
|
|
+ self._stats["evictions"] += 1
|
|
|
+
|
|
|
+ else:
|
|
|
+ cache[key] = entry
|
|
|
+
|
|
|
+ # 检查大小限制
|
|
|
+ if config.max_size and len(cache) > config.max_size:
|
|
|
+ # 简单策略:随机移除一个条目
|
|
|
+ victim_key = next(iter(cache))
|
|
|
+ del cache[victim_key]
|
|
|
+ self._stats["evictions"] += 1
|
|
|
+
|
|
|
+ # 定期清理过期条目
|
|
|
+ self._maybe_cleanup(cache_name)
|
|
|
+
|
|
|
+ def get(
|
|
|
+ self, cache_type: CacheType, key: str, namespace: str = ""
|
|
|
+ ) -> Optional[Any]:
|
|
|
+ """获取缓存值"""
|
|
|
+ cache_name = self._get_cache_name(cache_type, namespace)
|
|
|
+
|
|
|
+ if cache_name not in self._caches:
|
|
|
+ self._stats["misses"] += 1
|
|
|
+ return None
|
|
|
+
|
|
|
+ cache = self._caches[cache_name]
|
|
|
+ config = self._configs[cache_name]
|
|
|
+
|
|
|
+ with self._locks[cache_name]:
|
|
|
+ if key not in cache:
|
|
|
+ self._stats["misses"] += 1
|
|
|
+ return None
|
|
|
+
|
|
|
+ entry = cache[key]
|
|
|
+
|
|
|
+ # 检查过期
|
|
|
+ if entry.is_expired():
|
|
|
+ del cache[key]
|
|
|
+ self._stats["misses"] += 1
|
|
|
+ return None
|
|
|
+
|
|
|
+ # 更新访问信息
|
|
|
+ entry.touch()
|
|
|
+
|
|
|
+ # LRU策略:移动到末尾
|
|
|
+ if config.strategy in [CacheStrategy.LRU, CacheStrategy.TTL_LRU]:
|
|
|
+ del cache[key]
|
|
|
+ cache[key] = entry
|
|
|
+
|
|
|
+ self._stats["hits"] += 1
|
|
|
+ return entry.value
|
|
|
+
|
|
|
+ def delete(self, cache_type: CacheType, key: str, namespace: str = "") -> bool:
|
|
|
+ """删除缓存条目"""
|
|
|
+ cache_name = self._get_cache_name(cache_type, namespace)
|
|
|
+
|
|
|
+ if cache_name not in self._caches:
|
|
|
+ return False
|
|
|
+
|
|
|
+ cache = self._caches[cache_name]
|
|
|
+
|
|
|
+ with self._locks[cache_name]:
|
|
|
+ if key in cache:
|
|
|
+ del cache[key]
|
|
|
+ return True
|
|
|
+ return False
|
|
|
+
|
|
|
+ def clear(self, cache_type: CacheType, namespace: str = "") -> None:
|
|
|
+ """清空指定缓存"""
|
|
|
+ cache_name = self._get_cache_name(cache_type, namespace)
|
|
|
+
|
|
|
+ if cache_name not in self._caches:
|
|
|
+ return
|
|
|
+
|
|
|
+ with self._locks[cache_name]:
|
|
|
+ self._caches[cache_name].clear()
|
|
|
+
|
|
|
+ def invalidate_pattern(
|
|
|
+ self, cache_type: CacheType, pattern: str, namespace: str = ""
|
|
|
+ ) -> int:
|
|
|
+ """按模式失效缓存条目"""
|
|
|
+ cache_name = self._get_cache_name(cache_type, namespace)
|
|
|
+
|
|
|
+ if cache_name not in self._caches:
|
|
|
+ return 0
|
|
|
+
|
|
|
+ cache = self._caches[cache_name]
|
|
|
+ deleted_count = 0
|
|
|
+
|
|
|
+ with self._locks[cache_name]:
|
|
|
+ keys_to_delete = [key for key in cache.keys() if pattern in key]
|
|
|
+ for key in keys_to_delete:
|
|
|
+ del cache[key]
|
|
|
+ deleted_count += 1
|
|
|
+
|
|
|
+ return deleted_count
|
|
|
+
|
|
|
+ def _cleanup_expired(self, cache_name: str) -> int:
|
|
|
+ """清理过期条目"""
|
|
|
+ if cache_name not in self._caches:
|
|
|
+ return 0
|
|
|
+
|
|
|
+ cache = self._caches[cache_name]
|
|
|
+ deleted_count = 0
|
|
|
+
|
|
|
+ with self._locks[cache_name]:
|
|
|
+ expired_keys = [key for key, entry in cache.items() if entry.is_expired()]
|
|
|
+ for key in expired_keys:
|
|
|
+ del cache[key]
|
|
|
+ deleted_count += 1
|
|
|
+
|
|
|
+ return deleted_count
|
|
|
+
|
|
|
+ def _maybe_cleanup(self, cache_name: str):
|
|
|
+ """定期清理检查"""
|
|
|
+ config = self._configs.get(cache_name)
|
|
|
+ if not config:
|
|
|
+ return
|
|
|
+
|
|
|
+ now = time.time()
|
|
|
+ if now - self._last_cleanup > config.cleanup_interval:
|
|
|
+ self._last_cleanup = now
|
|
|
+ deleted = self._cleanup_expired(cache_name)
|
|
|
+ if deleted > 0:
|
|
|
+ self._stats["cleanups"] += 1
|
|
|
+ self.logger.debug(f"清理缓存 {cache_name}: 删除 {deleted} 个过期条目")
|
|
|
+
|
|
|
+
|
|
|
+# 创建全局缓存管理器实例
|
|
|
+cache_manager = GlobalCacheManager()
|