Lettuce Redis客户端集群拓扑刷新配置不生效问题排查
关于Lettuce 6.2.0连接Redis 6.2集群拓扑刷新异常的问题
问题场景
使用Lettuce 6.2.0客户端连接Redis 6.2集群(3主1从架构),期望主节点宕机后客户端能自动重新发现集群拓扑,客户端代码如下:
List<RedisURI> redisURIs = new ArrayList<>(); redisURIs.add(RedisURI.create("redis://127.0.0.1:7000")); redisURIs.add(RedisURI.create("redis://127.0.0.1:7001")); redisURIs.add(RedisURI.create("redis://127.0.0.1:7002")); redisURIs.add(RedisURI.create("redis://127.0.0.1:7003")); redisURIs.add(RedisURI.create("redis://127.0.0.1:7004")); redisURIs.add(RedisURI.create("redis://127.0.0.1:7005")); ClusterTopologyRefreshOptions topologyRefreshOptions = ClusterTopologyRefreshOptions.builder() .enableAllAdaptiveRefreshTriggers() .refreshTriggersReconnectAttempts(1) .enablePeriodicRefresh(Duration.ofSeconds(5)) .build(); ClusterClientOptions clientOptions = ClusterClientOptions.builder() .autoReconnect(true).topologyRefreshOptions(topologyRefreshOptions).build(); ClientResources clientResources = ClientResources.builder().reconnectDelay(Delay.equalJitter()).build(); RedisClusterClient clusterClient = RedisClusterClient.create(clientResources, redisURIs); clusterClient.setOptions(clientOptions);
问题现象
尽管配置了enablePeriodicRefresh(Duration.ofSeconds(5)),实际拓扑刷新间隔仍为60秒。主节点宕机后的1分钟内,客户端无法执行incr操作,重复出现以下错误:
Jul 18, 2022 5:56:21 PM io.lettuce.core.protocol.ConnectionWatchdog lambda$run$4 WARNING: Cannot reconnect to [127.0.0.1:7000]: Connection refused: /127.0.0.1:7000
1分钟超时后出现以下警告,客户端才恢复执行命令:
Jul 18, 2022 5:56:22 PM io.lettuce.core.cluster.topology.DefaultClusterTopologyRefresh lambda$openConnections$12 WARNING: Unable to connect to [127.0.0.1:7000]: Connection refused: /127.0.0.1:7000 Command timed out after 1 minute(s)
此后还会持续出现如下警告:
Jul 18, 2022 5:56:27 PM io.lettuce.core.cluster.topology.DefaultClusterTopologyRefresh lambda$openConnections$12 WARNING: Unable to connect to [127.0.0.1:7000]: Connection refused: /127.0.0.1:7000
请问遗漏了什么配置?
问题分析与解决
核心原因
Lettuce的定期拓扑刷新默认存在最小间隔限制(默认60秒),你设置的5秒小于该最小值,因此配置被忽略,实际仍使用默认的60秒间隔。需要显式配置periodicRefreshTimeout来覆盖默认的最小间隔阈值。
修改方案
调整ClusterTopologyRefreshOptions的构建代码,添加periodicRefreshTimeout(Duration.ofSeconds(5)),确保定期刷新间隔生效:
ClusterTopologyRefreshOptions topologyRefreshOptions = ClusterTopologyRefreshOptions.builder() .enableAllAdaptiveRefreshTriggers() .refreshTriggersReconnectAttempts(1) .enablePeriodicRefresh(Duration.ofSeconds(5)) .periodicRefreshTimeout(Duration.ofSeconds(5)) // 新增:覆盖默认最小间隔 .build();
额外优化建议
- 调整命令超时时间:默认命令超时为1分钟,过长的等待会延迟拓扑刷新触发。可以在
ClusterClientOptions中设置更短的超时:
ClusterClientOptions clientOptions = ClusterClientOptions.builder() .autoReconnect(true) .topologyRefreshOptions(topologyRefreshOptions) .commandTimeout(Duration.ofSeconds(10)) // 缩短命令超时时间 .build();
- 确认自适应刷新触发:已开启的
enableAllAdaptiveRefreshTriggers()包含了节点连接失败、MOVED/ASK重定向等触发条件,结合refreshTriggersReconnectAttempts(1),当连接重试1次失败后会立即触发拓扑刷新,该配置无需调整。
修改后的完整配置示例
List<RedisURI> redisURIs = new ArrayList<>(); redisURIs.add(RedisURI.create("redis://127.0.0.1:7000")); redisURIs.add(RedisURI.create("redis://127.0.0.1:7001")); redisURIs.add(RedisURI.create("redis://127.0.0.1:7002")); redisURIs.add(RedisURI.create("redis://127.0.0.1:7003")); redisURIs.add(RedisURI.create("redis://127.0.0.1:7004")); redisURIs.add(RedisURI.create("redis://127.0.0.1:7005")); ClusterTopologyRefreshOptions topologyRefreshOptions = ClusterTopologyRefreshOptions.builder() .enableAllAdaptiveRefreshTriggers() .refreshTriggersReconnectAttempts(1) .enablePeriodicRefresh(Duration.ofSeconds(5)) .periodicRefreshTimeout(Duration.ofSeconds(5)) .build(); ClusterClientOptions clientOptions = ClusterClientOptions.builder() .autoReconnect(true) .topologyRefreshOptions(topologyRefreshOptions) .commandTimeout(Duration.ofSeconds(10)) .build(); ClientResources clientResources = ClientResources.builder().reconnectDelay(Delay.equalJitter()).build(); RedisClusterClient clusterClient = RedisClusterClient.create(clientResources, redisURIs); clusterClient.setOptions(clientOptions);
内容的提问来源于stack exchange,提问作者RRM
相关产品推荐
相关产品推荐

