likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 31

一. 背景

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

二. SpringBoot项目中使用

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

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

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

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

  1. 在项目的pom文件中引入依赖:
<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文件,所以信息如下:
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加载配置类,配置类如下:
@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 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。
  @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框架整合,就不介绍了。
<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的配置信息,信息如下:
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 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文件中。
<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。
@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的依赖。
<dependencies>
  <dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
  </dependency>
</dependencies>
  1. 创建application.properties文件。
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 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
@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,会有性能影响,但是如果不配置,又不能及时将不可用的连接移除。