Redis Sentinel 客户端如何发现新的主节点
来源:17golang原创
时间:2026-10-09 13:10:22 343浏览 收藏
Redis Sentinel 客户端发现新主节点的关键,不是等待 Sentinel 把新 IP 自动塞进已有业务连接,而是把多个 Sentinel 地址和一个主节点服务名当作稳定入口。客户端向可用 Sentinel 查询当前主节点地址,连接候选节点后用 ROLE 确认它确实是 master;一旦连接断开,就重新执行这套解析,而不是继续重连旧 IP。
官方文档:https://redis.io/docs/latest/develop/reference/sentinel-clients/
故障转移后真正让业务连接切到新主节点的,是客户端的“重新发现 + 角色校验 + 连接池重建”。Pub/Sub 事件可以让切换更快,但不能代替主动查询。
问题现场:旧主节点下线了,客户端为什么还在报错
常见现场是:Sentinel 已经把一个副本提升为 master,运维侧看起来故障转移完成,但部分应用仍向旧地址写入,出现连接断开、只读错误或重试抖动。初看像 Sentinel 切换慢,实际经常是客户端只配置了固定 Redis 地址,或者连接池断线后仍按旧地址创建连接。
Sentinel 同时承担监控、故障转移协调和服务发现。它能告诉客户端“某个服务名现在对应哪个 master”,但业务客户端必须显式支持 Sentinel 协议。换句话说,Sentinel 负责维护答案,客户端负责在合适时机重新提问并替换连接。
发现入口不是主节点 IP,而是 Sentinel 列表加服务名
可靠配置由两部分组成:一组已知 Sentinel 的 ip:port,以及 Sentinel 配置中监控主节点时使用的服务名,例如 cache-main。主节点 IP 会在故障转移后变化,服务名则是客户端长期持有的逻辑标识。
redis:
sentinel:
# 配置多个 Sentinel,避免单个发现入口不可用
nodes:
- "10.0.1.11:26379"
- "10.0.1.12:26379"
- "10.0.1.13:26379"
# 这里是 Sentinel 监控的服务名,不是主机名
master_name: "cache-main"
# 发现连接与业务命令应分别设置合理超时
connect_timeout_ms: 300
客户端应依次尝试 Sentinel,使用较短的连接超时;第一个成功响应的 Sentinel 可被移到内存列表前部,让下一次重连优先访问近期可用节点。如果所有 Sentinel 都无法连接,应明确报出“Sentinel 不可达”,而不是模糊地包装成普通 Redis 超时。

向 Sentinel 查询当前主节点地址
连接到一个 Sentinel 后,客户端发送 SENTINEL get-master-addr-by-name,参数就是服务名。返回值通常是 IP 与端口;如果返回空值,表示这个 Sentinel 不认识该服务名,客户端应继续询问列表中的下一个 Sentinel。
# 向 Sentinel 查询 cache-main 当前对应的主节点地址 redis-cli -h 10.0.1.11 -p 26379 \ SENTINEL get-master-addr-by-name cache-main # 仅用于观察 Sentinel 当前记录,不应把返回地址重新写死到配置中
这里需要区分“发现失败”的两种原因:所有 Sentinel 都连不上,说明发现入口不可用;Sentinel 都能响应但均返回空值,则通常是服务名写错、监控配置未建立,或连接到了不属于这套部署的 Sentinel。把两类错误分开,排障会快很多。
候选地址还要通过 ROLE 校验
拿到地址并不等于已经拿到可信的新主节点。故障转移传播期间,不同 Sentinel 的信息可能短暂不一致,因此客户端应连接候选 Redis 节点并调用 ROLE。只有角色确实为 master,才把连接交给业务层。
# 连接候选 Redis 节点,确认它当前承担 master 角色 redis-cli -h 10.0.2.21 -p 6379 ROLE # 如果角色不是 master,等待一个很短的退避时间后重新询问 Sentinel
如果角色不符合预期,正确动作不是盲目重试这个候选地址,而是短暂退避后回到 Sentinel 列表重新解析。这个校验能挡住一部分尚未更新信息的 Sentinel,减少应用误连旧主节点或副本的概率。
断线后必须重新解析,而不是只重连原地址
客户端首次启动时发现一次主节点还不够。只要底层连接因为超时、套接字错误、用户主动关闭或其他原因需要重建,就应再次询问 Sentinel。官方规范还说明,Sentinel 在重新配置实例时会主动断开普通客户端连接,促使支持 Sentinel 的客户端重新发现地址。
因此,连接恢复代码应以“服务名”作为输入,而不是以最近一次主节点地址作为永久目标。最近地址可以做短期缓存,但断线之后必须失效;否则客户端虽然有重试机制,却会一次次重连已经降级为副本的旧节点。
func reconnect(serviceName string, sentinels []string) (net.Conn, error) {
// 每次恢复都重新解析服务名,避免沿用故障转移前的旧地址
addr, err := discoverMaster(serviceName, sentinels)
if err != nil {
return nil, fmt.Errorf("重新发现 Redis 主节点失败: %w", err)
}
// 连接后还要校验 ROLE,角色不匹配时回到 Sentinel 重新查询
conn, err := dialAndVerifyMaster(addr)
if err != nil {
return nil, fmt.Errorf("候选节点角色校验失败: %w", err)
}
return conn, nil
}
主节点变化后,连接池必须整体换地址
单连接客户端只需关闭旧连接并创建新连接;连接池则更容易留下隐患。只替换当前报错的那一条连接,会让池里其他空闲连接继续指向旧地址。客户端一旦发现解析出的主节点地址发生变化,应关闭旧池中的全部连接,并以新地址建立新的池。
客户端还可以在成功查询主节点后调用 SENTINEL sentinels ,把新发现的 Sentinel 节点追加到内存列表,以提升后续发现的可用性。这个刷新不必写回静态配置,内存更新已经能带来收益。

