示例说明
主从 Redis 的客户端必须理解拓扑会变:主库宕机后哨兵把某个副本提升为新主,客户端要能拿到新地址。master_for 与 slave_for 每次都向哨兵查询当前角色,连接串不再写死。读写分离之外,两条容错路径很关键——副本读失败回退主库保可用性,写失败等待切换窗口后重新解析地址重试。
技术要点
- 角色解析实时询问哨兵集群
- 读副本失败自动回退主库
- 写失败重解析地址越过切换窗口
代码解读
三台哨兵防脑裂,socket_timeout 压短探测延迟;cache_get 先打副本读,连接类异常时降级主库;cache_set 捕获连接错误后小睡 200 毫秒让哨兵完成推举,再重新 master_for 拿新主地址重试写入。current_topology 展示运行期动态发现的拓扑结构。
使用建议
切换窗口内的写入仍可能失败,重要数据要有最终一致性兜底。副本读注意复制延迟对一致性的影响。哨兵与 Redis 节点跨可用区部署。
代码示例
import redis
import time
from redis.sentinel import Sentinel
sentinel = Sentinel(
[('10.0.0.11', 26379), ('10.0.0.12', 26379), ('10.0.0.13', 26379)],
socket_timeout=0.5,
)
master = sentinel.master_for('mymaster', socket_timeout=1.0)
replica = sentinel.slave_for('mymaster', socket_timeout=1.0)
def cache_get(key: str) -> str | None:
try:
return replica.get(key) # 读走副本,减轻主库压力
except (redis.ConnectionError, redis.TimeoutError):
return master.get(key) # 副本不可用时回退主库
def cache_set(key: str, value: str, ttl: int = 60) -> bool:
try:
master.set(key, value, ex=ttl)
return True
except redis.ConnectionError:
# 哨兵可能正在切换,稍等后重新解析主节点地址
time.sleep(0.2)
fresh = sentinel.master_for('mymaster', socket_timeout=1.0)
fresh.set(key, value, ex=ttl)
return True
def current_topology() -> dict:
master_addr = sentinel.discover_master('mymaster')
replicas = sentinel.discover_slaves('mymaster')
return {
'master': f'{master_addr[0]}:{master_addr[1]}',
'replicas': [f'{h}:{p}' for h, p in replicas],
}
print('topology:', current_topology())
cache_set('session:7', 'active', ttl=300)
print('read back:', cache_get('session:7'))