关闭

Spring整合Redis之集群与故障转移

标签: redisspring集群故障
813人阅读 评论(0) 收藏 举报
分类:

前言

本文主要讲解spring整合redis集群,关于redis集群搭建网上相关文章挺多的,大家可以自己先搭建好,可以参考官网中文版官网。本文假设你已经搭建好集群了,笔者redis(版本4.0.1)集群环境如下图:

正常集群状态

7000、7001、7002三个主节点,7003、7004、7005三个从节点。Redis集群共有16384个哈希槽(hash slot)用于存放key,当前3个节点哈希槽分布为:

  • 节点 7000 包含 0 到 5460号哈希槽
  • 节点 7001 包含 5461 到 10922 号哈希槽
  • 节点 7002 包含 10923 到 16383 号哈希槽

spring整合redis cluster

step1、maven依赖引入

<dependencies>
    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-redis</artifactId>
        <version>1.8.6.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
        <version>2.9.0</version>
    </dependency>
</dependencies>

step2、配置ConnectionFactory

建立集群连接需要获取RedisClusterConnection实例,该实例通过RedisConnectionFactory获取,可以如下配置

<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
    <constructor-arg name="clusterConfig" ref="redisClusterConfiguration"/>
    <constructor-arg name="poolConfig" ref="jedisPoolConfig" />
</bean>

JedisConnectionFactory是RedisConnectionFactory的实现,构造函数需要两个参数,分别是集群配置clusterConfig和池配置poolConfig,下面一一来看

step3、配置JedisPoolConfig

<bean id="jedisPoolConfig"   class="redis.clients.jedis.JedisPoolConfig" >
    <property name="minIdle" value="${redis.minIdle}" />
    <property name="maxIdle" value="${redis.maxIdle}" />
    <property name="maxTotal" value="${redis.maxActive}" />
    <property name="maxWaitMillis" value="${redis.maxWait}" />
    <property name="testOnBorrow" value="${redis.testOnBorrow}" />
    <property name="testOnReturn" value="true" />
    <property name="testWhileIdle" value="true" />
</bean>

各个属性值从属性文件中读取,需要如下配置

<context:property-placeholder location="classpath:*.properties" ignore-unresolvable="true" />

属性文件spring-redis.properties内容

redis.minIdle=50
redis.maxIdle=200
redis.maxActive=100
redis.maxWait=3000
redis.testOnBorrow=true

step4、配置RedisClusterConfiguration

集群配置有两种方式

1.clusterNodes设值注入

<bean id="redisClusterConfiguration" class="org.springframework.data.redis.connection.RedisClusterConfiguration">
    <property name="clusterNodes">
      <set>
           <bean name="node1" class="org.springframework.data.redis.connection.RedisClusterNode">
               <constructor-arg name="host" value="192.168.48.28"/>
               <constructor-arg name="port" value="7000"/>
           </bean>
           <bean name="node2" class="org.springframework.data.redis.connection.RedisClusterNode">
               <constructor-arg name="host" value="192.168.48.28"/>
               <constructor-arg name="port" value="7001"/>
           </bean>
           <bean name="node3" class="org.springframework.data.redis.connection.RedisClusterNode">
               <constructor-arg name="host" value="192.168.48.28"/>
               <constructor-arg name="port" value="7002"/>
           </bean>
       </set>
    </property>
</bean>

2.propertySource构造注入

<bean id="redisClusterConfiguration" class="org.springframework.data.redis.connection.RedisClusterConfiguration">
    <constructor-arg name="propertySource" ref="propertySource"/>
</bean>

构造函数参数类型为org.springframework.core.env.PropertySource,我们使用它的实现类ResourcePropertySource

<bean name="propertySource" class="org.springframework.core.io.support.ResourcePropertySource">
    <constructor-arg name="location" value="classpath:spring-redis-cluster.properties" />
</bean>

spring-redis-cluster.properties内容:

#集群节点host:port,多个节点逗号分隔
spring.redis.cluster.nodes=192.168.48.28:7000,192.168.48.28:7001,192.168.48.28:7002
spring.redis.cluster.max-redirects=5

查看RedisClusterConfiguration源码可以看到这两个配置属性常量

RedisClusterConfiguration源码

个人比较喜欢第二种配置,更加简洁

step5、配置RedisTemplate

然后配置RedisTemplate,我们用StringRedisTemplate实现

<bean id="stringRedisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate" p:connection-factory-ref="jedisConnectionFactory"/>

step6、测试

通过main方法测试一下

package example;

import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;

public class SpringMain {

    public static void main(String[] args) throws InterruptedException {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring-redis.xml");
        StringRedisTemplate template = context.getBean(StringRedisTemplate.class);
        ValueOperations<String, String> valueOperations = template.opsForValue();
        valueOperations.set("key1","value1");
        valueOperations.set("key2","value2");
        valueOperations.set("key3","value3");
        valueOperations.set("key4","value4");
        valueOperations.set("key5","value5");
        valueOperations.set("key6","value6");
        System.exit(0);
    }
}

