window上通过springboot访问docker部署的建立在桥接网络上的redis-cluster集群上出现的访问超时问题的分析与解决总结

window上通过springboot访问docker部署的建立在桥接网络上的redis-cluster集群上出现的访问超时问题的分析与解决总结

情景说明:

springboot想与docker部署的六个(3主3从)的redis集群cluster建立通信。
但是redis内部可以成功建立集群,且springboot单独与单机redis也可以建立通讯,但是一旦涉及到redis集群,就会失败。现在调查原因:

建立docker网络:

笔者首先通过docker网络建立了一个专门用来测试redis集群的桥接网络:

docker network create --driver bridge --subnet 172.173.0.0/24 docker-bridge-reidsCluster-only

建立redis配置文件:

随后通过sh脚本建立6个节点的配置文件:

for port in $(seq 6379 6384); 
do 
mkdir -p "E:\DockerWsl\redis\redis-cluster-${port}\conf"
mkdir -p "E:\DockerWsl\redis\redis-cluster-${port}\data"
touch "E:\DockerWsl\redis\redis-cluster-${port}\conf\redis.conf"
cat  << EOF > "E:\DockerWsl\redis\redis-cluster-${port}\conf\redis.conf"
port ${port}
requirepass password
bind 0.0.0.0
protected-mode no
daemonize no
appendonly yes
masterauth password
repl-diskless-load on-empty-db
cluster-enabled yes 
cluster-config-file nodes.conf
cluster-node-timeout 5000
EOF
done

运行redis容器:

接下来再通过bat脚本运行6个redis节点的容器:

docker run -d -p 6379:6379 -p 16379:16379 -v /E/DockerWsl/redis/redis-cluster-6379/conf/redis.conf:/etc/redis/redis.conf -v /E/DockerWsl/redis/redis-cluster-6379/data:/data/ --name redis-cluster-6379 --net docker-bridge-reidsCluster-only --ip 172.173.0.2 09497 redis-server /etc/redis/redis.conf

docker run -d -p 6380:6380 -p 16380:16380 -v /E/DockerWsl/redis/redis-cluster-6380/conf/redis.conf:/etc/redis/redis.conf -v /E/DockerWsl/redis/redis-cluster-6380/data:/data/ --name redis-cluster-6380 --net docker-bridge-reidsCluster-only --ip 172.173.0.3 09497 redis-server /etc/redis/redis.conf

docker run -d -p 6381:6381 -p 16381:16381 -v /E/DockerWsl/redis/redis-cluster-6381/conf/redis.conf:/etc/redis/redis.conf -v /E/DockerWsl/redis/redis-cluster-6381/data:/data/ --name redis-cluster-6381 --net docker-bridge-reidsCluster-only --ip 172.173.0.4 09497 redis-server /etc/redis/redis.conf

docker run -d -p 6382:6382 -p 16382:16382 -v /E/DockerWsl/redis/redis-cluster-6382/conf/redis.conf:/etc/redis/redis.conf -v /E/DockerWsl/redis/redis-cluster-6382/data:/data/ --name redis-cluster-6382 --net docker-bridge-reidsCluster-only --ip 172.173.0.5 09497 redis-server /etc/redis/redis.conf

docker run -d -p 6383:6383 -p 16383:16383 -v /E/DockerWsl/redis/redis-cluster-6383/conf/redis.conf:/etc/redis/redis.conf -v /E/DockerWsl/redis/redis-cluster-6383/data:/data/ --name redis-cluster-6383 --net docker-bridge-reidsCluster-only --ip 172.173.0.6 09497 redis-server /etc/redis/redis.conf

docker run -d -p 6384:6384 -p 16384:16384 -v /E/DockerWsl/redis/redis-cluster-6384/conf/redis.conf:/etc/redis/redis.conf -v /E/DockerWsl/redis/redis-cluster-6384/data:/data/ --name redis-cluster-6384 --net docker-bridge-reidsCluster-only --ip 172.173.0.7 09497 redis-server /etc/redis/redis.conf

其中暴露了6379 和 16379两个端口,一个是用来暴露业务,一个是用来总线通信。
09497这个数字是一个id。表示该容器要按照什么id的镜像来创建,这个id是笔者下载的官方redis镜像的id。

组织集群关系:

redis-cli -a password --cluster create --cluster-replicas 1 172.173.0.2:6379 172.173.0.3:6380 172.173.0.4:6381 172.173.0.5:6382 172.173.0.6:6383 172.173.0.7:6384

–cluster- replicas 1 表示为每个master创建一一个slave节点

随后再进行springboot的配置:

spring:
  application:
    name: redis
  data:
    redis:
      password: password
      cluster:
      #重点重点重点重点重点重点
        nodes: 127.0.0.1:6379,127.0.0.1:6380,127.0.0.1:6381,127.0.0.1:6382,127.0.0.1:6383,127.0.0.1:6384
      lettuce:
        pool:
          max-active: 8
          max-wait: -1ms
          max-idle: 8
          min-idle: 0
        cluster:
          refresh:
            adaptive: true
            period: 2000
  mvc:
    pathmatch:
      matching-strategy: ant_path_matcher

