feat(phase1): core/ 基础设施迁移,旧路径改垫片
- core/: database/redis_client/jwt/password/response/exceptions/security/logger/deps/middleware/app - create_app 迁移到 core/app.py,中间件注册顺序与异常处理器原样保留 - extensions/ utils/ apps/ 旧文件全部改为 re-export 垫片,import 路径保持兼容 - 新增 core 单元测试(jwt/password/response/request 共 12 个) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
"""
|
||||
Redis客户端核心模块(由 utils/redis_ctx.py 迁移而来)
|
||||
|
||||
封装 Redis 连接管理和请求级上下文注入:
|
||||
- lifespan 中创建连接池,关闭时断开
|
||||
- 每个请求通过中间件将 Redis 客户端注入 contextvars
|
||||
- 业务代码通过 get_redis() 获取当前请求的 Redis 客户端
|
||||
"""
|
||||
|
||||
import logging
|
||||
from contextvars import ContextVar
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
from fastapi import FastAPI
|
||||
from redis.asyncio import Redis
|
||||
from redis import asyncio as aioredis
|
||||
|
||||
from config import REDIS_HOST, REDIS_PORT, REDIS_PASSWORD, REDIS_INDEX, REDIS_AUTH
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# 请求级 Redis 客户端上下文变量
|
||||
_redis_ctx: ContextVar[Redis] = ContextVar('_redis_ctx')
|
||||
|
||||
|
||||
def get_redis() -> Redis:
|
||||
"""获取当前请求的 Redis 客户端"""
|
||||
return _redis_ctx.get()
|
||||
|
||||
|
||||
def _build_redis_url() -> str:
|
||||
"""构建 Redis 连接 URL"""
|
||||
if REDIS_AUTH and REDIS_PASSWORD:
|
||||
return f"redis://:{REDIS_PASSWORD}@{REDIS_HOST}:{REDIS_PORT}/{REDIS_INDEX}"
|
||||
return f"redis://{REDIS_HOST}:{REDIS_PORT}/{REDIS_INDEX}"
|
||||
|
||||
|
||||
async def create_redis_pool() -> Redis:
|
||||
"""创建 Redis 异步连接池"""
|
||||
redis = await aioredis.from_url(
|
||||
_build_redis_url(),
|
||||
encoding="utf-8",
|
||||
decode_responses=True,
|
||||
protocol=2
|
||||
)
|
||||
return redis
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def redis_lifespan(app: FastAPI):
|
||||
"""Redis 生命周期管理,替代已废弃的 on_event("startup"/"shutdown")"""
|
||||
# startup: 创建连接池
|
||||
redis = await create_redis_pool()
|
||||
await redis.ping()
|
||||
app.state.redis = redis
|
||||
print(f"Redis 连接成功 --> {REDIS_HOST}:{REDIS_PORT}/{REDIS_INDEX}")
|
||||
logger.info(f"Redis 连接成功: {REDIS_HOST}:{REDIS_PORT}/{REDIS_INDEX}")
|
||||
|
||||
# startup: 初始化定时任务调度器
|
||||
from apps.services.scheduler import init_scheduler, shutdown_scheduler
|
||||
job_count = init_scheduler()
|
||||
logger.info(f"定时任务调度器已启动,加载 {job_count} 个任务")
|
||||
|
||||
yield
|
||||
# shutdown: 关闭调度器
|
||||
shutdown_scheduler()
|
||||
|
||||
# shutdown: 关闭连接
|
||||
await redis.aclose()
|
||||
app.state.redis = None
|
||||
logger.info("Redis 连接已关闭")
|
||||
|
||||
|
||||
async def redis_middleware(request, call_next):
|
||||
"""Redis 上下文注入中间件,每个请求开始时将 Redis 客户端注入 contextvars"""
|
||||
redis = getattr(request.app.state, 'redis', None)
|
||||
if redis is not None:
|
||||
_redis_ctx.set(redis)
|
||||
return await call_next(request)
|
||||
Reference in New Issue
Block a user