SSM项目与SpringBoot项目中Redis集群使用

一. 背景

一次Redis集群故障,Redis集群中的某个节点宕机了,项目中配置的Redis连接池,没有将不可用的连接从连接池中移除,从而导致程序从连接池中拿到了一个不可用的连接,导致系统功能不可用,从而引发了生产故障。

二. SpringBoot项目中使用

创建一个SpringBoot项目,然后在项目中引入依赖:

SpringBoot2.0以上,默认使用lettuce作为连接Redis的客户端,如果我们要使用默认的lettuce客户端,需要引入commons-pool2依赖。

xml 复制代码
<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-pool2</artifactId>
</dependency>

本文,我们使用spring-boot-starter-data-redisjedis来实现Redis的集群高可用配置,具体配置步骤如下:

  1. 在项目的pom文件中引入依赖:
xml 复制代码
<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>3.8.0</version>
        </dependency>

因为我们使用Jedis客户端,所以将spring-boot-starter-data-redis中的lettuce依赖去掉。

  1. yml文件或者properties文件中添加Redis信息,本文使用yml文件,所以信息如下:
yaml 复制代码
spring:
  redis:
    cluster:
      nodes: 192.168.1.2:6379,192.168.1.3:6379,192.168.1.4:6379
      max-redirects: 3
redis:
  timeout: 10000 # 客户端超时时间(单位是毫秒,默认是2000)
  maxIdle: 300 # 最大空闲数
  maxTotal: 1000 # 控制一个pool可分配多少个jedis实例,用来替换redis.maxActive,如果是jedis 2.4以后用该属性
  maxWaitMillis: 1000 # 最大建立连接等待时间,如果超过此时间将连接到异常,-1表示无限制
  minEvictableIdleTimeMillis: 300000 # 连接的最小空闲时间 默认1800000毫秒(30分钟)
  numTestsPerEvictionRun: 1024 # 每次释放连接的最大数目,默认3
  timeBetweenEvictionRunsMillis: 30000 # 逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1
  testOnBorrow: true # 是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个
  testWhileIdle: true # 在空闲时检查有效性, 默认false
  password: Test@Pass@Cluster
  1. 创建Redis集群的配置类,在SpringBoot项目中,我们使用注解@Configuration加载配置类,配置类如下:
