285 lines
8.0 KiB
Python
285 lines
8.0 KiB
Python
#!/usr/bin/env python
|
||
# -*- coding: utf-8 -*-
|
||
|
||
"""
|
||
缓存管理模块
|
||
提供Redis缓存连接和操作功能
|
||
"""
|
||
|
||
import logging
|
||
import json
|
||
from typing import Any, Optional, Union, Dict, List
|
||
import redis
|
||
|
||
from config.database_config import CacheConfig
|
||
from utils.logger import get_logger
|
||
|
||
# 获取日志记录器
|
||
logger = get_logger("data.cache")
|
||
|
||
|
||
class RedisClient:
|
||
"""Redis客户端,封装常用的Redis操作"""
|
||
|
||
_instance = None
|
||
_redis_client = None
|
||
|
||
def __new__(cls, *args, **kwargs):
|
||
"""单例模式,确保只有一个Redis连接"""
|
||
if cls._instance is None:
|
||
cls._instance = super(RedisClient, cls).__new__(cls)
|
||
return cls._instance
|
||
|
||
def __init__(self):
|
||
"""初始化Redis连接"""
|
||
if self._redis_client is None:
|
||
try:
|
||
self._redis_client = redis.Redis(
|
||
**CacheConfig.get_redis_connection_args()
|
||
)
|
||
logger.info(f"Redis连接已建立:{CacheConfig.REDIS_HOST}:{CacheConfig.REDIS_PORT}/{CacheConfig.REDIS_DB}")
|
||
except Exception as e:
|
||
logger.error(f"Redis连接失败:{str(e)}")
|
||
self._redis_client = None
|
||
|
||
def get_client(self) -> redis.Redis:
|
||
"""获取Redis客户端"""
|
||
return self._redis_client
|
||
|
||
def add_prefix(self, key: str) -> str:
|
||
"""添加键前缀"""
|
||
return f"{CacheConfig.REDIS_PREFIX}{key}"
|
||
|
||
def set(self, key: str, value: Any, expire: Optional[int] = None) -> bool:
|
||
"""
|
||
设置缓存
|
||
|
||
Args:
|
||
key: 缓存键
|
||
value: 缓存值,如果不是字符串会尝试JSON序列化
|
||
expire: 过期时间(秒)
|
||
|
||
Returns:
|
||
bool: 操作是否成功
|
||
"""
|
||
try:
|
||
if self._redis_client is None:
|
||
return False
|
||
|
||
# 添加前缀
|
||
prefixed_key = self.add_prefix(key)
|
||
|
||
# 处理非字符串值
|
||
if not isinstance(value, (str, bytes, int, float)):
|
||
value = json.dumps(value, ensure_ascii=False)
|
||
|
||
# 设置缓存
|
||
self._redis_client.set(prefixed_key, value)
|
||
|
||
# 设置过期时间
|
||
if expire is not None:
|
||
self._redis_client.expire(prefixed_key, expire)
|
||
|
||
return True
|
||
except Exception as e:
|
||
logger.error(f"设置缓存失败:{str(e)}")
|
||
return False
|
||
|
||
def get(self, key: str, default: Any = None) -> Any:
|
||
"""
|
||
获取缓存
|
||
|
||
Args:
|
||
key: 缓存键
|
||
default: 默认值(缓存不存在时返回)
|
||
|
||
Returns:
|
||
Any: 缓存值或默认值
|
||
"""
|
||
try:
|
||
if self._redis_client is None:
|
||
return default
|
||
|
||
# 添加前缀
|
||
prefixed_key = self.add_prefix(key)
|
||
|
||
# 获取缓存
|
||
value = self._redis_client.get(prefixed_key)
|
||
|
||
if value is None:
|
||
return default
|
||
|
||
# 尝试JSON反序列化
|
||
try:
|
||
return json.loads(value)
|
||
except (TypeError, json.JSONDecodeError):
|
||
return value
|
||
except Exception as e:
|
||
logger.error(f"获取缓存失败:{str(e)}")
|
||
return default
|
||
|
||
def delete(self, key: str) -> bool:
|
||
"""
|
||
删除缓存
|
||
|
||
Args:
|
||
key: 缓存键
|
||
|
||
Returns:
|
||
bool: 操作是否成功
|
||
"""
|
||
try:
|
||
if self._redis_client is None:
|
||
return False
|
||
|
||
# 添加前缀
|
||
prefixed_key = self.add_prefix(key)
|
||
|
||
# 删除缓存
|
||
return bool(self._redis_client.delete(prefixed_key))
|
||
except Exception as e:
|
||
logger.error(f"删除缓存失败:{str(e)}")
|
||
return False
|
||
|
||
def exists(self, key: str) -> bool:
|
||
"""
|
||
检查缓存是否存在
|
||
|
||
Args:
|
||
key: 缓存键
|
||
|
||
Returns:
|
||
bool: 缓存是否存在
|
||
"""
|
||
try:
|
||
if self._redis_client is None:
|
||
return False
|
||
|
||
# 添加前缀
|
||
prefixed_key = self.add_prefix(key)
|
||
|
||
# 检查缓存
|
||
return bool(self._redis_client.exists(prefixed_key))
|
||
except Exception as e:
|
||
logger.error(f"检查缓存失败:{str(e)}")
|
||
return False
|
||
|
||
def expire(self, key: str, seconds: int) -> bool:
|
||
"""
|
||
设置缓存过期时间
|
||
|
||
Args:
|
||
key: 缓存键
|
||
seconds: 过期时间(秒)
|
||
|
||
Returns:
|
||
bool: 操作是否成功
|
||
"""
|
||
try:
|
||
if self._redis_client is None:
|
||
return False
|
||
|
||
# 添加前缀
|
||
prefixed_key = self.add_prefix(key)
|
||
|
||
# 设置过期时间
|
||
return bool(self._redis_client.expire(prefixed_key, seconds))
|
||
except Exception as e:
|
||
logger.error(f"设置缓存过期时间失败:{str(e)}")
|
||
return False
|
||
|
||
def ttl(self, key: str) -> int:
|
||
"""
|
||
获取缓存剩余过期时间
|
||
|
||
Args:
|
||
key: 缓存键
|
||
|
||
Returns:
|
||
int: 剩余过期时间(秒),-1表示永不过期,-2表示键不存在
|
||
"""
|
||
try:
|
||
if self._redis_client is None:
|
||
return -2
|
||
|
||
# 添加前缀
|
||
prefixed_key = self.add_prefix(key)
|
||
|
||
# 获取过期时间
|
||
return self._redis_client.ttl(prefixed_key)
|
||
except Exception as e:
|
||
logger.error(f"获取缓存过期时间失败:{str(e)}")
|
||
return -2
|
||
|
||
def hset(self, key: str, field: str, value: Any) -> bool:
|
||
"""
|
||
设置哈希表字段
|
||
|
||
Args:
|
||
key: 缓存键
|
||
field: 字段名
|
||
value: 字段值
|
||
|
||
Returns:
|
||
bool: 操作是否成功
|
||
"""
|
||
try:
|
||
if self._redis_client is None:
|
||
return False
|
||
|
||
# 添加前缀
|
||
prefixed_key = self.add_prefix(key)
|
||
|
||
# 处理非字符串值
|
||
if not isinstance(value, (str, bytes, int, float)):
|
||
value = json.dumps(value, ensure_ascii=False)
|
||
|
||
# 设置哈希表字段
|
||
self._redis_client.hset(prefixed_key, field, value)
|
||
return True
|
||
except Exception as e:
|
||
logger.error(f"设置哈希表字段失败:{str(e)}")
|
||
return False
|
||
|
||
def hget(self, key: str, field: str, default: Any = None) -> Any:
|
||
"""
|
||
获取哈希表字段
|
||
|
||
Args:
|
||
key: 缓存键
|
||
field: 字段名
|
||
default: 默认值(字段不存在时返回)
|
||
|
||
Returns:
|
||
Any: 字段值或默认值
|
||
"""
|
||
try:
|
||
if self._redis_client is None:
|
||
return default
|
||
|
||
# 添加前缀
|
||
prefixed_key = self.add_prefix(key)
|
||
|
||
# 获取哈希表字段
|
||
value = self._redis_client.hget(prefixed_key, field)
|
||
|
||
if value is None:
|
||
return default
|
||
|
||
# 尝试JSON反序列化
|
||
try:
|
||
return json.loads(value)
|
||
except (TypeError, json.JSONDecodeError):
|
||
return value
|
||
except Exception as e:
|
||
logger.error(f"获取哈希表字段失败:{str(e)}")
|
||
return default
|
||
|
||
|
||
# 导出Redis客户端单例
|
||
redis_client = RedisClient()
|
||
|
||
|
||
def get_cache():
|
||
"""获取Redis客户端"""
|
||
return redis_client |