Python Redis单例客户端(支持URL/参数双模式)
·
以下是详细的解释说明和整合后的单例类实现:
两种连接方式的区别说明
-
redis.Redis(*args, **kwargs)- 直接通过Redis类的构造函数创建连接
- 需要手动传递
host、port、db等参数 - 适合简单场景或本地开发
-
redis.from_url(redis_url)- 通过URL格式字符串创建连接(更符合生产环境配置)
- URL格式示例:
redis://username:password@host:port/db - 自动解析URL中的认证信息、数据库编号等
- 适合需要环境变量配置的场景
整合后的单例类实现
import redis
from functools import wraps
class RedisJSONClient:
"""支持单例模式和URL/参数两种连接方式的Redis客户端"""
REDIS_URL = None # 可在子类或外部配置
_instance = None
_redis_conn = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super().__new__(cls)
# 初始化时暂不创建连接(延迟到首次使用时)
return cls._instance
def __init__(self, redis_url=None, **kwargs):
"""
初始化连接(懒加载模式)
:param redis_url: 优先使用URL连接方式,格式 redis://[:password]@host:port/db
:param kwargs: 备用参数,当无URL时使用 host/port/db 等参数
"""
if not hasattr(self, '_redis_conn') or self._redis_conn is None:
self._redis_url = redis_url or self.REDIS_URL
self._conn_kwargs = kwargs
@property
def client(self):
"""获取连接(懒加载)"""
if self._redis_conn is None:
self._connect()
return self._redis_conn
def _connect(self):
"""实际建立连接的方法"""
try:
if self._redis_url:
self._redis_conn = redis.from_url(self._redis_url)
else:
self._redis_conn = redis.Redis(**self._conn_kwargs)
# 测试连接
self._redis_conn.ping()
except Exception as e:
self._redis_conn = None
raise ConnectionError(f"Redis连接失败: {str(e)}")
def __del__(self):
"""析构时关闭连接"""
if self._redis_conn:
self._redis_conn.close()
关键设计说明
-
统一接口:
- 保留
client属性作为统一访问入口 - 同时支持URL和参数两种连接方式
- 保留
-
懒加载优化:
- 首次访问
client属性时才实际建立连接 - 避免实例化时立即连接可能导致的异常
- 首次访问
-
优先级逻辑:
# 使用顺序: # 1. 优先使用构造函数传入的redis_url # 2. 其次使用类变量REDIS_URL # 3. 最后回退到kwargs参数 -
生产环境建议:
# 推荐在子类中配置默认URL class ProductionRedisClient(RedisJSONClient): REDIS_URL = "redis://:password@prod-redis:6379/0"
使用示例
# 方式1:通过URL连接
client1 = RedisJSONClient(redis_url="redis://localhost:6379/0")
r = client1.client # 实际建立连接
# 方式2:通过参数连接
client2 = RedisJSONClient(host='localhost', port=6379, db=0)
# 单例验证
print(client1 is client2) # 输出 True
# 子类配置默认URL
class ConfigurableClient(RedisJSONClient):
REDIS_URL = "redis://:password@remote-host:6380/1"
client3 = ConfigurableClient() # 自动使用REDIS_URL
为什么这样整合?
-
兼容性:
- 保留原有
RedisJSONClient的接口设计 - 新增对直接参数的支持
- 保留原有
-
可维护性:
- 连接逻辑集中在
_connect方法中 - 错误处理和资源清理更明确
- 连接逻辑集中在
-
灵活性:
- 既支持生产环境的URL配置
- 也支持开发环境的参数直传
这样的设计既保持了原有功能,又提供了更健壮的单例实现。
更多推荐
所有评论(0)