Redis Cluster集群与spring boot 2集成

搭建Redis Cluster集群

本文使用redis-5.0.5,redis安装在/soft/redis目录下,需新建/soft/redis/data目录。

安装ruby

wget https://cache.ruby-lang.org/pub/ruby/2.6/ruby-2.6.3.tar.gz
tar -zxvf ruby-2.6.3.tar.gz
cd ruby-2.6.3/ 
./configure -prefix=/usr/local/ruby
make
make install
cd /usr/local/ruby/
cp bin/ruby /usr/local/bin/
cp bin/gem /usr/local/bin/
ruby --version

创建redis配置

cd /soft/redis

vim config/redis-7000.conf

port 7000
protected-mode no
daemonize yes
dir "/soft/redis/data"
dbfilename "dump-7000.rdb"
logfile "log-7000.log"

cluster-enabled yes
cluster-config-file nodes-7000.conf
cluster-require-full-coverage no

配置详解
cluster-enabled yes  启动cluster集群
cluster-require-full-coverage no  有一个节点不可用,依旧运行集群

再创建其他5份配置

sed 's/7000/7001/g' config/redis-7000.conf > config/redis-7001.conf
sed 's/7000/7002/g' config/redis-7000.conf > config/redis-7002.conf
sed 's/7000/7003/g' config/redis-7000.conf > config/redis-7003.conf
sed 's/7000/7004/g' config/redis-7000.conf > config/redis-7004.conf
sed 's/7000/7005/g' config/redis-7000.conf > config/redis-7005.conf

启动这6个redis实例

redis-server config/redis-7000.conf
redis-server config/redis-7001.conf
redis-server config/redis-7002.conf
redis-server config/redis-7003.conf
redis-server config/redis-7004.conf
redis-server config/redis-7005.conf

创建集群,redis-v5版本不再使用redis-trib.rb来创建集群,使用redis-cli --cluster代替(192.168.4.176是linux服务器的ip)

redis-cli --cluster create  --cluster-replicas 1 \
192.168.4.176:7000 192.168.4.176:7001 192.168.4.176:7002 192.168.4.176:7003 192.168.4.176:7004 192.168.4.176:7005

创建过程需要你输入yes确认
yes

打开一个客户端

redis-cli -p 7000

查看集群状态
cluster info

查看集群主从节点、槽位分配情况
cluster nodes

事实上新建cluster集群不是本文重点,网上有很多这种教程,cluster集群与spring boot 2集成才是本文重点。

spring boot 2集成redis cluster

pom.xml配置

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.6.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

其他配置省略不写了
<dependencies>

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

    <!-- cluster集群模式必须使用jedis 2.X,不然无法做到故障迁移 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
        <exclusions>
            <exclusion>
                <groupId>io.lettuce</groupId>
                <artifactId>lettuce-core</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
        <version>2.10.2</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
    </dependency>
</dependencies>

spring boot 2默认使用lettuce-core作为redis的客户端。一开始我使用lettuce-core连接redis,当我kill掉一个master节点(7000节点),redis服务端能够完成主从切换,实现故障转移。spring boot 2却还是会连接7000节点,导致key落在7000节点槽位的请求一直报错。估计是lettuce-core客户端没有重建连接池缓存,所以客户端无法做到故障迁移。后来改用jedis 2.X版本,spring boot 2工程才做到了故障迁移,不去连接7000节点。注意jedis 3.X版本会导致spring-boot 2无法启动。

application.properties配置

spring.redis.timeout=2000
spring.redis.jedis.pool.max-active=8
spring.redis.jedis.pool.max-idle=8
spring.redis.jedis.pool.min-idle=0
spring.redis.jedis.pool.max-wait=1000

spring.redis.cluster.nodes=192.168.4.176:7000, 192.168.4.176:7001, 192.168.4.176:7002, 192.168.4.176:7003, 192.168.4.176:7004, 192.168.4.176:7005
# 重定向次数
spring.redis.cluster.max-redirects=5

