Spring Boot 3默认使用Lettuce,需同时启用pingBeforeActivateConnection和ClusterTopologyRefreshOptions保活连接,且配置前缀已更新为spring.data.redis.*,旧配置如spring.redis.host等已废弃。

Spring Boot 3 默认用 Lettuce,而 Lettuce 不会自动发 PING 或启用 TCP keepalive,空闲连接大概率在 60–300 秒内被 NAT、SLB 或防火墙静默断开——这不是 Redis 挂了,而是连接早被掐了,下次取连接时直接抛 RedisCommandTimeoutException: Command timed out after 5 second(s)。
为什么 spring.redis.lettuce.pool.test-on-borrow=true 没用
这个配置只控制连接从池中取出时是否调用 validateConnection(),但 Lettuce 的默认连接工厂压根不实现该验证逻辑。光配它等于没配,连接照样悄无声息地失效。
-
test-on-borrow依赖底层连接工厂主动调用ping(),Lettuce 默认跳过这步 - 哪怕配了
ping-before-activate-connection=true,若没启用拓扑刷新,节点变更后仍连不上 - Netty 连接默认不开启 OS 级 TCP keepalive,得手动打开或靠应用层心跳兜底
必须启用 pingBeforeActivateConnection + 拓扑刷新
保活的核心不是“检测”,而是“主动触发连接活跃状态”。Lettuce 提供了两个关键开关,必须同时启用:
- 在自定义
LettuceConnectionFactory中设置ClientOptions.builder().pingBeforeActivateConnection(true):每次从连接池取出连接前,先发一次PING命令,失败则丢弃并重建 - 集群/哨兵模式下必须配
ClusterTopologyRefreshOptions,否则 IP 变更(如云上弹性伸缩)后连接永远无法恢复
示例代码片段:
ClusterTopologyRefreshOptions topologyRefreshOptions = ClusterTopologyRefreshOptions.builder()
.enablePeriodicRefresh(Duration.ofSeconds(30))
.enableAllAdaptiveRefreshTriggers()
.build();
ClientOptions clientOptions = ClientOptions.builder()
.topologyRefreshOptions(topologyRefreshOptions)
.pingBeforeActivateConnection(true)
.autoReconnect(true)
.build();
application.yml 里哪些配置已废弃或易踩坑
Spring Boot 3 要求使用 spring.data.redis.* 前缀,旧的 spring.redis.* 在多数场景下已不生效,尤其涉及 Lettuce 高级行为时。
-
spring.redis.host已废弃 → 改用spring.data.redis.host -
spring.redis.timeout不再控制底层连接超时 → 应设spring.data.redis.connect-timeout(单位毫秒) -
lettuce.pool.max-active实际受max-idle和 Netty event loop 线程数制约,盲目调高反而引发资源争抢 - 未显式配置
spring.data.redis.lettuce.shutdown-timeout时,应用关闭可能卡住,建议设为100ms
要不要开 TCP keepalive?怎么开
可以开,但不是必须——Lettuce 的 pingBeforeActivateConnection 已覆盖主要保活需求。如果网络中间设备特别激进(比如某些云厂商 SLB 强制 60 秒断连),可叠加 OS 级 keepalive。
- 通过
ClientResources.builder().dnsResolver(...)注入自定义DnsResolver时,可同步配置EventLoopGroup的ChannelOption.SO_KEEPALIVE - 更简单的方式:在 JVM 启动参数加
-Dsun.net.client.defaultConnectTimeout=5000 -Dsun.net.client.defaultReadTimeout=5000(仅影响部分底层行为,非核心) - 真正起效的是 Netty 层配置,需在构建
ClientResources时传入SocketAddress并指定ChannelOption
复杂点在于:keepalive 周期由 OS 决定(Linux 默认 7200 秒),你没法在应用层精确控制;而 Lettuce 的 PING 是应用层可控的,频率、超时、重试都可编程——所以优先信 pingBeforeActivateConnection,keepalive 当作冗余兜底即可。











