Redis发布订阅长连接问题本质是空闲连接无人管理导致无声断开,需服务端设timeout≥600秒和tcp-keepalive=60,客户端启用心跳与自动重连,并注意DNS缓存及云环境VIP漂移。
Redis发布订阅的长连接问题,本质不是“连接太长”,而是“连接空闲时没人管”——默认配置下,客户端和服务器都可能在无声中断开,导致订阅失效却不报警。
为什么空闲超时设置不当会导致订阅中断
Redis服务器端的
参数控制空闲连接自动关闭时间,默认是 300 秒(5 分钟)。一旦客户端订阅后没发任何命令(Pub/Sub 不会主动发心跳),服务器就会单方面断开 socket。此时客户端往往还“以为连着”,后续
或重连逻辑可能失败,但错误不明显。
常见现象包括:
订阅端突然收不到新消息,日志无报错
连接数缓慢上涨(旧连接未释放,新连接不断建)
中
稳定,但
持续 > 1000 —— 表明某订阅者已卡死,消息在 Redis 内存里堆积
服务端必须改的两个 config 参数
仅调客户端不够,Redis 服务端要同步开启保活机制:
:把空闲断连阈值拉到 10 分钟,给客户端留出重连窗口
:启用内核级 TCP 心跳,每 60 秒发一次
探测,能更快发现网络中间设备(如 NAT、防火墙)静默丢连接
这两个命令需写入
持久化,否则重启失效。注意:
表示禁用,不能省略设置。
客户端 keepalive 和 autoReconnect 要配对启用
以 Lettuce(Spring Boot 默认)为例,光设
不够,必须配合心跳检测,否则重连触发延迟高:
启用客户端心跳:
(Spring Boot 2.3+)
显式配置重连策略:
Jedis 用户需手动加
:在订阅线程里定期执行
,避免被服务端
清退
关键点:心跳必须发生在订阅连接上,不是主连接池里的任意连接。
别忽略 DNS 缓存和 JVM 层面的隐性超时
如果 Redis 地址是域名(如
),DNS 解析结果可能长期缓存,导致客户端连到已下线的 IP:
启动 JVM 时加参数:
,强制 DNS TTL 为 60 秒
Redisson 用户还要检查
,默认 5000ms,建议保持或微调至 3000ms
某些云 Redis 服务(如阿里云)会动态漂移 VIP,纯靠
+
不足以覆盖这种场景
真正稳定的长连接,从来不是靠“不断开”,而是靠“快速发现断开 + 快速重建 + 快速恢复订阅”。空闲超时只是第一道探测开关,漏掉它,后面所有重连逻辑都可能晚几秒甚至几十秒才启动。
timeoutPUBLISHINFO clientsconnected_clientsclient_longest_output_listconfig set timeout 600config set tcp-keepalive 60ACKredis.conftcp-keepalive 0autoReconnect=truespring.redis.lettuce.keep-alive=trueClientOptions.builder().autoReconnect(true).disconnectedBehavior(ClientOptions.DisconnectedBehavior.RECONNECT_AND_QUEUE_COMMANDS)PINGjedis.ping()timeoutredis-prod.example.com-Dsun.net.inetaddr.ttl=60dnsMonitoringIntervaltimeouttcp-keepalive