redis pub/sub客户端无法自动迁移,必须主动重建连接并重订阅;因subscribe连接是无状态长连接,原master退出即断开,需监听+switch-master事件、关闭旧连接、用新地址重订阅。

Redis PUB/SUB 客户端不会、也不能自动迁移,必须由你主动重建连接并重订阅——这是设计使然,不是配置没配对。
为什么 SUBSCRIBE 连接在哨兵切换后必然断开
Redis 的 PUBSUB 连接是纯内存、无状态的长连接,不走复制流,也不写 AOF/RDB。一旦原 Master 进程退出(哪怕只是被哨兵踢下线),该 TCP 连接立即失效,所有已注册的频道监听全部丢失。
- 常见现象:客户端日志静默,
redis-cli -h 新主地址 SUBSCRIBE channel能收到消息,但业务代码收不到 - 根本原因不是“连不上”,而是旧连接还挂着、没 close,新消息发到新主,老连接却卡在半关闭状态
-
__sentinel__:hello频道广播的是哨兵间通信,不是给业务客户端用的;它不带新主地址,只含发送者 IP 和 master-name
客户端必须实现的三步动作
哨兵只负责通知角色变更,不负责维持你的订阅上下文。要恢复 pub/sub 流,必须手动完成以下三件事:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 监听
+switch-master事件:通过SENTINEL subscribe __sentinel__:hello或轮询SENTINEL get-master-addr-by-name mymaster捕获切换信号 - 主动关闭旧连接:不能依赖 TCP 自动断连,需在检测到
read: connection reset by peer或心跳超时后显式调用close() - 用新地址重建连接并重订阅:必须重新执行
SUBSCRIBE channel_a channel_b,顺序和频道名一个都不能少;若用PSUBSCRIBE,也要一并重订
Jedis 和 Lettuce 的典型陷阱
不同客户端库对哨兵的支持程度差异很大,但都默认不接管 pub/sub 生命周期:
-
JedisSentinelPool只管命令连接池,不管PUBSUB连接;即使你拿到新主地址,Jedis实例仍可能复用旧 socket,必须新建Jedis实例并重新subscribe() -
Lettuce的StatefulRedisMasterSlaveConnection是长生命周期对象,缓存了已关闭连接;URI 必须用redis-sentinel://格式,且每次重连后要调用RedisClient.connect(new RedisURI()),不能只改setUri() - 别信“自动重连”:Jedis 的
returnResource()、Lettuce 的shutdown()都不等于清理 pub/sub 上下文;漏掉这步,下次listen()会卡死
容易被忽略的黑洞期与兜底策略
腾讯云实测显示,主从切换平均带来 2–3 秒消息黑洞期。这不是 bug,而是 PUB/SUB 的固有局限——切换瞬间旧主停服、新主尚未接受连接、DNS 缓存未刷新、TCP TIME_WAIT 未释放,全都会叠加延迟。
- 不要依赖单次
get-master-addr-by-name结果:哨兵返回可能有延迟或抖动,建议加本地缓存 + 时间戳,拒绝过期响应 - 重订阅后别直接消费:若业务强依赖顺序,应用层需加序列号或时间戳,并容忍少量重复(重连窗口内可能漏收)
- 最简兜底:每 5 秒 ping 一次连接,失败则强制重建连接池 + 全量重订阅,比等事件更可靠










