from functools import lru_cache import logging import os from typing import Optional, TYPE_CHECKING import redis logger = logging.getLogger(__name__) REDIS_URL = os.getenv("REDIS_URL", "").strip() REDIS_CONNECT_TIMEOUT_SECONDS = float(os.getenv("REDIS_CONNECT_TIMEOUT_SECONDS", "2")) REDIS_SOCKET_TIMEOUT_SECONDS = float(os.getenv("REDIS_SOCKET_TIMEOUT_SECONDS", "2")) _redis_client: Optional["redis.Redis"] = None _initialized = False @lru_cache def get_redis_client() -> Optional["redis.Redis"]: """Return a singleton Redis client, or None when Redis is unavailable.""" global _redis_client, _initialized if _initialized: return _redis_client _initialized = True if not REDIS_URL: logger.info("REDIS_URL 未配置,验证码将回退到内存存储") _redis_client = None return _redis_client try: _redis_client = redis.Redis.from_url( REDIS_URL, decode_responses=True, socket_connect_timeout=REDIS_CONNECT_TIMEOUT_SECONDS, socket_timeout=REDIS_SOCKET_TIMEOUT_SECONDS, health_check_interval=30, ) _redis_client.ping() logger.info("Redis 连接成功,验证码将优先使用 Redis") except Exception as exc: # pragma: no cover logger.warning("Redis 连接失败,将回退到数据库存储。error=%s", exc) _redis_client = None return _redis_client