redis-py 连接指南:从单节点、Sentinel、Cluster 到异步客户端的完整连接体系
【免费下载链接】redis-pyRedis Python client项目地址: https://gitcode.com/GitHub_Trending/re/redis-py
本篇指南以 redis-py(Redis Python client)官方文档 docs/connections.rst 为骨架,系统讲解该库提供的全系列连接客户端:直接连接标准 Redis 节点的通用客户端(Generic Client)、面向高可用的 Sentinel 客户端、面向分片集群的 Cluster 客户端,以及对应的异步(asyncio)版本,并深入Connection与ConnectionPool的底层实现。读完本文,你将掌握不同部署形态下 redis-py 客户端的选择依据、构造参数含义、URL 配置方式与连接池复用技巧,并能依据源码理解各客户端的连接管理原理。
客户端选型总览
redis-py 针对不同的 Redis 部署拓扑提供了四类连接入口,全部集中在 redis/client.py、redis/sentinel.py、redis/cluster.py 与 redis/asyncio/ 目录下:
| 部署形态 | 同步客户端 | 异步客户端 | 核心类位置 |
|---|---|---|---|
| 单节点 / 主从 | redis.Redis | redis.asyncio.client.Redis | redis/client.py、redis/asyncio/client.py |
| Sentinel 高可用 | redis.sentinel.Sentinel | redis.asyncio.sentinel.Sentinel | redis/sentinel.py、redis/asyncio/sentinel.py |
| Cluster 集群 | redis.cluster.RedisCluster | redis.asyncio.cluster.RedisCluster | redis/cluster.py、redis/asyncio/cluster.py |
| 底层连接 / 连接池 | redis.connection.Connection/ConnectionPool | redis.asyncio.connection.Connection/ConnectionPool | redis/connection.py、redis/asyncio/connection.py |
同步与异步两个体系共享相同的命令集与参数语义,区别仅在于异步客户端的方法返回awaitable对象。下文逐类展开。
通用客户端(Generic Client)
通用客户端redis.Redis用于直接连接一个标准 Redis 节点,是 redis-py 最基础、最常用的入口。其类定义位于 redis/client.py,继承自RedisModuleCommands、CoreCommands、SentinelCommands,因此同时具备 Redis 核心命令、模块命令与 Sentinel 管理命令的调用能力。
直接构造方式
最直观的用法是传入主机与端口:
import redis r = redis.Redis(host="localhost", port=6379) r.set("foo", "bar") print(r.get("foo")) # b'bar'Redis.__init__(见 redis/client.py)支持一组丰富的连接参数,常用参数及其默认值如下:
| 参数 | 默认值 | 说明 |
|---|---|---|
host/port | localhost/6379 | 目标 Redis 节点地址 |
db | 0 | 逻辑数据库编号(0~15,由服务端databases配置决定上限) |
username/password | None | 认证凭据,Redis 6+ 支持 ACL 用户名 |
socket_timeout | 默认超时 | 读写超时(秒),None表示不超时 |
socket_connect_timeout | 默认值 | 建立 TCP 连接的超时(秒) |
socket_read_size | 默认值 | 单次读取套接字的缓冲区大小(字节) |
socket_keepalive | True | 是否开启 TCP keepalive,默认开启;空闲 30 秒、间隔 5 秒、3 次探测,可用socket_keepalive_options(如{socket.TCP_KEEPIDLE: 30})定制 |
decode_responses | False | 为True时将响应解码为 UTF-8 字符串而非字节串 |
encoding/encoding_errors | utf-8/strict | 编解码配置 |
max_connections | None | 连接池上限,未指定时由连接池默认(100)决定 |
single_connection_client | False | 为True时不使用连接池、独占单条连接,此时客户端实例不是线程安全的 |
health_check_interval | 0 | 空闲连接健康检查间隔(秒),0表示禁用 |
client_name | None | 通过CLIENT SETNAME设置的客户端名称,便于服务端排查 |
ssl系列 | False | ssl_keyfile、ssl_certfile、ssl_ca_certs、ssl_check_hostname等 TLS 配置 |
retry/retry_on_error | 默认指数退避重试 | 网络错误重试策略,详见 docs/retry.rst |
从源码可见(redis/client.py):当未显式传入connection_pool时,客户端会根据unix_socket_path是否设置,自动选择UnixDomainSocketConnection或 TCP 型Connection(若ssl=True则选用SSLConnection),并把上述参数统一交给连接池构造。因此,这些连接参数本质上都会传递到Connection与ConnectionPool层。
URL 方式:from_url
Redis.from_url(见 redis/client.py)允许用连接串一次性配置客户端,并自动完成连接池构建:
import redis # 支持三种 scheme r = redis.Redis.from_url("redis://localhost:6379/0") # TCP r_ssl = redis.Redis.from_url("rediss://user:pass@localhost:6379/0") # SSL 包装的 TCP r_unix = redis.Redis.from_url("unix:///path/to/redis.sock?db=0") # Unix 域套接字 # 可叠加关键字参数与查询串参数 r = redis.Redis.from_url("redis://localhost:6379/0?decode_responses=True", max_connections=50)数据库编号的解析优先级依次为:URL 查询串中的db参数(如redis://localhost?db=0)→ URL 路径(如redis://localhost/0)→from_url的关键字参数db,均未指定时默认db=0。查询串中的参数会被自动转换为合适的 Python 类型(布尔值可写作"True"/"False"或"Yes"/"No",无法转换时抛出ValueError),且查询串参数优先于关键字参数。所有 URL 值与用户名、密码均会经过urllib.parse.unquote进行百分号解码。
从已有连接池构建:from_pool
若需精细控制连接池的生命周期,可使用Redis.from_pool(connection_pool)(redis/client.py)。需要注意:from_pool返回的客户端会接管(ownership)连接池,在客户端关闭或被垃圾回收时关闭池内全部连接,因此同一个池不要通过from_pool共享给多个客户端(线程间交替关闭会中断对方正在使用的连接)。若要共享一个池,应直接使用Redis(connection_pool=pool)构造(不接管池的关闭),并结合上下文管理器管理池:
from redis import Redis from redis.connection import ConnectionPool with ConnectionPool.from_url("redis://localhost:6379/0") as pool: r = Redis(connection_pool=pool) # 不接管池的生命周期,可安全共享 r.set("foo", "bar")Sentinel 客户端
Redis Sentinel 为 Redis 提供高可用能力:它持续监控主节点与副本节点,在主节点故障时自动执行故障转移(failover)并选举新主节点。Sentinel 本身以独立进程运行(默认端口 26379),并提供一组只能在 Sentinel 模式下执行的专用命令,因此需要专门的客户端来连接与操作 Sentinel 节点。
Sentinel 模式下的完整部署说明见 docs/geographic_failover.rst,本地开发可用仓库提供的 dockers/sentinel.conf 快速搭建哨兵环境。
连接与发现:Sentinel类
Sentinel类(redis/sentinel.py)接收哨兵节点列表,对每个节点内部封装一个Redis客户端。官方文档给出的连接示例(假设 Sentinel 与 Redis 分别运行在下述端口):
>>> from redis import Sentinel >>> sentinel = Sentinel([('localhost', 26379)], socket_timeout=0.1) >>> sentinel.discover_master('mymaster') ('127.0.0.1', 6379) >>> sentinel.discover_slaves('mymaster') [('127.0.0.1', 6380)]其中:
sentinels:哨兵节点列表,每个节点是(hostname, port)二元组;min_other_sentinels:哨兵视为"可信"所需的最少对等哨兵数量。查询某个哨兵时,若其报告的对等节点数低于该阈值,其响应将不被采信(见 redis/sentinel.py 与check_master_state中的num-other-sentinels校验);sentinel_kwargs:连接哨兵节点时使用的参数字典,任何普通 Redis 连接参数均可放入;未指定时自动继承connection_kwargs中以socket_开头的选项(如socket_timeout、socket_keepalive),见 redis/sentinel.py;force_master_ip:强制指定 master 地址的 IP(可覆盖哨兵返回的ip),用于 NAT 或容器场景,见discover_master实现(redis/sentinel.py)。
discover_master(service_name)会遍历哨兵节点调用SENTINEL MASTERS,校验 master 状态(必须是 master、未处于主观下线 sdown / 客观下线 odown、对等哨兵数量达标)后返回(ip, port);找不到合格 master 时抛出MasterNotFoundError(MasterNotFoundError与SlaveNotFoundError均定义于 redis/sentinel.py,继承自ConnectionError)。discover_slaves(service_name)则通过SENTINEL SLAVES查询并过滤掉处于 sdown/odown 状态的副本,返回存活的副本地址列表。
另外,Sentinel实现了上下文管理器协议(__enter__/__exit__),并支持close()关闭全部内部哨兵客户端及其连接池(见 redis/sentinel.py)。
读写分离:master_for与slave_for
Sentinel的真正价值在于动态发现读写节点并自动跟随故障转移。master_for与slave_for(以及语义更准确的别名replica_for)分别返回绑定 master 或 slave 的Redis客户端(默认redis_class=Redis),底层均使用SentinelConnectionPool连接池(见 redis/sentinel.py):
from redis.sentinel import Sentinel sentinel = Sentinel([('localhost', 26379)], socket_timeout=0.1) # 写客户端:始终路由到当前 master;发生故障转移后自动探测新 master master = sentinel.master_for('mymaster', socket_timeout=0.1) master.set('foo', 'bar') # 读客户端:在存活副本之间轮询(round-robin),适合读多写少场景 slave = sentinel.slave_for('mymaster', socket_timeout=0.1) print(slave.get('foo')) # b'bar'从master_for/slave_for的源码实现可以看到,二者只是分别向连接池注入is_master=True/False后再用redis_class.from_pool(...)包装连接池。因此:
- master 客户端在故障转移后会通过哨兵重新发现新 master;当检测到原 master 地址变化时,连接池会断开所有空闲连接,使后续请求自然切换到新地址(见 redis/sentinel.py);
- slave 客户端通过
rotate_slaves以随机起点 + 循环(round-robin)方式在存活副本间分配读请求,全部副本不可用时回退到 master 地址,仍失败才抛出SlaveNotFoundError(见 redis/sentinel.py)。
SentinelConnectionPool 与 SentinelManagedConnection
SentinelConnectionPool(redis/sentinel.py)继承自ConnectionPool,是 Sentinel 客户端的连接池实现。其关键设计:
- 构造参数:
service_name(服务名,即哨兵监控的主从组名)、sentinel_manager(Sentinel实例)、is_master(池面向 master 还是 slave,默认True)、check_connection(连接建立后是否立即发送PING做健康检查,默认False); - 连接类自动选择:传入
ssl=True时使用SentinelManagedSSLConnection,否则使用SentinelManagedConnection(见 redis/sentinel.py); - 地址解析全部委托给内部的
SentinelConnectionPoolProxy,其中get_master_address每次都会向哨兵重新询问 master 地址并缓存比对,rotate_slaves实现副本轮询(redis/sentinel.py)。
SentinelManagedConnection(redis/sentinel.py)是 Sentinel 专用的连接类:建立连接时按is_master决定连接到 master 或轮询到的 slave;读取响应时若遇到ReadOnlyError,会判断"原 master 已被降级为 slave",主动断开连接以便下次重连时重新向哨兵查询新 master(见 redis/sentinel.py)。这正是 Sentinel 客户端能够透明应对故障转移的底层机制。
Cluster 客户端
RedisCluster客户端用于连接 Redis Cluster(集群分片模式)。与单节点/Sentinel 模式不同,集群的数据按哈希槽(slot)分布在各节点上,客户端需要维护槽位与节点的映射关系,并处理MOVED/ASK重定向。
RedisCluster 与 ClusterNode
from redis.cluster import RedisCluster, ClusterNode # 方式一:给定一个启动节点 rc = RedisCluster(host="localhost", port=7000) # 方式二:显式构造启动节点列表 node = ClusterNode("localhost", 7000) rc = RedisCluster(startup_nodes=[node], require_full_coverage=False) rc.set("foo", "bar") print(rc.get("foo")) # b'bar'ClusterNode(redis/cluster.py)封装单个集群节点的地址信息(host、port 及可选的 server_type 等),startup_nodes是用于初始引导(bootstrapping)的节点列表——客户端会通过这些节点执行CLUSTER SLOTS获取完整的槽位拓扑。RedisCluster.__init__(redis/cluster.py)还支持以下常用参数:
| 参数 | 默认值 | 说明 |
|---|---|---|
startup_nodes/host/port | None/localhost/6379 | 引导节点,二选一即可 |
require_full_coverage | True | 为True时要求所有槽位均被覆盖,否则构造客户端即抛出RedisClusterException;为False时允许部分槽位缺失(但服务端若开启cluster-require-full-coverage yes,相关命令仍可能报ClusterDownError) |
reinitialize_steps | 5 | 槽位拓扑重新初始化的触发阈值(按命令执行次数计) |
read_from_replicas | False(已弃用) | 旧版"从副本读"开关,建议改用load_balancing_strategy |
load_balancing_strategy | None | 从副本读的负载均衡策略(如轮询),数据为最终一致 |
dynamic_startup_nodes | True | 为True时把发现到的全部节点作为下次拓扑刷新的依据 |
address_remap | None | 地址重映射回调,用于 NAT/端口映射场景,将CLUSTER SLOTS返回的地址转换为可访问地址 |
retry | 默认重试对象 | 集群错误(如MOVED/ASK)的重试策略 |
集群客户端同样支持RedisCluster.from_url(redis/cluster.py),URL 语法与通用客户端一致。
集群 Pipeline
在集群上使用 Pipeline 时,由于命令可能路由到不同节点,客户端会将命令按槽位分组发送。同步实现为redis.cluster.ClusterPipeline(redis/cluster.py),核心方法为execute_command与execute:
from redis.cluster import RedisCluster rc = RedisCluster(host="localhost", port=7000) with rc.pipeline(transaction=False) as pipe: pipe.set("foo", "bar") pipe.get("foo") result = pipe.execute()异步客户端(asyncio)
redis-py 从 4.2 版本起提供了完整的 asyncio 支持,接口与同步版一一对应。异步通用客户端为redis.asyncio.client.Redis(redis/asyncio/client.py),用法:
import asyncio import redis.asyncio as redis async def main(): # 直接构造 r = redis.Redis(host="localhost", port=6379, decode_responses=True) # 或使用 URL r = redis.from_url("redis://localhost:6379/0") await r.set("foo", "bar") print(await r.get("foo")) # 'bar' # 使用完毕后关闭(内部连接池随之释放) await r.aclose() asyncio.run(main())异步客户端的所有命令方法均返回可等待对象,必须用await调用;连接池、重试、SSL、Sentinel 等参数语义与同步版完全一致。异步版Redis.from_url的实现同样构建一个ConnectionPool并设置auto_close_connection_pool(见 redis/asyncio/client.py 起的类方法),因此需要记住在结束时显式关闭客户端,或在async with上下文管理器中自动释放。
完整的异步示例可参考仓库中的 docs/examples/asyncio_examples.ipynb。
异步 Cluster 客户端
异步集群客户端redis.asyncio.cluster.RedisCluster(redis/asyncio/cluster.py)与同步版接口对齐:
import asyncio from redis.asyncio.cluster import RedisCluster, ClusterNode async def main(): rc = RedisCluster(host="localhost", port=7000) # 或 rc = RedisCluster(startup_nodes=[ClusterNode("localhost", 7000)]) await rc.set("foo", "bar") print(await rc.get("foo")) await rc.aclose() asyncio.run(main())异步集群体系包含三个核心类(均位于 redis/asyncio/cluster.py):
RedisCluster(L223):异步集群客户端,负责拓扑发现、槽位路由与命令分发;ClusterNode(L1631):异步版节点描述;ClusterPipeline(L2464):异步集群 Pipeline,核心公开方法同样为execute_command与execute:
async def pipeline_demo(): rc = RedisCluster(host="localhost", port=7000) async with rc.pipeline(transaction=False) as pipe: pipe.set("k1", "v1") pipe.get("k1") results = await pipe.execute() await rc.aclose()Connection 与 Connection Pool:连接体系的底层基石
Connection 类
redis.connection.Connection(redis/connection.py)负责与 Redis 服务器之间的 TCP 通信,是所有上层客户端的地基。其构造参数包含host(默认localhost)、port(默认6379)、socket_keepalive(默认开启 TCP keepalive,并支持通过socket_keepalive_options定制如{socket.TCP_KEEPIDLE: 30}等选项)等。底层_connect方法(redis/connection.py)模仿socket.create_connection的行为:
- 通过
socket.getaddrinfo解析主机(同时支持 IPv4/IPv6); - 创建套接字后设置
TCP_NODELAY,按需开启SO_KEEPALIVE并应用 keepalive 选项; - 先用
socket_connect_timeout建立连接,连接成功后切换为socket_timeout作为读写超时; - 遍历
getaddrinfo返回的全部地址尝试连接,全部失败才抛出最后的OSError。
redis.connection模块还提供SSLConnection(TLS 连接)与UnixDomainSocketConnection(Unix 域套接字连接)等变体,ConnectionPool会根据配置自动选用。
ConnectionPool 连接池
ConnectionPool(redis/connection.py)实现了连接的创建、复用与上限控制,避免每条命令都新建 TCP 连接:
from redis.connection import ConnectionPool pool = ConnectionPool( host="localhost", port=6379, max_connections=50, # 池上限,默认 100;超过后抛出 ConnectionError decode_responses=True, ) r = redis.Redis(connection_pool=pool) # 显式共享连接池连接池的关键行为:
max_connections默认值为100,且必须是非负整数,否则抛出ValueError(见 redis/connection.py);达到上限时get_connection会抛出ConnectionError;connection_class参数决定连接类型:默认Connection(TCP),可换用UnixDomainSocketConnection或SSLConnection;ConnectionPool.from_url与Redis.from_url共享同一套 URL 解析逻辑(三种 scheme、db优先级、查询串类型转换与优先级规则完全一致,见 redis/connection.py);- 支持
disconnect(inuse_connections=...)断开空闲或全部连接,以及get_connection_count()查看当前池内连接统计(redis/connection.py); - 额外支持
maint_notifications_config(仅 RESP3 下的维护通知)、metadata_resolver(客户端缓存命令资格判定)、cache/cache_config(RESP3 客户端缓存)等高级能力。
异步版redis.asyncio.connection.Connection(redis/asyncio/connection.py)与ConnectionPool(redis/asyncio/connection.py)提供相同的功能,区别在于其底层使用 asyncio 的事件循环与异步套接字,读写不阻塞事件循环。
连接池的共享与所有权
综合前面几节可以看到,redis-py 对"谁负责关闭连接池"有明确约定:
Redis.from_url/Redis.from_pool:客户端接管连接池,客户端关闭/GC 时同步关闭池;- 直接
Redis(connection_pool=pool):客户端不接管池,池可被多个客户端安全共享,由调用方管理生命周期(推荐配合ConnectionPool的上下文管理器使用); Sentinel的master_for/slave_for:每个返回的客户端拥有独立的SentinelConnectionPool,因此应长期持有并使用同一个客户端实例,避免频繁创建导致池泛滥。
小结与延伸阅读
选择哪种连接客户端,取决于你的 Redis 部署形态:
- 单节点或简单主从:
redis.Redis通用客户端即可; - 哨兵高可用:使用
Sentinel+master_for/slave_for,获得自动故障转移与读写分离; - Cluster 集群:使用
RedisCluster,客户端负责槽位路由与拓扑刷新; - 高并发 IO 密集场景:优先选用
redis.asyncio异步版本; - 需要精细控制连接生命周期:直接操作
ConnectionPool,并遵循上述所有权约定。
官方文档 docs/connections.rst 中通过autoclass指令自动生成了上述全部类的成员级 API 参考,可作为精确查参手册;仓库内的 docs/examples/connection_examples.ipynb 与 docs/examples/asyncio_examples.ipynb 提供了可运行的全量示例,配合 tests/test_connect.py、tests/test_connection.py、tests/test_sentinel.py 等测试用例,可以进一步验证各连接方式在实际环境中的行为。此外,docs/connections.rst 提到的 Sentinel 高可用机制可结合 docs/geographic_failover.rst 与 docs/retry.rst(重试策略)一起阅读,构建完整的生产级连接方案。
【免费下载链接】redis-pyRedis Python client项目地址: https://gitcode.com/GitHub_Trending/re/redis-py
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考