Files
kplam 51dc8dc4f6 Initial commit: QMDSearch 分层信息检索服务
- FastAPI + Qdrant + Redis + Ollama 技术栈
- L1→L2→L3→chunk 四层分层检索(dense + sparse RRF 融合)
- 文档三级总结与 2.5 级回退
- query 解析路由与分类
- /admin 管理页面
2026-07-29 21:24:40 +08:00

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