51dc8dc4f6
- FastAPI + Qdrant + Redis + Ollama 技术栈 - L1→L2→L3→chunk 四层分层检索(dense + sparse RRF 融合) - 文档三级总结与 2.5 级回退 - query 解析路由与分类 - /admin 管理页面
76 lines
2.4 KiB
Python
76 lines
2.4 KiB
Python
"""Redis 缓存服务
|
|
|
|
为检索结果与 query 解析结果提供 JSON 缓存。所有操作容错:
|
|
Redis 不可用或缓存数据异常时降级为未命中 / 写入失败,绝不影响主流程。
|
|
"""
|
|
|
|
import json
|
|
from typing import Any
|
|
|
|
import structlog
|
|
from redis import asyncio as redis_async
|
|
|
|
from app.config import settings
|
|
|
|
logger = structlog.get_logger()
|
|
|
|
|
|
class RedisCache:
|
|
"""Redis JSON 缓存客户端(懒连接,全操作容错)"""
|
|
|
|
def __init__(self) -> None:
|
|
self._client: redis_async.Redis | None = None
|
|
|
|
def _get_client(self) -> redis_async.Redis:
|
|
"""懒创建 Redis 客户端(TCP 连接在首次执行命令时才建立)"""
|
|
if self._client is None:
|
|
self._client = redis_async.from_url(settings.redis_url, decode_responses=True)
|
|
return self._client
|
|
|
|
async def get_json(self, key: str) -> dict[str, Any] | None:
|
|
"""读取缓存并反序列化为 dict
|
|
|
|
任何异常(连接失败 / JSON 解析失败)或值非 dict 时都降级为未命中,返回 None。
|
|
"""
|
|
try:
|
|
raw = await self._get_client().get(key)
|
|
if raw is None:
|
|
return None
|
|
data = json.loads(raw)
|
|
return data if isinstance(data, dict) else None
|
|
except Exception:
|
|
logger.warning("Redis 读取缓存失败,降级为未命中", key=key, exc_info=True)
|
|
return None
|
|
|
|
async def set_json(self, key: str, value: dict[str, Any], ttl: int | None = None) -> bool:
|
|
"""写入缓存(SETEX),ttl 缺省取 settings.cache_ttl
|
|
|
|
异常时记录日志并返回 False,不影响主流程。
|
|
"""
|
|
try:
|
|
await self._get_client().setex(
|
|
key, ttl if ttl is not None else settings.cache_ttl, json.dumps(value, ensure_ascii=False)
|
|
)
|
|
return True
|
|
except Exception:
|
|
logger.warning("Redis 写入缓存失败", key=key, exc_info=True)
|
|
return False
|
|
|
|
async def close(self) -> None:
|
|
"""关闭底层连接"""
|
|
if self._client is not None:
|
|
await self._client.aclose()
|
|
self._client = None
|
|
|
|
|
|
# 模块级懒加载单例,避免每请求重建客户端
|
|
_cache: RedisCache | None = None
|
|
|
|
|
|
def get_cache() -> RedisCache:
|
|
"""获取全局 RedisCache 单例"""
|
|
global _cache
|
|
if _cache is None:
|
|
_cache = RedisCache()
|
|
return _cache
|