spring boot整合redis集群

最近的一个个人项目中使用到redis集群,redis集群的搭建环境使用同一台虚拟机,创建集群的命令是

redis-cli --cluster create 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 --cluster-replicas 1

具体如何创建redis集群可以看我另一篇文章。

以上是背景描述。下面先讲一下如何在spring boot 中连接redis集群。

首先引入redis starter 的pom依赖,版本信息可以自行添加。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

然后再配置中添加对应redis集群的节点信息。

#redis集群连接配置
spring.redis.cluster.nodes=192.168.0.15:6379,192.168.0.15:6380,192.168.0.15:6381,192.168.0.15:6382,192.168.0.15:6383,192.168.0.15:6384

到了这里就已经完成配置,其他的连接redis集群的操作,都由spring boot替我们完成,在使用的时候只需要依赖注入RedisTemplate就可以。

然而就在我以为确实是这样的时候,却发现程序在启动的时候一致报错。

配置正确,启动报错

报错信息很简单,关键部分如下所示。

io.lettuce.core.RedisConnectionException: Unable to connect to 127.0.0.1:6380
......

上面的描述基本上就是无法连接到redis节点,但是为什么呢?我的配置中的信息明明是正确的。按道理不应该报错。于是百度一番,但是很难找不到我这种情况。

仔细查看错误信息,发现是在我程序中一个@PostConstruct方法执行时,利用redisTemplate进行设置一个

redisTemplate.opsForValue().set

操作报错。根据这一点,跟踪代码。

调试,查看redisTemplate中的连接信息,如下图所示。确实是我的配置信息。但是在执行set的时候确实是报错。


springboot redis 集群主从_spring

没办法只能跟踪以下set的方法调用,分析连接信息是在什么时候改变的。

最终发现了问题所在,连接信息确实会在某一时刻被改变,方法如下

  • io.lettuce.core.cluster.RedisClusterClient#loadPartitions
protected Partitions loadPartitions() {

    Iterable<RedisURI> topologyRefreshSource = getTopologyRefreshSource();

    String message = "Cannot retrieve initial cluster partitions from initial URIs " + topologyRefreshSource;
    try {
        //分别从每个redis节点获取集群信息
        Map<RedisURI, Partitions> partitions = refresh.loadViews(topologyRefreshSource, useDynamicRefreshSources());

        if (partitions.isEmpty()) {
            throw new RedisException(message);
        }
        //从每个节点获得的集群信息中重新决定一份节点连接信息
        Partitions loadedPartitions = determinePartitions(this.partitions, partitions);
        RedisURI viewedBy = refresh.getViewedBy(partitions, loadedPartitions);

        for (RedisClusterNode partition : loadedPartitions) {
            if (viewedBy != null) {
                RedisURI uri = partition.getUri();
                RedisClusterURIUtil.applyUriConnectionSettings(viewedBy, uri);
            }
        }

        activateTopologyRefreshIfNeeded();

        return loadedPartitions;

    } catch (RedisConnectionException e) {
        throw new RedisException(message, e);
    }
}

这个方法是遍历集群节点配置,然后连接每个节点,分别从每个redis节点获取集群信息,获得的信息如下。

springboot redis 集群主从_redis集群_02

上面这张图,不看别的,就看从6379这个端口的节点获取的集群信息,除了6379本身,其他的全是127.0.0.1,然后后面经过determinePartitions方法,选出一个127.0.0.1的节点连接也就不出为奇了。

那么到底是什么造成上面的情况?分析了一下,执行以下命令

redis-cli --cluster check 127.0.0.1:6379

得到集群信息,是127.0.0.1的连接信息,试想一下,如果java程序连接redis时,获取集群信息是不是也是这样,那么就可以解释为何连接是程序获得的节点连接是127.0.0.1了。

127.0.0.1:6379 (48d62570...) -> 0 keys | 5461 slots | 1 slaves.
127.0.0.1:6381 (842f698d...) -> 0 keys | 5461 slots | 1 slaves.
127.0.0.1:6380 (fdfb3bea...) -> 0 keys | 5462 slots | 1 slaves.

解决方案

上面知道了问题所在就好办了,我们只需要在创建集群的时候,不要使用127.0.0.1,要使用我们机器能访问到的ip地址,例如我的虚拟机ip是192.168.0.15,所以重新建立redis集群,将一开始的创建命令改为以下方式。

redis-cli --cluster create 192.168.0.15:6379 192.168.0.15:6380 192.168.0.15:6381 192.168.0.15:6382 192.168.0.15:6383 192.168.0.15:6384 --cluster-replicas 1

重新创建集群后,重新启动程序,正确启动,ok!!!