关于spring.redis.cluster集群部分Master节点故障后 交易中断60秒解决方案

背景

   第一种方案:

公司项目做性能测试,redis集群选用cluster集群模式,开始选用3主3从,共3台服务器,每个服务器一主一从。

       服务器A   redis 8001  8002

       服务器B   redis 8003  8004 

       服务器C   redis 8005  8006 

     8001 主  

     8002 从 (主为8003)

     8003 主

     8004 从(主为8005)

     8005 主

     8006从(主为8001)

            

 1.停止服务器A      

     此时集群状态为

      8003 主

     8004 从(主为8005)

     8005 主

     8006 主(代替8001)

2.启动服务器A

  此时集群状态为

     8001 从 (主为8006)

     8002 从 (主为8003)

     8003 主

     8004 从(主为8005)

     8005 主

     8006 主 

3.停止服务器C

   由于C服务器现承载两个主节点,服务器停止 导致集群过半数主节点停止,集群瘫痪

总结 :此方案在业内较为流行,但不是最完美的解决方案,若遇到此种情况 无法避免

第二种方案:

   服务器A   redis 8001 

   服务器B   redis 8002   

   服务器C   redis 8003   

   服务器D   redis 8004

   服务器E   redis 8005

   服务器F   redis 8006

随意启停服务均可实现高可用,但是此时又暴露出一个问题  

重头戏

停止Master节点及Slave节点过程中,出现停止部分Master节点交易中断60秒无法连接情况,该问题主要原因为Redis集群本身高可用切换结束之后,应用程序没有及时更新集群拓扑信息(spring-boot-starter-data-jpa框架默认关闭了集群拓扑刷新机制,默认刷新时间为60秒),从而导致交易中断 60秒的情况 

步骤一:将RedisConfiguration.java添加到应用的cofing文件夹中(




import io.lettuce.core.ReadFrom;
import io.lettuce.core.cluster.ClusterClientOptions;
import io.lettuce.core.cluster.ClusterTopologyRefreshOptions;
import lombok.extern.slf4j.Slf4j;

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisNode;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
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.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * Redis配置
 *
 * @author zhaoxg
 */
@Slf4j
@Configuration
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class RedisConfiguration {
    @Autowired
    private RedisProperties redisProperties;

    @Bean(destroyMethod = "destroy")
    public LettuceConnectionFactory redisConnectionFactory() {
        // redis单节点
        if (null == redisProperties.getCluster() || null == redisProperties.getCluster().getNodes()) {
            RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration(redisProperties.getHost(),
                    redisProperties.getPort());
            configuration.setPassword(redisProperties.getPassword());
            return new LettuceConnectionFactory(configuration);
        }

        // redis集群
        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 redisClusterConfiguration = new RedisClusterConfiguration();
        redisClusterConfiguration.setClusterNodes(nodes);
        //RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration(redisProperties.getCluster().getNodes());
        redisClusterConfiguration.setPassword(redisProperties.getPassword());
        redisClusterConfiguration.setMaxRedirects(redisProperties.getCluster().getMaxRedirects());

        GenericObjectPoolConfig<?> genericObjectPoolConfig = new GenericObjectPoolConfig<>();
        genericObjectPoolConfig.setMaxTotal(redisProperties.getLettuce().getPool().getMaxActive());
        genericObjectPoolConfig.setMaxIdle(redisProperties.getLettuce().getPool().getMaxIdle());
        genericObjectPoolConfig.setMinIdle(redisProperties.getLettuce().getPool().getMinIdle());
        genericObjectPoolConfig.setMaxWaitMillis(redisProperties.getLettuce().getPool().getMaxWait().getSeconds());
        
       
        ClusterTopologyRefreshOptions clusterTopologyRefreshOptions = ClusterTopologyRefreshOptions.builder()
                .enableAllAdaptiveRefreshTriggers()
                .adaptiveRefreshTriggersTimeout(Duration.ofSeconds(3))
                .enablePeriodicRefresh(Duration.ofSeconds(6))
                .build();

        ClusterClientOptions clusterClientOptions = ClusterClientOptions.builder()
                .topologyRefreshOptions(clusterTopologyRefreshOptions).build();

        LettuceClientConfiguration lettuceClientConfiguration = LettucePoolingClientConfiguration.builder()
                .poolConfig(genericObjectPoolConfig)
                .clientOptions(clusterClientOptions)
                .commandTimeout(redisProperties.getTimeout()).build();

        LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory(redisClusterConfiguration, lettuceClientConfiguration);
        lettuceConnectionFactory.setShareNativeConnection(false);// 是否允许多个线程操作共用同一个缓存连接,默认 true,false 时每个操作都将开辟新的连接
        lettuceConnectionFactory.resetConnection();// 重置底层共享连接, 在接下来的访问时初始化
        return lettuceConnectionFactory;
    }

    @Bean
    public RedisTemplate<Object, Object> redisTemplate(LettuceConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
}

步骤二:修改应用pom.xml文件,添加comm-pools依赖;

<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>

步骤三:修改application文件

spring.redis.timeout=3000
spring.redis.cluster.max-redirects=5
spring.redis.cluster.nodes=集群信息
spring.redis.lettuce.pool.max-active=1000
spring.redis.lettuce.pool.max-idle=100
spring.redis.lettuce.pool.max-wait=5000
spring.redis.lettuce.pool.min-idle=5
spring.redis.password=密码

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值