Pub/Sub 事件可以加速,但不能当成唯一依据
客户端可以订阅 Sentinel 发布的配置变化事件,在收到切换信号后立即触发重新发现。这比等待下一次命令失败更灵敏,但事件消息可能因为断线等原因丢失,因此不能直接把事件携带的信息当成最终连接配置。
更稳妥的做法是:事件只负责“唤醒”重配置逻辑,真正的地址仍通过 Sentinel 查询获得,并继续执行 ROLE 校验。即使没有收到事件,业务连接断开时也会触发同一套发现流程。
如何验证客户端真的切到了新主节点
测试时不要只看 Sentinel 是否完成选主,还要同时观察客户端侧状态。建议在测试环境发起受控故障转移,记录切换前后的主节点地址、连接池重建次数和恢复耗时,并确认旧池已关闭。
# 在测试环境触发指定服务的受控故障转移 redis-cli -h 10.0.1.11 -p 26379 \ SENTINEL failover cache-main # 重新查询服务名,确认 Sentinel 返回的新地址 redis-cli -h 10.0.1.12 -p 26379 \ SENTINEL get-master-addr-by-name cache-main # 再检查新地址的 ROLE;不要只依据 Sentinel 返回值判断成功 redis-cli -h 10.0.2.22 -p 6379 ROLE
| 观察点 | 正确表现 | 异常信号 |
|---|---|---|
| 发现入口 | 多个 Sentinel + 服务名 | 只保存固定 Redis IP |
| 候选节点 | ROLE 返回 master | 拿到地址就立即用于写入 |
| 断线恢复 | 重新解析服务名 | 循环重连旧地址 |
| 连接池 | 地址变化时整体重建 | 池内混有新旧节点连接 |
| 事件订阅 | 只用于加速重新查询 | 把事件当成唯一配置来源 |
常见问题
客户端只配置一个 Sentinel 可以吗?
功能上可能工作,但这个 Sentinel 不可达时客户端就失去发现入口。生产环境应配置多个 Sentinel 地址,并设置较短的单节点连接超时。
为什么 Sentinel 已选出新主节点,应用仍有短暂错误?
故障检测、选主、配置传播、旧连接断开和客户端重建连接池都需要时间。应用还应配置有界重试与退避,避免切换窗口内形成重试风暴。
能否一直缓存 Sentinel 返回的主节点地址?
可以在连接存活期间使用,但断线重连时必须重新解析。把地址当作永久配置,会绕过 Sentinel 服务发现。
读副本也能使用相同机制吗?
可以。客户端可通过 SENTINEL replicas 获取副本列表,并用 ROLE 确认候选节点确实是 replica。
归根结底,Sentinel 客户端的可靠性来自三层约束:服务名避免写死主节点地址,多 Sentinel 避免单一发现入口,ROLE 校验避免采用错误角色。再把断线重解析和连接池整体重建补齐,故障转移后的新主节点才能真正被业务客户端稳定接管。
-
117 收藏
-
426 收藏
-
171 收藏
-
113 收藏
-
195 收藏
-
468 收藏
-
461 收藏
-
320 收藏
-
154 收藏
-
462 收藏
-
484 收藏
-
410 收藏
-
397 收藏
-
178 收藏
-
309 收藏
-
105 收藏
-
397 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习