65 lines
1.8 KiB
Python
65 lines
1.8 KiB
Python
"""统一 Redis 连接管理。
|
||
|
||
提供全局 Redis 连接池单例,所有模块共享同一连接池,
|
||
避免多处独立创建连接导致资源浪费和连接数失控。
|
||
|
||
用法:
|
||
from app.core.redis import get_redis
|
||
|
||
redis = await get_redis()
|
||
await redis.set("key", "value")
|
||
"""
|
||
import logging
|
||
|
||
import redis.asyncio as aioredis
|
||
|
||
from app.config import settings
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
# 全局 Redis 连接池单例
|
||
_redis: aioredis.Redis | None = None
|
||
|
||
|
||
async def get_redis() -> aioredis.Redis:
|
||
"""获取全局 Redis 连接。
|
||
|
||
首次调用时根据 settings.REDIS_URL 创建连接池,
|
||
后续调用返回同一实例。REDIS_URL 为空时抛出 ValueError。
|
||
"""
|
||
global _redis
|
||
if _redis is None:
|
||
if not settings.REDIS_URL:
|
||
raise ValueError("REDIS_URL is not configured")
|
||
_redis = aioredis.from_url(
|
||
settings.REDIS_URL,
|
||
encoding="utf-8",
|
||
decode_responses=True,
|
||
)
|
||
logger.info("Redis connection pool created (url=%s)", _safe_url(settings.REDIS_URL))
|
||
return _redis
|
||
|
||
|
||
async def close_redis() -> None:
|
||
"""关闭全局 Redis 连接。在应用 shutdown 时调用。"""
|
||
global _redis
|
||
if _redis is not None:
|
||
await _redis.aclose()
|
||
_redis = None
|
||
logger.info("Redis connection pool closed")
|
||
|
||
|
||
def is_redis_configured() -> bool:
|
||
"""检查 Redis 是否已配置(REDIS_URL 非空)。"""
|
||
return bool(settings.REDIS_URL)
|
||
|
||
|
||
def _safe_url(url: str) -> str:
|
||
"""隐藏 Redis URL 中的密码部分。"""
|
||
if "@" in url:
|
||
# redis://:password@host:port/db → redis://***@host:port/db
|
||
parts = url.split("@", 1)
|
||
prefix = parts[0].rsplit(":", 1)[0]
|
||
return f"{prefix}:***@{parts[1]}"
|
||
return url
|