kotlin 复制代码
@Configuration
public class RedisTemplateConfiguration {
​
    @Value("${spring.redis.cluster.nodes}")
    private String clusterNodes;
​
    @Value("${spring.redis.cluster.max-redirects}")
    private int maxRedirects;
​
    @Value("${redis.password}")
    private String password;
​
    @Value("${redis.timeout}")
    private int timeout;
​
    @Value("${redis.maxIdle}")
    private int maxIdle;
​
    @Value("${redis.maxTotal}")
    private int maxTotal;
​
    @Value("${redis.maxWaitMillis}")
    private int maxWaitMillis;
​
    @Value("${redis.minEvictableIdleTimeMillis}")
    private int minEvictableIdleTimeMillis;
​
    @Value("${redis.numTestsPerEvictionRun}")
    private int numTestsPerEvictionRun;
​
    @Value("${redis.timeBetweenEvictionRunsMillis}")
    private int timeBetweenEvictionRunsMillis;
​
    @Value("${redis.testOnBorrow}")
    private boolean testOnBorrow;
​
    @Value("${redis.testWhileIdle}")
    private boolean testWhileIdle;
​
​
    /**
     * <p>Jedis连接池配置</p>
     * @return {@link JedisPoolConfig}
     */
    @Bean
    public JedisPoolConfig getJedisPoolConfig() {
      final JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
      // 最大空闲数
      jedisPoolConfig.setMaxIdle(maxIdle);
      // 连接池最大连接数
      jedisPoolConfig.setMaxTotal(maxTotal);
      // 最大建立连接等待时间
      jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
      // 逐出连接的最小空闲时间,默认1800000毫秒(30分钟)
      jedisPoolConfig.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
      // 每次逐出检查时 逐出的最大数目
      jedisPoolConfig.setNumTestsPerEvictionRun(numTestsPerEvictionRun);
      // 逐出扫描的时间间隔
      jedisPoolConfig.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
      // 是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个
      jedisPoolConfig.setTestOnBorrow(testOnBorrow);
      jedisPoolConfig.setTestOnReturn(testOnBorrow);
      // 在空闲时检查有效性, 默认false
      jedisPoolConfig.setTestWhileIdle(testWhileIdle);
      return jedisPoolConfig;
    }
​
    /**
     * <p>Redis集群配置</p>
     * @return {@link RedisClusterConfiguration}
     */
    @Bean
    public RedisClusterConfiguration redisClusterConfiguration() {
      final RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration();
      final String[] nodes = StringUtils.split(clusterNodes, ",");
      final Set<RedisNode> nodeSet = new HashSet<>();
      for (final String node : nodes) {
        final String[] clusterNode = StringUtils.split(node, ":");
        nodeSet.add(new RedisNode(clusterNode[0], Integer.parseInt(clusterNode[1])));
      }
      redisClusterConfiguration.setClusterNodes(nodeSet);
      redisClusterConfiguration.setMaxRedirects(maxRedirects);
      redisClusterConfiguration.setPassword(RedisPassword.of(password));
      return redisClusterConfiguration;
    }
​
    /**
     * <p>redis集群模式</p>
     * @return {@link JedisConnectionFactory}
     */
    @Bean
    public JedisConnectionFactory jedisConnectionFactory() {
      final JedisConnectionFactory connectionFactory = new JedisConnectionFactory(redisClusterConfiguration(), getJedisPoolConfig());
      connectionFactory.setDatabase(0);
      connectionFactory.setTimeout(timeout);
      connectionFactory.setUsePool(true);
      return connectionFactory;
    }
​
    /**
     * <p>实例化RedisTemplate</p>
     * @return {@link RedisTemplate< String, Object>}
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate() {
      final RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
      initDomainRedisTemplate(redisTemplate);
      return redisTemplate;
    }
​
    /**
     * <p>序列化</p>
     * @param redisTemplate
     */
    private void initDomainRedisTemplate(final RedisTemplate<String, Object> redisTemplate) {
      redisTemplate.setKeySerializer(new StringRedisSerializer());
      redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
      redisTemplate.setEnableTransactionSupport(false);
      redisTemplate.setConnectionFactory(jedisConnectionFactory());
    }
​
}
  1. 除了使用配置类,我们也可以使用xml文件来配置,步骤3和步骤4,可以根据自己项目的实际情况选择,如下是xml的配置方式。
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/util
        http://www.springframework.org/schema/util/spring-util.xsd
        ">
  
  <!-- redis连接池配置 -->
  <bean id="jedisPoolConf" class="redis.clients.jedis.JedisPoolConfig">
    <property name="maxIdle" value="${redis.maxIdle}"/>
    <property name="maxTotal" value="${redis.maxTotal}"/>
    <property name="maxWaitMillis" value="${redis.maxWaitMillis}"/>
    <property name="minEvictableIdleTimeMillis" value="${redis.minEvictableIdleTimeMillis}"/>
    <property name="numTestsPerEvictionRun" value="${redis.numTestsPerEvictionRun}"/>
    <property name="timeBetweenEvictionRunsMillis" value="${redis.timeBetweenEvictionRunsMillis}"/>
    <property name="testOnBorrow" value="${redis.testOnBorrow}"/>
    <property name="testWhileIdle" value="${redis.testWhileIdle}"/>
  </bean>
​
  <!-- redis集群配置 -->
  <bean id="redisClusterConfiguration" class="org.springframework.data.redis.connection.RedisClusterConfiguration">
    <property name="clusterNodes">
      <set>
        <bean id="cluster1" class="org.springframework.data.redis.connection.RedisNode">
          <constructor-arg name="host" value="${cluster.redis.host1}"/>
          <constructor-arg name="port" value="${cluster.redis.port1}"/>
        </bean>
        <bean id="cluster2" class="org.springframework.data.redis.connection.RedisNode">
          <constructor-arg name="host" value="${cluster.redis.host1}"/>
          <constructor-arg name="port" value="${cluster.redis.port2}"/>
        </bean>
        <bean id="cluster3" class="org.springframework.data.redis.connection.RedisNode">
          <constructor-arg name="host" value="${cluster.redis.host2}"/>
          <constructor-arg name="port" value="${cluster.redis.port1}"/>
        </bean>
        <bean id="cluster4" class="org.springframework.data.redis.connection.RedisNode">
          <constructor-arg name="host" value="${cluster.redis.host2}"/>
          <constructor-arg name="port" value="${cluster.redis.port2}"/>
        </bean>
        <bean id="cluster5" class="org.springframework.data.redis.connection.RedisNode">
          <constructor-arg name="host" value="${cluster.redis.host3}"/>
          <constructor-arg name="port" value="${cluster.redis.port1}"/>
        </bean>
        <bean id="cluster6" class="org.springframework.data.redis.connection.RedisNode">
          <constructor-arg name="host" value="${cluster.redis.host3}"/>
          <constructor-arg name="port" value="${cluster.redis.port2}"/>
        </bean>
      </set>
    </property>
    <property name="maxRedirects" value="${spring.redis.cluster.max-redirects}"/>
    <property name="password" value="${redis.password}"/>
  </bean>
