弱网下Pub/Sub连接秒断主因是空闲TCP链路被NAT/云LB/防火墙静默关闭,需服务端tcp-keepalive与timeout、客户端分连接心跳、DNS刷新及节点校验四者协同;单靠重连无法解决。

弱网下Pub/Sub连接为什么秒断不是客户端代码写得烂
根本原因是Redis Pub/Sub连接本身不发心跳,TCP空闲链路在NAT、云LB或防火墙眼里就是“死连接”,60–300秒内被静默掐断——而订阅者还在等消息,既不发命令也不响应,服务端timeout和客户端tcp-keepalive若没对齐,断开就是必然。这不是重连逻辑能兜住的,是连接存活机制缺失。
Lettuce/Redisson/Jedis的心跳配置必须分开通用连接和Pub/Sub连接
通用连接池里的pingBeforeActivateConnection(true)或setHeartbeatInterval()默认只作用于命令连接,StatefulRedisPubSubConnection要单独处理:
- Lettuce:监听
onException后手动调connection.ping(),不能依赖自动探测 - Redisson:必须显式设
setHeartbeatInterval(15000)且setSubscriptionConnectionPoolSize(20),否则单连接排队导致心跳超时 - Jedis:得在
JedisPubSub子类里起独立定时器,每15秒调jedis.ping(),且必须复用同一个Jedis实例,混用连接池会报RedisConnectionClosedException
重连风暴的根源是没做状态隔离和退避控制
多个频道共用一个StatefulRedisPubSubConnection,一个频道消费慢就拖垮整条连接;重连时又不检查connection.isOpen(),直接subscribe()触发RedisConnectionClosedException,形成循环异常。正确做法是:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 每个业务频道用独立连接池,避免互相干扰
- 用
AtomicBoolean connected控制重连状态,禁止并发subscribe - 重连失败后按指数退避重试:
1s → 2s → 4s → 8s,上限卡在30s - 重连前先
INFO replication校验当前节点是否仍是master,防止连到只读副本后PUBLISH失败
别指望纯Pub/Sub扛住弱网,得加一层List兜底
Pub/Sub断连必丢消息,这是协议设计决定的。真要可靠,就得把“通知”和“数据”拆开:
- 发布端严格按顺序执行:
r.lpush('queue:order', data)→r.publish('ch:order', '1') - 订阅端启动时先
lrange queue:order 0 -1补读,再ltrim queue:order 0 -1清空(或lrem按值删) - 务必限制List长度,比如
ltrim queue:order -1000,否则内存持续涨
真正难的不是写重连代码,是让心跳、连接池、DNS刷新、节点校验四件事在弱网下始终同步生效——少一环,重连就变成无效轮询。

