通过命令行连接redis服务器查看结果:

测试结果

每个key通过CRC16(详情参考官网)校验后对16384取模放到对应哈希槽(hash slot)中。redis-cli客户端工具会根据哈希槽(hash slot)所在节点进行跳转,所以会看到“-> Redirected to slot…”的提示。
最后贴下完整配置:

<?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:redis="http://www.springframework.org/schema/redis"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/redis http://www.springframework.org/schema/redis/spring-redis.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:property-placeholder location="classpath:*.properties" ignore-unresolvable="true" />

    <!-- Jedis ConnectionFactory -->
    <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <constructor-arg name="clusterConfig" ref="redisClusterConfiguration"/>
        <constructor-arg name="poolConfig" ref="jedisPoolConfig" />
    </bean>

    <!-- RedisClusterConfiguration -->
    <bean id="redisClusterConfiguration" class="org.springframework.data.redis.connection.RedisClusterConfiguration">
        <constructor-arg name="propertySource" ref="propertySource"/>
      <!--  <property name="clusterNodes">
           <set>
               <bean name="node1" class="org.springframework.data.redis.connection.RedisClusterNode">
                   <constructor-arg name="host" value="192.168.48.28"/>
                   <constructor-arg name="port" value="7000"/>
               </bean>
               <bean name="node2" class="org.springframework.data.redis.connection.RedisClusterNode">
                   <constructor-arg name="host" value="192.168.48.28"/>
                   <constructor-arg name="port" value="7001"/>
               </bean>
               <bean name="node3" class="org.springframework.data.redis.connection.RedisClusterNode">
                   <constructor-arg name="host" value="192.168.48.28"/>
                   <constructor-arg name="port" value="7002"/>
               </bean>
           </set>
        </property>-->
    </bean>

    <bean name="propertySource" class="org.springframework.core.io.support.ResourcePropertySource">
        <constructor-arg name="location" value="classpath:spring-redis-cluster.properties" />
    </bean>

    <!-- JedisPoolConfig definition -->
    <bean id="jedisPoolConfig"   class="redis.clients.jedis.JedisPoolConfig" >
        <property name="minIdle" value="${redis.minIdle}" />
        <property name="maxIdle" value="${redis.maxIdle}" />
        <property name="maxTotal" value="${redis.maxActive}" />
        <property name="maxWaitMillis" value="${redis.maxWait}" />
        <property name="testOnBorrow" value="${redis.testOnBorrow}" />
        <property name="testOnReturn" value="true" />
        <property name="testWhileIdle" value="true" />
    </bean>

    <!-- redis template definition -->
    <bean id="stringRedisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate" p:connection-factory-ref="jedisConnectionFactory"/>

</beans>

redis集群进阶

故障转移

现在我们来测试下故障转移,通过向端口号为7002的主节点发送DEBUG SEGFAULT命令, 让这个主节点崩溃:

$ redis-cli -p 7002 debug segfault

查看现在集群节点状态:

7002fail

可以看到7002状态为fail,他的从节点7005被选举为新的主节点
根据前面的测试我们知道键key4被“hash”到了13120哈希槽,位于7002节点,现在7002挂了,他会放到哪呢?

ValueOperations<String, String> valueOperations = template.opsForValue();
valueOperations.set("key4","bar4");

7002fail 后故障转移

可以看见,13120哈希槽已经转移到新的主节点7005上了

主从复制模型

如果上步新的主节点7005也挂了会出现什么情况呢?我们让这个节点崩溃:

$ redis-cli -p 7005 debug segfault

make 7005 fail command

查看现在集群节点状态:

7002 and 7005 both fail

可以看到7005的状态也为fail,现在通过spring redis发送命令,会报连接异常

Exception in thread "main" org.springframework.data.redis.RedisConnectionFailureException: Could not get a resource from the pool; nested exception is redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
    at org.springframework.data.redis.connection.jedis.JedisExceptionConverter.convert(JedisExceptionConverter.java:67)
    at org.springframework.data.redis.connection.jedis.JedisExceptionConverter.convert(JedisExceptionConverter.java:41)
    at org.springframework.data.redis.PassThroughExceptionTranslationStrategy.translate(PassThroughExceptionTranslationStrategy.java:37)
    at org.springframework.data.redis.connection.jedis.JedisClusterConnection.convertJedisAccessException(JedisClusterConnection.java:3999)
    at org.springframework.data.redis.connection.jedis.JedisClusterConnection.set(JedisClusterConnection.java:642)
    at org.springframework.data.redis.connection.DefaultStringRedisConnection.set(DefaultStringRedisConnection.java:744)
    at org.springframework.data.redis.core.DefaultValueOperations$10.inRedis(DefaultValueOperations.java:172)
    at org.springframework.data.redis.core.AbstractOperations$ValueDeserializingRedisCallback.doInRedis(AbstractOperations.java:57)
    at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:207)
    at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:169)
    at org.springframework.data.redis.core.AbstractOperations.execute(AbstractOperations.java:91)
    at org.springframework.data.redis.core.DefaultValueOperations.set(DefaultValueOperations.java:169)
    at example.SpringMain.main(SpringMain.java:26)