​
  <!-- jedis连接工厂配置 -->
  <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
    <property name="database" value="0"/>
    <property name="timeout" value="${redis.timeout}"/>
    <property name="usePool" value="true"/>
    <constructor-arg index="0" ref="redisClusterConfiguration"/>
    <constructor-arg index="1" ref="jedisPoolConf"/>
  </bean>
​
  <!-- RedisTemplate实例化 -->
  <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
    <property name="keySerializer" ref="stringRedisSerializer"/>
    <property name="valueSerializer" ref="jdkSerializationRedisSerializer"/>
    <property name="enableTransactionSupport" value="false"/>
    <property name="connectionFactory" ref="jedisConnectionFactory"/>
  </bean>
​
  <!-- 序列化 -->
  <bean id="stringRedisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
  <bean id="jdkSerializationRedisSerializer" class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>
</beans>

当Redis集群某个节点出问题时,通过testOnBorrowtestOnReturn属性将不可用的连接从JedisPool中丢弃,所以这两个属性的值都要设置成true,从而不影响服务的使用,但是当整个集群不可用时,我们的服务去调用Redis进行操作,也是不可行的。

当Redis集群可用时,会自动将连接添加到连接池中。

通过上面的方式,在我们的项目中配置Redis集群,即使是整个Redis集群不可用时,我们的服务依然可以正常启动,不会因为Redis集群服务不可用,从而导致我们的服务启动失败。

  1. 使用RedisTemplate类来操作Redis。
typescript 复制代码
  @Resource
  private RedisTemplate<String, Object> redisTemplate;
​
  /**
   * <p>操作Redis</p>
   * @return  {@link String}
   */
  public String test() {
    redisTemplate.opsForValue().set("name", "test-redis-cluster......");
    return String.valueOf(redisTemplate.opsForValue().get("name"));
  }

以上便是在SpringBoot项目中配置Redis集群的方式。

三. SSM项目中使用

所谓SSM项目,指的是使用Spring+SpringMVC+Mybatis框架整合的项目,本文将通过如下步骤介绍如何在SSM项目中配置Redis集群。

  1. 创建一个Maven项目,在Maven项目的pom文件中,引入Spring、SpringMVC、Mybatis、Redis的依赖,本文主要是介绍Redis集群在SSM中的配置,所以Spring、SpringMVC、Mybatis框架整合,就不介绍了。
xml 复制代码
<properties>
  <maven.compiler.source>8</maven.compiler.source>
  <maven.compiler.target>8</maven.compiler.target>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  <jedis.version>2.9.0</jedis.version>
  <spring.data.redis>2.0.10.RELEASE</spring.data.redis>
</properties>
​
<dependencies>
  <dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>${jedis.version}</version>
  </dependency>
​
  <dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-redis</artifactId>
    <version>${spring.data.redis}</version>
  </dependency>
</dependencies>
​
  1. 在resource目录下创建application.properties文件,这个文件中填写Redis的配置信息,信息如下:
