"""Redis缓存服务""" import json import logging from typing import Optional, Any, Union from functools import lru_cache try: import redis from redis import Redis REDIS_AVAILABLE = True except ImportError: REDIS_AVAILABLE = False Redis = None from ..config import get_settings logger = logging.getLogger(__name__) # 全局Redis连接池 _redis_pool: Optional[Any] = None _redis_client: Optional[Any] = None def get_redis_client() -> Optional[Any]: """获取Redis客户端单例""" global _redis_pool, _redis_client if not REDIS_AVAILABLE: logger.warning("Redis module not installed, cache disabled") return None if _redis_client is not None: return _redis_client settings = get_settings() try: _redis_pool = redis.ConnectionPool.from_url( settings.REDIS_URL, max_connections=20, decode_responses=True ) _redis_client = Redis(connection_pool=_redis_pool) # 测试连接 _redis_client.ping() logger.info(f"Redis connected: {settings.REDIS_URL}") return _redis_client except Exception as e: logger.warning(f"Redis connection failed: {e}, cache disabled") _redis_client = None return None class CacheService: """缓存服务 提供统一的缓存接口,支持Redis和内存回退 使用示例: cache = CacheService() # 设置缓存 cache.set("user:123", {"name": "test"}, ttl=3600) # 获取缓存 user = cache.get("user:123") # 删除缓存 cache.delete("user:123") """ def __init__(self, prefix: Optional[str] = None): """初始化缓存服务 Args: prefix: 键前缀,默认使用配置中的REDIS_PREFIX """ settings = get_settings() self.prefix = prefix or settings.REDIS_PREFIX self._client = get_redis_client() # 内存回退缓存(当Redis不可用时使用) self._memory_cache: dict = {} @property def is_available(self) -> bool: """Redis是否可用""" return self._client is not None def _make_key(self, key: str) -> str: """生成完整的缓存键""" return f"{self.prefix}{key}" def get(self, key: str, default: Any = None) -> Any: """获取缓存值 Args: key: 缓存键 default: 默认值 Returns: 缓存值或默认值 """ full_key = self._make_key(key) if self._client: try: value = self._client.get(full_key) if value is None: return default # 尝试解析JSON try: return json.loads(value) except (json.JSONDecodeError, TypeError): return value except Exception as e: logger.error(f"Cache get error: {e}") return default else: # 内存回退 return self._memory_cache.get(full_key, default) def set( self, key: str, value: Any, ttl: Optional[int] = None, nx: bool = False ) -> bool: """设置缓存值 Args: key: 缓存键 value: 缓存值 ttl: 过期时间(秒) nx: 只在键不存在时设置 Returns: 是否设置成功 """ full_key = self._make_key(key) # 序列化值 if isinstance(value, (dict, list)): serialized = json.dumps(value, ensure_ascii=False) else: serialized = str(value) if value is not None else "" if self._client: try: if nx: result = self._client.set(full_key, serialized, ex=ttl, nx=True) else: result = self._client.set(full_key, serialized, ex=ttl) return bool(result) except Exception as e: logger.error(f"Cache set error: {e}") return False else: # 内存回退(不支持TTL和NX) if nx and full_key in self._memory_cache: return False self._memory_cache[full_key] = value return True def delete(self, key: str) -> bool: """删除缓存 Args: key: 缓存键 Returns: 是否删除成功 """ full_key = self._make_key(key) if self._client: try: return bool(self._client.delete(full_key)) except Exception as e: logger.error(f"Cache delete error: {e}") return False else: return self._memory_cache.pop(full_key, None) is not None def exists(self, key: str) -> bool: """检查键是否存在 Args: key: 缓存键 Returns: 是否存在 """ full_key = self._make_key(key) if self._client: try: return bool(self._client.exists(full_key)) except Exception as e: logger.error(f"Cache exists error: {e}") return False else: return full_key in self._memory_cache def ttl(self, key: str) -> int: """获取键的剩余过期时间 Args: key: 缓存键 Returns: 剩余秒数,-1表示永不过期,-2表示键不存在 """ full_key = self._make_key(key) if self._client: try: return self._client.ttl(full_key) except Exception as e: logger.error(f"Cache ttl error: {e}") return -2 else: return -1 if full_key in self._memory_cache else -2 def incr(self, key: str, amount: int = 1) -> int: """递增计数器 Args: key: 缓存键 amount: 递增量 Returns: 递增后的值 """ full_key = self._make_key(key) if self._client: try: return self._client.incrby(full_key, amount) except Exception as e: logger.error(f"Cache incr error: {e}") return 0 else: current = self._memory_cache.get(full_key, 0) new_value = int(current) + amount self._memory_cache[full_key] = new_value return new_value def expire(self, key: str, ttl: int) -> bool: """设置键的过期时间 Args: key: 缓存键 ttl: 过期时间(秒) Returns: 是否设置成功 """ full_key = self._make_key(key) if self._client: try: return bool(self._client.expire(full_key, ttl)) except Exception as e: logger.error(f"Cache expire error: {e}") return False else: return full_key in self._memory_cache def clear_prefix(self, prefix: str) -> int: """删除指定前缀的所有键 Args: prefix: 键前缀 Returns: 删除的键数量 """ full_prefix = self._make_key(prefix) if self._client: try: keys = self._client.keys(f"{full_prefix}*") if keys: return self._client.delete(*keys) return 0 except Exception as e: logger.error(f"Cache clear_prefix error: {e}") return 0 else: count = 0 keys_to_delete = [k for k in self._memory_cache if k.startswith(full_prefix)] for k in keys_to_delete: del self._memory_cache[k] count += 1 return count # 全局缓存实例 _cache_instance: Optional[CacheService] = None def get_cache() -> CacheService: """获取全局缓存实例""" global _cache_instance if _cache_instance is None: _cache_instance = CacheService() return _cache_instance