美文网首页redisalreadyredis
Redis集群Lettuce主从切换问题

Redis集群Lettuce主从切换问题

作者: AC编程 | 来源:发表于2022-06-09 14:59 被阅读0次

    一、问题描述

    Redis Cluster集群,当master宕机,主从切换,客户端报错 timed out

    二、原因

    SpringBoot2.X版本开始Redis默认的连接池都是采用的Lettuce。当节点发生改变后,Letture默认是不会刷新节点拓扑的。

    三、解决方案

    3.1 方案一:把lettuce换成jedis

    只需要在pom.xml里调整一下依赖的引用

          <dependency>
               <groupId>org.springframework.boot</groupId>
               <artifactId>spring-boot-starter-data-redis</artifactId>
               <version>2.1.5.RELEASE</version>
                   <!-- 不用lettuce ,用jedis -->
                   <exclusions>
                       <exclusion>
                           <groupId>io.lettuce</groupId>
                           <artifactId>lettuce-core</artifactId>
                       </exclusion>
                   </exclusions>
           </dependency>
    
    
           <dependency>
               <groupId>redis.clients</groupId>
               <artifactId>jedis</artifactId>
               <version>3.1.0-m4</version>
           </dependency>
    
    3.2 方案二:刷新节点拓扑视图

    Redis节点异常,服务端的Redis集群拓扑被刷新了,Java程序没有获取到新的拓扑。

    Lettuce官方文档中关于Redis Cluster的相关说明:Lettuce处理Moved和Ask永久重定向,由于命令重定向,你必须刷新节点拓扑视图。而自适应拓扑刷新(Adaptive updates)与定时拓扑刷新(Periodic updates)是默认关闭的,可以通过如下代码打开。

    https://github.com/lettuce-io/lettuce-core/wiki/Redis-Cluster#user-content-refreshing-the-cluster-topology-view

    Lettuce

    修改代码如下

    package com.montnets.common.redis;
    
    import io.lettuce.core.ClientOptions;
    import io.lettuce.core.cluster.ClusterClientOptions;
    import io.lettuce.core.cluster.ClusterTopologyRefreshOptions;
    import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.data.redis.connection.RedisClusterConfiguration;
    import org.springframework.data.redis.connection.RedisNode;
    import org.springframework.data.redis.connection.RedisPassword;
    import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
    import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
    import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
    import org.springframework.stereotype.Component;
    
    import java.time.Duration;
    import java.util.HashSet;
    import java.util.List;
    import java.util.Set;
    
    @Component
    public class RedisPoolConfig {
    
        @Autowired
        private RedisProperties redisProperties;
    
        public GenericObjectPoolConfig<?> genericObjectPoolConfig(RedisProperties.Pool properties) {
            GenericObjectPoolConfig<?> config = new GenericObjectPoolConfig<>();
            config.setMaxTotal(properties.getMaxActive());
            config.setMaxIdle(properties.getMaxIdle());
            config.setMinIdle(properties.getMinIdle());
            if (properties.getTimeBetweenEvictionRuns() != null) {
                config.setTimeBetweenEvictionRunsMillis(properties.getTimeBetweenEvictionRuns().toMillis());
            }
            if (properties.getMaxWait() != null) {
                config.setMaxWaitMillis(properties.getMaxWait().toMillis());
            }
            return config;
        }
    
        @Bean(destroyMethod = "destroy")
        public LettuceConnectionFactory lettuceConnectionFactory() {
    
            //开启 自适应集群拓扑刷新和周期拓扑刷新
            ClusterTopologyRefreshOptions clusterTopologyRefreshOptions =  ClusterTopologyRefreshOptions.builder()
                    // 开启全部自适应刷新
                    .enableAllAdaptiveRefreshTriggers() // 开启自适应刷新,自适应刷新不开启,Redis集群变更时将会导致连接异常
                    // 自适应刷新超时时间(默认30秒)
                    .adaptiveRefreshTriggersTimeout(Duration.ofSeconds(30)) //默认关闭开启后时间为30秒
                    // 开周期刷新
                    .enablePeriodicRefresh(Duration.ofSeconds(20))  // 默认关闭开启后时间为60秒 ClusterTopologyRefreshOptions.DEFAULT_REFRESH_PERIOD 60  .enablePeriodicRefresh(Duration.ofSeconds(2)) = .enablePeriodicRefresh().refreshPeriod(Duration.ofSeconds(2))
                    .build();
    
            // https://github.com/lettuce-io/lettuce-core/wiki/Client-Options
            ClientOptions clientOptions = ClusterClientOptions.builder()
                    .topologyRefreshOptions(clusterTopologyRefreshOptions)
                    .build();
    
            LettuceClientConfiguration clientConfig = LettucePoolingClientConfiguration.builder()
                    .poolConfig(genericObjectPoolConfig(redisProperties.getJedis().getPool()))
                    //.readFrom(ReadFrom.MASTER_PREFERRED)
                    .clientOptions(clientOptions)
                    .commandTimeout(redisProperties.getTimeout()) //默认RedisURI.DEFAULT_TIMEOUT 60
                    .build();
    
            List<String> clusterNodes = redisProperties.getCluster().getNodes();
            Set<RedisNode> nodes = new HashSet<RedisNode>();
            clusterNodes.forEach(address -> nodes.add(new RedisNode(address.split(":")[0].trim(), Integer.valueOf(address.split(":")[1]))));
    
            RedisClusterConfiguration clusterConfiguration = new RedisClusterConfiguration();
            clusterConfiguration.setClusterNodes(nodes);
            clusterConfiguration.setPassword(RedisPassword.of(redisProperties.getPassword()));
            clusterConfiguration.setMaxRedirects(redisProperties.getCluster().getMaxRedirects());
    
            LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory(clusterConfiguration, clientConfig);
            // lettuceConnectionFactory.setShareNativeConnection(false); //是否允许多个线程操作共用同一个缓存连接,默认true,false时每个操作都将开辟新的连接
            // lettuceConnectionFactory.resetConnection(); // 重置底层共享连接, 在接下来的访问时初始化
            return lettuceConnectionFactory;
        }
    }
    

    相关文章

      网友评论

        本文标题:Redis集群Lettuce主从切换问题

        本文链接:https://www.haomeiwen.com/subject/sdsmmrtx.html