Initial commit: QMDSearch 分层信息检索服务
- FastAPI + Qdrant + Redis + Ollama 技术栈 - L1→L2→L3→chunk 四层分层检索(dense + sparse RRF 融合) - 文档三级总结与 2.5 级回退 - query 解析路由与分类 - /admin 管理页面
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
"""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
|
||||
Reference in New Issue
Block a user