ini 复制代码
redis.pool.maxTotal=2000
redis.pool.maxIdle=300
redis.pool.timeBetweenEvictionRunsMillis=30000
redis.pool.minEvictableIdleTimeMillis=300000
redis.pool.softMinEvictableIdleTimeMillis=1800000
redis.pool.maxWaitMillis=1000
redis.pool.testOnBorrow=true
redis.pool.testOnReturn=false
redis.pool.testWhileIdle=true
redis.pool.numTestsPerEvictionRun=1024
redis.pool.blockWhenExhausted=false
redis.timeout=10000
redis.max.redirects=6
redis.cluster.password=Test@Pass@Cluster
redis.cluster.host1=192.168.1.2
redis.cluster.host2=192.168.1.3
redis.cluster.host3=192.168.1.4
redis.cluster.port1=6279
redis.cluster.port2=6379
  1. 在resource目录下创建applicationContext-redis.xml文件,文件命名可以根据自己项目的命名规则命名。
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/util
        http://www.springframework.org/schema/util/spring-util.xsd
        ">
  
  <!-- redis连接池配置 -->
  <bean id="jedisPoolConf" class="redis.clients.jedis.JedisPoolConfig">
    <property name="maxIdle" value="${redis.maxIdle}"/>
    <property name="maxTotal" value="${redis.maxTotal}"/>
    <property name="maxWaitMillis" value="${redis.maxWaitMillis}"/>
    <property name="minEvictableIdleTimeMillis" value="${redis.minEvictableIdleTimeMillis}"/>
    <property name="numTestsPerEvictionRun" value="${redis.numTestsPerEvictionRun}"/>
    <property name="timeBetweenEvictionRunsMillis" value="${redis.timeBetweenEvictionRunsMillis}"/>
    <property name="testOnBorrow" value="${redis.testOnBorrow}"/>
    <property name="testWhileIdle" value="${redis.testWhileIdle}"/>
  </bean>
​
  <!-- redis集群配置 -->
  <bean id="redisClusterConfiguration" class="org.springframework.data.redis.connection.RedisClusterConfiguration">
    <property name="clusterNodes">
      <set>
        <bean id="cluster1" class="org.springframework.data.redis.connection.RedisNode">
          <constructor-arg name="host" value="${cluster.redis.host1}"/>
          <constructor-arg name="port" value="${cluster.redis.port1}"/>
        </bean>
        <bean id="cluster2" class="org.springframework.data.redis.connection.RedisNode">
          <constructor-arg name="host" value="${cluster.redis.host1}"/>
          <constructor-arg name="port" value="${cluster.redis.port2}"/>
        </bean>
        <bean id="cluster3" class="org.springframework.data.redis.connection.RedisNode">
          <constructor-arg name="host" value="${cluster.redis.host2}"/>
          <constructor-arg name="port" value="${cluster.redis.port1}"/>
        </bean>
        <bean id="cluster4" class="org.springframework.data.redis.connection.RedisNode">
          <constructor-arg name="host" value="${cluster.redis.host2}"/>
          <constructor-arg name="port" value="${cluster.redis.port2}"/>
        </bean>
        <bean id="cluster5" class="org.springframework.data.redis.connection.RedisNode">
          <constructor-arg name="host" value="${cluster.redis.host3}"/>
          <constructor-arg name="port" value="${cluster.redis.port1}"/>
        </bean>
        <bean id="cluster6" class="org.springframework.data.redis.connection.RedisNode">
          <constructor-arg name="host" value="${cluster.redis.host3}"/>
          <constructor-arg name="port" value="${cluster.redis.port2}"/>
        </bean>
      </set>
    </property>
    <property name="maxRedirects" value="${spring.redis.cluster.max-redirects}"/>
    <property name="password" value="${redis.password}"/>
  </bean>
​
  <!-- jedis连接工厂配置 -->
  <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
    <property name="database" value="0"/>
    <property name="timeout" value="${redis.timeout}"/>
    <property name="usePool" value="true"/>
    <constructor-arg index="0" ref="redisClusterConfiguration"/>
    <constructor-arg index="1" ref="jedisPoolConf"/>
  </bean>
​
  <!-- RedisTemplate实例化 -->
  <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
    <property name="keySerializer" ref="stringRedisSerializer"/>
    <property name="valueSerializer" ref="jdkSerializationRedisSerializer"/>
    <property name="enableTransactionSupport" value="false"/>
    <property name="connectionFactory" ref="jedisConnectionFactory"/>
  </bean>
​
  <!-- 序列化 -->
  <bean id="stringRedisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
  <bean id="jdkSerializationRedisSerializer" class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>
</beans>

其实这个applicationContext.xml中的内容,和上面SpringBoot项目中的那个xml文件内容是一致的,唯一的区别是,需要在applicationContext.xml文件中新增一行代码<context:property-placeholder location="classpath:application.properties"/>,增加这行代码的目的,是保证我们的xml文件能够正确的读取到properties文件中的配置信息。