还有一个很坑爹的地方,在application.properties写好配置后,仅仅是能连接集群,客户端也无法做到故障迁移,还需要手动写一个JedisConnectionFactory配置类,再将JedisConnectionFactory设置为RedisTemplate的配置工厂,下面 是代码。

@Configuration
public class RedisConfigJedis {

    @Value("${spring.redis.timeout}")
    private Integer redisTimeout;
    @Value("${spring.redis.jedis.pool.max-active}")
    private Integer poolMaxActive;
    @Value("${spring.redis.jedis.pool.max-idle}")
    private Integer poolMaxIdle;
    @Value("${spring.redis.jedis.pool.min-idle}")
    private Integer poolMinIdle;
    @Value("${spring.redis.jedis.pool.max-wait}")
    private Integer poolMaxWait;
    @Value("${spring.redis.cluster.nodes}")
    private List<String> clusterNodes;
    @Value("${spring.redis.cluster.max-redirects}")
    private Integer clusterMaxRedirects;

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMaxTotal(poolMaxActive);
        poolConfig.setMaxIdle(poolMaxIdle);
        poolConfig.setMinIdle(poolMinIdle);
        poolConfig.setMaxWaitMillis(poolMaxWait);
        JedisClientConfiguration clientConfig = JedisClientConfiguration.builder()
                .usePooling().poolConfig(poolConfig).and().readTimeout(Duration.ofMillis(redisTimeout)).build();

        // cluster模式
        RedisClusterConfiguration redisConfig = new RedisClusterConfiguration();
        redisConfig.setMaxRedirects(clusterMaxRedirects);
        for (String ipPort :clusterNodes){
            String[] ipPortArr = ipPort.split(":");
            redisConfig.clusterNode(ipPortArr[0], Integer.parseInt(ipPortArr[1]));
        }

        return new JedisConnectionFactory(redisConfig, clientConfig);
    }

    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);

        //key使用StringRedisSerializer
        StringRedisSerializer strSerializer = new StringRedisSerializer();
        template.setKeySerializer(strSerializer);
        template.setHashKeySerializer(strSerializer);

        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);

        //value使用Jackson2JsonRedisSerializer
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.setHashValueSerializer(jackson2JsonRedisSerializer);

        return template;
    }
}

总结下,当kill掉一个redis集群的主节点(7000节点),从节点7003晋升为主节点,7000节点下线。redis集群故障迁移没问题,但spring boot工程此时要清空旧的redis连接池缓存,重新建立连接池缓存,这样才能把已经无效的7000节点连接池清除掉,不再去连接7000节点。spring boot 2版本要做故障迁移,要注意以下几点。

1、pom.xml导入jedis 2.X版本依赖包。jedis 3.X版本也不行。

2、把application.properties中的配置再次配置到JedisConnectionFactory中,RedisTemplate 使用JedisConnectionFactory作为连接工厂。

当然,也可能是我哪里搞错了,欢迎大家留言讨论。

接下来写一个测试类循环调用redis

@RunWith(SpringRunner.class)
@SpringBootTest
public class Test01 {

    @Autowired
    RedisTemplate redisTemplate;

    @Test
    public void test001(){
        while (true){
            try {
                String key = "test:" + new Date().getTime();
                redisTemplate.opsForValue().set(key, new Date().getTime());
                TimeUnit.MILLISECONDS.sleep(100L);
                System.out.println(redisTemplate.opsForValue().get(key));
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
}

运行测试类

打开一个redis-cli

redis-cli -p 7002

查看集群节点配置

cluster nodes

7001是主节点,7005是7001的从节点

kill 主节点7001的进程

ps -ef|grep redis-server

kill -9 master进程号

此时spring boot工程会报异常,经过短暂时间后,工程运行正常。

再次查看redis集群节点

7005晋升为master,7001下线。

再次启动7001节点,7001会变成7005的从节点。

当7001、7005这一对主从节点都下线的时候,由于负责5491-10933槽位端的节点都下线了,get、set到此槽位端的请求都会报错。

发布了51 篇原创文章 · 获赞 14 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/u010606397/article/details/94613553