Java中用TestContainers搭建Redis集群遇JedisCluster超时问题求助
核心问题分析
你遇到的Cluster retry deadline exceeded和redis-cli重定向卡住问题,本质是Redis集群节点向客户端通告的是容器内部的IP与端口,而运行在宿主机的Jedis/redis-cli只能访问宿主机映射后的端口,无法直接连接容器内部地址,导致重定向指令无法执行,最终触发超时或卡住。
一、单节点集群修复方案
1. 调整容器启动命令,添加集群通告配置
启动容器后获取宿主机映射端口,重新配置Redis节点的通告地址,让节点向客户端暴露宿主机可访问的端口:
int basePort = 6379; String redisPassword = "your-password"; Network network = Network.newNetwork(); RedisContainer redisContainer = new RedisContainer(DockerImageName.parse("redis:7.0.5")) .withExposedPorts(basePort) .withNetwork(network) .waitingFor(Wait.forListeningPort()); // 先启动容器获取映射端口 redisContainer.start(); int mappedPort = redisContainer.getMappedPort(basePort); // 更新启动命令,添加集群通告地址 redisContainer.withCommand( "redis-server --port " + basePort + " --requirepass " + redisPassword + " --masterauth " + redisPassword + " --cluster-enabled yes" + " --cluster-config-file nodes.conf" + " --cluster-node-timeout 5000" + " --appendonly yes" + " --bind 0.0.0.0" + " --cluster-announce-ip 127.0.0.1" + " --cluster-announce-port " + mappedPort ); redisContainer.restart(); // 执行槽位分配 redisContainer.execInContainer( "redis-cli", "--no-auth-warning", "-h", "localhost", "-p", String.valueOf(basePort), "-a", redisPassword, "cluster", "addslotsrange", "0", "16383" );
2. JedisCluster连接验证
你的现有Jedis连接代码无需修改,因为已经使用了宿主机映射端口,和节点通告的端口一致,重新运行即可正常读写。
二、多节点集群修复方案
1. 启动并配置所有节点
先启动所有容器获取映射端口,再为每个节点添加集群通告配置,最后执行集群初始化命令:
int nodeCount = 6; int basePort = 6379; String redisPassword = "your-password"; Network network = Network.newNetwork(); List<RedisContainer> redisContainers = new ArrayList<>(); Set<HostAndPort> hostAndPorts = new HashSet<>(); // 第一步:启动所有节点(暂不配置集群通告) for (int i = 0; i < nodeCount; i++) { int port = basePort + i; RedisContainer container = new RedisContainer(DockerImageName.parse("redis:7.0.5")) .withExposedPorts(port) .withCommand( "redis-server --port " + port + " --requirepass " + redisPassword + " --masterauth " + redisPassword + " --cluster-enabled yes" + " --cluster-config-file nodes.conf" + " --cluster-node-timeout 5000" + " --appendonly yes" + " --bind 0.0.0.0" ) .withNetwork(network) .waitingFor(Wait.forListeningPort()); container.start(); redisContainers.add(container); int mappedPort = container.getMappedPort(port); hostAndPorts.add(new HostAndPort("127.0.0.1", mappedPort)); } // 第二步:更新每个节点的集群通告配置并重启 for (int i = 0; i < redisContainers.size(); i++) { RedisContainer container = redisContainers.get(i); int originalPort = basePort + i; int mappedPort = container.getMappedPort(originalPort); container.withCommand( "redis-server --port " + originalPort + " --requirepass " + redisPassword + " --masterauth " + redisPassword + " --cluster-enabled yes" + " --cluster-config-file nodes.conf" + " --cluster-node-timeout 5000" + " --appendonly yes" + " --bind 0.0.0.0" + " --cluster-announce-ip 127.0.0.1" + " --cluster-announce-port " + mappedPort ); container.restart(); } // 第三步:执行集群初始化命令(在容器内执行,利用容器网络访问内部端口) StringBuilder clusterCreateCmd = new StringBuilder("redis-cli --no-auth-warning -a " + redisPassword + " --cluster create "); for (HostAndPort hp : hostAndPorts) { clusterCreateCmd.append(hp.getHost()).append(":").append(hp.getPort()).append(" "); } clusterCreateCmd.append("--cluster-replicas 1 --cluster-yes"); redisContainers.get(0).execInContainer( "/bin/sh", "-c", clusterCreateCmd.toString() );
2. JedisCluster连接验证
保持你现有Jedis连接代码不变,运行后即可正常进行集群读写操作,redis-cli -c也不会再卡在重定向步骤。
额外注意事项
- 确保所有节点的
--masterauth和--requirepass配置完全一致,避免节点间通信认证失败。 - 单节点集群仅适合测试场景,Redis官方不推荐生产环境使用。
- 执行集群初始化时添加
--cluster-yes参数,可跳过交互确认步骤。
内容的提问来源于stack exchange,提问作者djGowda