依然使用testOnBorrowtestOnReturn属性将不可用的连接从JedisPool中丢弃,然后通过JedisConnectionFactory来管理,如果你的Redis集群有密码验证,那么你使用的spring-data-redis的版本必须是2.0以上版本,在2.0以上版本中,RedisClusterConfiguration才支持密码。

在SSM项目中,spring、spring-data-redis、jedis版本必须要对应才行,如果版本之间不对应,则会出现一些想不到的问题。

如果我们把密码配置在JedisConnectionFactory上,在集群模式上也是加载不了的。

  1. 将创建的applicationContext.xml文件添加到web.xml文件中。
xml 复制代码
<context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>
    classpath:applicationContext.xml
    classpath:applicationContext-redis.xml
  </param-value>
</context-param>
<listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
  1. 使用RedisTemplate来操作Redis。
typescript 复制代码
@Resource
private RedisTemplate<String, Object> redisTemplate;
​
public String test() {
  final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
  valueOperations.set("test", "This use RedisTemplate ......");
  return String.valueOf(valueOperations.get("test"));
}

以上步骤便是SSM项目中配置Redis集群和操作Redis的方式。

四. Jedis单独使用

对于一些比较老的项目,例如spring的版本在4.2.*以下,只使用Jedis来配置Redis集群

  1. 在项目的pom文件中引入jedis的依赖。
xml 复制代码
<dependencies>
  <dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
  </dependency>
</dependencies>
  1. 创建application.properties文件。
ini 复制代码
redis.pool.maxTotal=2000
redis.pool.maxIdle=300
redis.pool.timeBetweenEvictionRunsMillis=30000
redis.pool.minEvictableIdleTimeMillis=300000
redis.pool.softMinEvictableIdleTimeMillis=1800000
redis.pool.maxWaitMillis=1000
redis.pool.testOnBorrow=true
redis.pool.testOnReturn=false
redis.pool.testWhileIdle=true
redis.pool.numTestsPerEvictionRun=1024
redis.pool.blockWhenExhausted=false
redis.timeout=10000
redis.max.redirects=6
redis.cluster.password=Test@Pass@Cluster
redis.cluster.host1=192.168.1.2
redis.cluster.host2=192.168.1.3
redis.cluster.host3=192.168.1.4
redis.cluster.port1=6279
redis.cluster.port2=6379
  1. 创建Redis集群配置的xml文件。
xml 复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
            http://www.springframework.org/schema/tx
            http://www.springframework.org/schema/tx/spring-tx.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context-2.5.xsd">
​
  <context:property-placeholder location="classpath:application.properties"/>
​
  <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
    <!-- 最大连接数 -->
    <property name="maxTotal" value="200"/>
    <!-- 连接池中最大空闲数 -->
    <property name="maxIdle" value="100"/>
    <!-- 连接池中确保最少空闲连接数 -->
    <property name="minIdle" value="10"/>
    <!-- 当连接池用尽后,调用者是否要等待,只有当为true时 maxWaitMillis 才生效 -->
    <property name="blockWhenExhausted" value="true"/>
    <property name="maxWaitMillis" value="300000"/>
    <!-- 使用前检查连接是否正常,剔除无效连接,在主从切换后可以做到自动重连 -->
    <property name="testOnBorrow" value="true"/>
    <property name="testOnReturn" value="true"/>
​
    <property name="timeBetweenEvictionRunsMillis" value="10000"/>
    <property name="minEvictableIdleTimeMillis" value="30000"/>
​
    <property name="jmxEnabled" value="true"/>
  </bean>