再看看通过命令行向集群发送命令:

cluster down

发现整个集群已经宕掉了,根据redis主从复制模型,当某个主节点和他的所有从节点都挂掉,集群会因为缺少某一范围的哈希槽而不用,如上集群7002主节点7005从节点都挂了,集群因缺少10923-16383范围哈希槽而变的不可用。

参考

spring-data-redis cluster:https://docs.spring.io/spring-data/redis/docs/2.0.0.RC2/reference/html/#cluster

中文官网Redis集群教程:http://www.redis.cn/topics/cluster-tutorial.html

1
0
查看评论

Redis RedisCluster Spring整合

前言: 在上一篇文章中,用了jedisCluster来做redis集群的客户端,这一篇我们介绍一下spring-data-redis给我们提供的操作redis集群的redisTemplate,着急用的可以跳过1,直接看2 准备工作: jdk版本:1.8 junit版本:4.12 jar包版本: ...
  • fengyong7723131
  • fengyong7723131
  • 2016-11-01 15:20
  • 10136

spring-boot配置redis cluster

只需简单两步,便可以配置好redis cluster连接,然后方便地使用RedisTemplate来存取数据。
  • ClementAD
  • ClementAD
  • 2016-10-28 10:19
  • 11039

spring 整合 redis cluster集群

搭建好redis cluster 集群之后,项目中使用起来很方便,只需要少量的配置 。 代码下载地址 http://download.csdn.net/detail/wangzhi291/9750657 新建一个配置文件redis.properties #redis中心   #re...
  • wangzhi291
  • wangzhi291
  • 2017-02-09 11:24
  • 1916

Redis-3.x集群配置(RedisCluster+SpringBoot+Jedis)

Redis-3.2.4集群配置(RedisCluster+SpringBoot+Jedis)
  • zhe1110
  • zhe1110
  • 2016-11-01 09:51
  • 10469

spring集成redisCluster

简介 在spring框架的web项目中使用redisTemplate操作redis cluster。参考文档:http://www.cnblogs.com/moonandstar08/p/5149585.html。 依赖版本 spring版本:4.1.3.RELEASE。 spri...
  • u014196729
  • u014196729
  • 2016-04-22 16:59
  • 2695

Spring Data Redis Redis集群---笔记5

继续。。。 6.Redis Cluster(Redis集群) Redis集群需要版本3.0+,它提供一些特性和集群的能力,详情参考Redis官网 注意: Redis 集群目前只支持 jedis和lettuce。 6.1.Enabling Redis Cluster(开启redis集群) 集群支持是基...
  • m0_37355951
  • m0_37355951
  • 2017-08-03 11:24
  • 393

Spring Redis(5)Redis集群

Redis集群Redis Cluster 支持Redi3.0+连接集群配置@Component @ConfigurationProperties(prefix = "spring.redis.cluster") public class ClusterConfigurationP...
  • supermancoke
  • supermancoke
  • 2017-04-11 14:57
  • 554

spring集成jedis支持redis3.0集群

通过spring FactoryBean实现redis 3.0集群JedisCluster与spring集成。
  • tianwei7518
  • tianwei7518
  • 2015-11-07 00:03
  • 6214

四:redis主从读写分离,哨兵模式与spring的集成

本篇主要介绍redis实现主从读写分离,采用哨兵模式后如何与spring集成在项目中进行开发 主要依赖的jar包,其他项目中实际依赖的jar包很多这里就不一一列举了: jar包依赖 <dependency> <groupId>redis.clients<...
  • zyshappy
  • zyshappy
  • 2017-07-16 17:31
  • 2177

Spring+SpringMVC做Redis集群(Sentinel模式)

研究Redis也有一段时间了,在前面的Redis系列文章中,介绍了Redis的安装,集群配置,及节点的增加和删除,但是并未实际的使用到项目中,趁这周末时间,参照项目中实际的使用场景,做了一个Redis集群Spring整合的案例,在介绍案例之前,先简单介绍下Redis集群的方式有哪些 1、单机版 ...
  • kity9420
  • kity9420
  • 2016-12-11 10:35
  • 4106
    个人资料
    • 访问:72568次
    • 积分:1090
    • 等级:
    • 排名:千里之外
    • 原创:33篇
    • 转载:2篇
    • 译文:0篇
    • 评论:26条
    友情链接
    文章分类
    最新评论