登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  数据库 >  Redis

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 列表与服务名定位候选主节点并校验角色的结构图
图1:客户端通过 Sentinel 列表和服务名解析当前主节点,再用 ROLE 确认角色;这是静态结构说明图,不是运行截图。

向 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 节点追加到内存列表,以提升后续发现的可用性。这个刷新不必写回静态配置,内存更新已经能带来收益。

Redis Sentinel 故障转移后关闭旧连接池并建立新连接池的边界图
图2:主节点地址变化时应关闭旧池并重建新池;事件通知可加速响应,但不能替代重新解析与角色校验。

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 校验避免采用错误角色。再把断线重解析和连接池整体重建补齐,故障转移后的新主节点才能真正被业务客户端稳定接管。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>