​
  <bean id="jedisCluster" class="redis.clients.jedis.JedisCluster">
    <constructor-arg index="0">
      <set>
        <bean class="redis.clients.jedis.HostAndPort">
          <constructor-arg index="0" value="${redis.cluster.host1}"/>
          <constructor-arg index="1" value="${redis.cluster.port1}"/>
        </bean>
        <bean class="redis.clients.jedis.HostAndPort">
          <constructor-arg index="0" value="${redis.cluster.host1}"/>
          <constructor-arg index="1" value="${redis.cluster.port2}"/>
        </bean>
        <bean class="redis.clients.jedis.HostAndPort">
          <constructor-arg index="0" value="${redis.cluster.host2}"/>
          <constructor-arg index="1" value="${redis.cluster.port1}"/>
        </bean>
        <bean class="redis.clients.jedis.HostAndPort">
          <constructor-arg index="0" value="${redis.cluster.host2}"/>
          <constructor-arg index="1" value="${redis.cluster.port2}"/>
        </bean>
        <bean class="redis.clients.jedis.HostAndPort">
          <constructor-arg index="0" value="${redis.cluster.host3}"/>
          <constructor-arg index="1" value="${redis.cluster.port1}"/>
        </bean>
        <bean class="redis.clients.jedis.HostAndPort">
          <constructor-arg index="0" value="${redis.cluster.host3}"/>
          <constructor-arg index="1" value="${redis.cluster.port2}"/>
        </bean>
      </set>
    </constructor-arg>
    <constructor-arg index="1" value="${redis.timeout}"/>
    <constructor-arg index="2" value="10000"/>
    <constructor-arg index="3" value="1000"/>
    <constructor-arg index="4" value="${redis.cluster.password}"/>
    <constructor-arg index="5" ref="jedisPoolConfig"/>
  </bean>

JedisPoolConfig中将testOnBorrowtestOnReturn属性的值设置成true,达到的效果与SpringBoot项目和SSM项目是一致的 ,唯一的区别是,当Redis集群不可用时,启动我们的服务会启动失败,因为服务在启动的时候,会去连接Redis服务。

另外,当我们Redis集群的master没有时,服务也是不可用的

  1. 在项目中使用jedisCluster操作Redis
csharp 复制代码
@Resource
private JedisCluster jedisCluster;
​
public String test() {
  jedisCluster.set("test", "This use JedisCluster............");
  return jedisCluster.get("test");
}

以上便是只使用jedis配置Redis集群和操作Redis,弊端是:当Redis集群不可用时,重启服务会重启失败;当Redis集群中master节点没有时,也会有问题,表示找不到主节点。

五. 总结

  1. 在SpringBoot项目和SSM项目中使用spring-*-redis来管理Redis是最友好的,当Redis集群不可用时,我们重启服务,是可以启动成功的,不会因为Redis集群不可用而导致我们的服务启动失败。
  2. 在SSM项目中,如果spring的版本低于4.2.*以下,同时我们的集群又有密码限制,spring-data-redis兼容的版本在2.0以下,那么此时不能使用spring-data-redis来操作Redis了,因为spring-data-redis2.0以下版本不支持集群配置密码。
  3. 不使用Spring来管理Redis,只使用jedis,使用这种方式,当集群不可用,或者集群中主节点有问题时,会导致我们的服务不能操作Redis,如果服务重启,则会启动失败。
  4. 经过测试对比,使用spring-boot-starter-data-redis来管理Redis的效果是最好的,集群不可用或者集群中某个节点(不管是主节点还是从节点)都不会影响服务重启;只有当整个集群不可用时,服务操作Redis才会失败。
  5. 在连接池中将testOnBorrowtestOnReturn配置成true,会有性能影响,但是如果不配置,又不能及时将不可用的连接移除。
相关推荐
IT大白鼠1 天前
Redis 系列 · 第 01 篇——认知入门:Redis 是什么
redis·nosql
彧azz1 天前
Linux 环境下 Redis 学习总结:数据类型、持久化、锁、事务、主从与缓存问题
linux·redis·笔记·学习·面试
lbb 小魔仙1 天前
OpenClaw + cpolar 实战:远程 NAS、分享小游戏、RDP,再配置公网 AI 入口
数据库·人工智能·redis·oracle·prometheus
夕除1 天前
redis--018
redis
两锭子1 天前
Redis基础
redis
烟沙九洲1 天前
Redis 缓存穿透、缓存击穿、缓存雪崩
数据库·redis
字节探索1 天前
Redis 只会当缓存用?这 10 大实战场景,让你的系统快到飞起
redis·后端
于樱花森上飞舞1 天前
【Redis】哨兵详解
java·开发语言·数据库·redis
Rain的Java大神之路2 天前
如何快速上传10G文件
java·spring boot·redis·后端·mysql·spring cloud·面试
Wang's Blog2 天前
Java 接入Redis: 通用命令与键管理
java·服务器·redis