redis服务端必须配置timeout 600和tcp-keepalive 60,前者将空闲断连阈值设为10分钟,后者启用每60秒一次的tcp心跳探测,二者需写入redis.conf并重启生效,缺一则pub/sub连接易无声中断。

Redis服务端必须调的两个timeout配置
空闲连接无声断开,不是客户端的问题,而是服务端默认配置太激进。Redis默认timeout 300(5分钟),而Pub/Sub连接不发任何命令,服务器会在无人响应时直接关闭socket——此时客户端还“以为连着”,后续消息就丢了。
必须在redis.conf里显式设置:
-
timeout 600:把空闲断连阈值拉到10分钟,给客户端重连留出缓冲窗口 -
tcp-keepalive 60:启用内核级TCP心跳,每60秒发一次ACK探测,能穿透NAT、防火墙等中间设备
注意:tcp-keepalive 0表示禁用,不能省略;这两个配置必须写入配置文件并重启,CONFIG SET临时生效但重启即丢。
Lettuce客户端keepalive与autoReconnect要配对启用
Spring Boot默认用Lettuce,但只开autoReconnect=true远远不够。它不会主动探测连接是否存活,重连往往要等到下一次PUBLISH失败才触发,延迟可能长达数分钟。
正确做法是同时启用心跳和重连策略:
- 启用客户端心跳:
spring.redis.lettuce.keep-alive=true(Spring Boot 2.3+) - 显式配置重连行为:
ClientOptions.builder().autoReconnect(true).disconnectedBehavior(ClientOptions.DisconnectedBehavior.RECONNECT_AND_QUEUE_COMMANDS)
关键点:心跳必须跑在**订阅连接上**,不是主连接池里的任意连接。Lettuce默认为每个订阅单独建连接,所以心跳需作用于StatefulRedisPubSubConnection实例。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
Jedis用户必须手动PING,且不能走连接池
Jedis没有内置Pub/Sub心跳机制,依赖timeout参数的被动检测,极易被服务端timeout踢掉。
解决方案是在订阅线程里定期执行PING:
- 不能用
JedisPool获取的连接去PING——那是主命令连接,和订阅连接无关 - 必须用
Jedis.subscribe()返回的Jedis实例(即实际持有SUBSCRIBE状态的那个) - 推荐间隔30秒执行一次
jedis.ping(),略小于服务端timeout值的一半
否则你看到的现象是:日志安静,INFO clients里connected_clients没变,但client_longest_output_list持续>1000——说明消息在Redis内存里堆积,订阅者已卡死。
DNS缓存和云环境VIP漂移会绕过所有保活机制
即使服务端、客户端心跳全开,如果Redis地址是域名(如redis-prod.example.com),JVM默认DNS缓存TTL是-1(永久),可能导致客户端连到已下线的IP。
必须同步处理底层网络不确定性:
- JVM启动加参数:
-Dsun.net.inetaddr.ttl=60,强制DNS解析结果最多缓存60秒 - Redisson用户检查
dnsMonitoringInterval,默认5000ms,建议设为3000 - 阿里云/腾讯云等托管Redis服务存在VIP漂移,仅靠TCP keepalive不够,需配合客户端主动健康检查(例如定时
PING+CLIENT LIST比对连接状态)
最易忽略的是:这些配置分散在服务端、客户端SDK、JVM、云平台四层,漏掉任何一层,保活机制就形同虚设。










