980a2db6d9
- 基础设施层 async:SQLAlchemy 异步引擎/会话、33 Repository async 化、models/security/seed 迁入 infrastructure、新增 cache.py(redis.asyncio) 与 oss.py(aioboto3) - 接口层:routers 迁 api/routers 并全 async,dependencies 迁 api/dependencies(get_db/get_current_user async) - 依赖:sqlalchemy[asyncio]/aiosqlite/asyncmy/redis/aioboto3;config 异步 URL + Redis/OSS 配置 - 删除废弃:旧同步 db/dependencies/repositories/storage - 验证:平台 19 路由 + 培训 48 路由全注册;/health /auth/login /auth/me /admin/tasks /notifications 等接口 async 可用
61 lines
1.9 KiB
Python
61 lines
1.9 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""基础设施层 · Redis 异步封装(缓存 / 限流 / 分布式锁)。"""
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
from typing import Any, Optional
|
|
|
|
from redis.asyncio import Redis
|
|
|
|
from .. import config
|
|
|
|
|
|
class Cache:
|
|
"""redis.asyncio 封装:连接池 / get / set / incr / 分布式锁(SET NX EX)。"""
|
|
|
|
def __init__(self, url: str | None = None) -> None:
|
|
self._redis: Optional[Redis] = None
|
|
self._url = url or config.REDIS_URL
|
|
|
|
async def connect(self) -> None:
|
|
if self._redis is None:
|
|
self._redis = Redis.from_url(self._url, decode_responses=True)
|
|
|
|
async def close(self) -> None:
|
|
if self._redis is not None:
|
|
await self._redis.aclose()
|
|
self._redis = None
|
|
|
|
async def get(self, key: str) -> str | None:
|
|
if self._redis is None:
|
|
return None
|
|
return await self._redis.get(key)
|
|
|
|
async def set(self, key: str, value: str, ttl: int | None = None) -> None:
|
|
if self._redis is None:
|
|
return
|
|
await self._redis.set(key, value, ex=ttl)
|
|
|
|
async def delete(self, key: str) -> None:
|
|
if self._redis is None:
|
|
return
|
|
await self._redis.delete(key)
|
|
|
|
async def incr(self, key: str) -> int:
|
|
if self._redis is None:
|
|
return 0
|
|
return int(await self._redis.incr(key))
|
|
|
|
async def lock(self, key: str, ttl: int = 30) -> bool:
|
|
"""分布式锁:SET key token NX EX ttl(成功拿到锁返回 True)。"""
|
|
if self._redis is None:
|
|
return True # 无 Redis 时降级为总是通过(单机部署)
|
|
token = f"{asyncio.get_event_loop().time()}"
|
|
ok = await self._redis.set(key, token, nx=True, ex=ttl)
|
|
return bool(ok)
|
|
|
|
async def unlock(self, key: str) -> None:
|
|
if self._redis is None:
|
|
return
|
|
await self._redis.delete(key)
|