一切就绪,准备连接:

@Service
@Slf4j
public class OrderService {
    public static final String ORDER_KEY = "ord:";
    @Autowired
    private StringRedisTemplate redisTemplate;

    public void addOrder(){
        long keyId = Instant.now().toEpochMilli();
        String serialNo = UUID.randomUUID().toString();

        String key = ORDER_KEY + keyId;
        String value = "虚拟订单号:" + serialNo;

        redisTemplate.opsForValue().set(key,value);

        log.info("key = {},value = {}",key,value);
    }

    public String getOrderById(Long id){
        return redisTemplate.opsForValue().get(ORDER_KEY+id);
    }

}
@RestController
public class OrderController {

    @Autowired
    private OrderService orderService;

    @RequestMapping(value = "/order/add", method = RequestMethod.POST)
    public void addOrder() {
        orderService.addOrder();
    }

    @RequestMapping(value = "/order/{keyId}", method = RequestMethod.GET)
    public String addOrder(@PathVariable Long keyId) {
        return orderService.getOrderById(keyId);
    }
}

最基础的springboot语法。随后运行起来想去建立连接。

出现问题:

出现了连接超时的报错

2024-08-02 21:57:03.230 [lettuce-nioEventLoop-4-10] WARN  io.lettuce.core.cluster.topology.DefaultClusterTopologyRefresh- Unable to connect to [172.173.0.5/<unresolved>:6382]: connection timed out after 10000 ms: /172.173.0.5:6382
2024-08-02 21:57:03.230 [lettuce-nioEventLoop-4-9] WARN  io.lettuce.core.cluster.topology.DefaultClusterTopologyRefresh- Unable to connect to [172.173.0.4/<unresolved>:6381]: connection timed out after 10000 ms: /172.173.0.4:6381

都是重复的,我就不全复制了

排查思路:

我们注意到,我们在springboot中配置的nodes节点的ip全是127.0.0.1,为什么报错里会显示诸如172.173.0.5:6382的docker自定义桥接网络的内网IP呢?

说明我们是访问到了redis,不然不可能得知他的内网ip。然后springboot根据这个内网ip去直接傻愣愣的去ping。那就ping到外网了呀,所以当然收不到回信。

于是乎,这篇帖子解释了: 解决问题的思路,cluster-announce-ip

说到了一组关键的配置:

cluster-announce-ip IP地址
cluster-announce-port port端口号
cluster-announce-bus-port 总线端口

总线端口是指6379对应的16379.也就是redis节点之间互相访问的那个端口。其实就是暴露的端口号+10000

结论:

也就是说,在Redis集群中,springboot客户端从Redis节点本身获取所有Redis节点的URL。因此,SpringBoot应用程序向其中一个配置的节点(127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381)发送“集群节点”的请求。

作为响应,它接收所有Redis节点的URL,在您的情况下是172.173.0.2:6379 172.173.0.3:6380 172.173.0.3:6381,并尝试与它们通信。由于这是内部Docker网络,就会收到连接失败错误。

我们想要的是将每个Redis节点配置为具有不同的“通告”主机和端口。这样,客户端将收到他们可以访问的URL,而不是内部主机和端口的URL。

所以我们应当添加上述三个参数,来让redis告知来请求url的客户端,对客户端那边来说,正确的访问路径。

但是!

我这里这么写可以吗?springboot得到了他想要的127.0.0.1,然后再加上我回复了正确的port端口,肯定可以正常读写了吧?

cluster-announce-ip 127.0.0.1
cluster-announce-port ${port}
cluster-announce-bus-port $((port + 10000))

NONONO,这里笔者认为,理论上springboot是可以读到的,但是我们根本到不了那一步,因为在创建集群的时候redis-cli -a password --cluster create --cluster-replicas 1 172.173.0.2:6379 172.173.0.3:6380 172.173.0.4:6381 172.173.0.5:6382 172.173.0.6:6383 172.173.0.7:6384,就会出现:

Waiting for the cluster to join -------------

的无限等待。

笔者大胆猜测(欢迎纠正),redis节点们建立集群的时候,也是通过这个cluster-announce-ip来确定对方的位置的。当A节点去问B节点的时候,场景就和Springboot去问B节点是一样的。这时候如果B节点返回了127.0.0.1,那么A节点一看,好啊,原来你得地址是127.0.0.1,这不是我自己容器的回环地址吗?
当然就连接不上了。

所以我们这里要填入宿主机的IP地址,也就是你输入ipconfig(linux输入:ifconfig)后出来的ip地址。

这样节点A访问B得到的ip就是本机IP,节点A再根据本机IP和端口去找节点B,那就和宿主机去找节点B的效果是一样的,当然可以找到了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值