Spring整合Redis之哨兵与故障转移

港控/mmm° 2022-06-10 04:28 395阅读 0赞

前言

上篇博客谈到了Spring整合redis集群以及故障转移演示,会发现redis集群模式存在一个很明显的问题:当某个主节点及其所有从节点挂掉,整个集群因为缺少该节点负责范围的哈希槽(hash slot)而宕掉,不具高可用性。redis引入了哨兵(sentinel)模式,能很好解决集群模式存在的不足。引用官网,redis哨兵系统有三个作用:

  • 监控(Monitoring): Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。
  • 提醒(Notification): 当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。
  • 自动故障迁移(Automatic failover): 当一个主服务器不能正常工作时, Sentinel 会开始一次自动故障迁移操作, 它会将失效主服务器的其中一个从服务器升级为新的主服务器, 并让失效主服务器的其他从服务器改为复制新的主服务器; 当客户端试图连接失效的主服务器时, 集群也会向客户端返回新主服务器的地址, 使得集群可以使用新主服务器代替失效服务器。

本文主要讲解spring整合redis sentinel及故障转移示例,以此加深理解。

环境准备

笔者已经预先搭建好Redis Sentinel,环境如下:

  • OS:CentOS release 6.5 (Final)
  • Redis版本:4.0.1








































role ip port
Redis Master 192.168.48.31 6379
Redis Slave1 192.168.48.32 6379
Redis Slave0 192.168.48.33 6379
Redis Sentinel 192.168.48.31 26379
Redis Sentinel 192.168.48.32 26379
Redis Sentinel 192.168.48.33 26379

客户端连接主节点,查看Redis Master相关信息

  1. redis-cli -h 192.168.48.31 info Replication

Redis Master相关信息

Redis Sentinel启动信息

Redis Sentinel启动信息

客户端连接sentinel节点,查看Redis Sentinel相关信息

  1. redis-cli -h 192.168.48.31 -p 26379 info Sentinel

Redis Sentinel相关信息

spring整合redis sentinel

step1、maven依赖引入

  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework.data</groupId>
  4. <artifactId>spring-data-redis</artifactId>
  5. <version>1.8.6.RELEASE</version>
  6. </dependency>
  7. <dependency>
  8. <groupId>redis.clients</groupId>
  9. <artifactId>jedis</artifactId>
  10. <version>2.9.0</version>
  11. </dependency>
  12. </dependencies>

step2、配置ConnectionFactory

构造JedisConnectionFactory实例,注入池配置poolConfig和哨兵配置sentinelConfig

  1. <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
  2. <constructor-arg name="sentinelConfig" ref="redisSentinelConfiguration"/>
  3. <constructor-arg name="poolConfig" ref="jedisPoolConfig" />
  4. </bean>

step3、配置JedisPoolConfig

  1. <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig" >
  2. <property name="minIdle" value="${redis.minIdle}" />
  3. <property name="maxIdle" value="${redis.maxIdle}" />
  4. <property name="maxTotal" value="${redis.maxActive}" />
  5. <property name="maxWaitMillis" value="${redis.maxWait}" />
  6. <property name="testOnBorrow" value="${redis.testOnBorrow}" />
  7. <property name="testOnReturn" value="true" />
  8. <property name="testWhileIdle" value="true" />
  9. </bean>

配置文件spring-redis.properties内容

  1. redis.minIdle=50
  2. redis.maxIdle=200
  3. redis.maxActive=100
  4. redis.maxWait=3000
  5. redis.testOnBorrow=true

step4、配置RedisSentinelConfiguration

和集群配置一样,sentinel也有两种配置方式

1.sentinels设值注入

  1. <bean id="redisSentinelConfiguration" class="org.springframework.data.redis.connection.RedisSentinelConfiguration">
  2. <property name="sentinels">
  3. <set>
  4. <bean name="sentinelNode1" class="org.springframework.data.redis.connection.RedisNode">
  5. <constructor-arg name="host" value="192.168.48.31"/>
  6. <constructor-arg name="port" value="26379"/>
  7. </bean>
  8. <bean name="sentinelNode2" class="org.springframework.data.redis.connection.RedisNode">
  9. <constructor-arg name="host" value="192.168.48.32"/>
  10. <constructor-arg name="port" value="26379"/>
  11. </bean>
  12. <bean name="sentinelNode3" class="org.springframework.data.redis.connection.RedisNode">
  13. <constructor-arg name="host" value="192.168.48.33"/>
  14. <constructor-arg name="port" value="26379"/>
  15. </bean>
  16. </set>
  17. </property>
  18. <property name="master">
  19. <bean name="masterNode" class="org.springframework.data.redis.connection.RedisNode">
  20. <!--必须指定主节点名称-->
  21. <property name="name" value="mymaster"/>
  22. <constructor-arg name="host" value="192.168.48.31"/>
  23. <constructor-arg name="port" value="6379"/>
  24. </bean>
  25. </property>
  26. </bean>

2.propertySource构造注入

  1. <bean id="redisSentinelConfiguration" class="org.springframework.data.redis.connection.RedisSentinelConfiguration">
  2. <constructor-arg name="propertySource" ref="propertySource"/>
  3. </bean>
  4. <bean name="propertySource" class="org.springframework.core.io.support.ResourcePropertySource">
  5. <constructor-arg name="location" value="classpath:spring-redis-sentinel.properties" />
  6. </bean>

spring-redis-sentinel.properties内容:

  1. #哨兵监控主redis节点名称,必选
  2. spring.redis.sentinel.master=mymaster
  3. #哨兵节点
  4. spring.redis.sentinel.nodes=192.168.48.31:26379,192.168.48.32:26379,192.168.48.33:26379

RedisSentinelConfiguration源码中这两个配置属性常量:

  1. private static final String REDIS_SENTINEL_MASTER_CONFIG_PROPERTY = "spring.redis.sentinel.master";
  2. private static final String REDIS_SENTINEL_NODES_CONFIG_PROPERTY = "spring.redis.sentinel.nodes";

RedisSentinelConfiguration源码

step5、配置RedisTemplate

然后配置RedisTemplate,用于redis命令操作

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

step6、测试

  1. package example;
  2. import org.springframework.context.support.ClassPathXmlApplicationContext;
  3. import org.springframework.data.redis.core.StringRedisTemplate;
  4. import org.springframework.data.redis.core.ValueOperations;
  5. public class SpringMain {
  6. public static void main(String[] args) throws InterruptedException {
  7. ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring-redis.xml");
  8. StringRedisTemplate template = context.getBean(StringRedisTemplate.class);
  9. ValueOperations<String, String> valueOperations = template.opsForValue();
  10. valueOperations.set("foo","bar");
  11. context.destroy();
  12. }
  13. }

由于主从复制,Master(48.31)、Slave1(48.32)、Slave0(48.33)能查看到相同的结果:

master的foo值
slave1的foo值
slave2的foo值

到此,spring整合redis sentinel完成,完整配置如下:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <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">
  3. <context:property-placeholder location="classpath:*.properties" ignore-unresolvable="true" />
  4. <!-- Jedis ConnectionFactory -->
  5. <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
  6. <constructor-arg name="sentinelConfig" ref="redisSentinelConfiguration"/>
  7. <constructor-arg name="poolConfig" ref="jedisPoolConfig" />
  8. </bean>
  9. <bean id="redisSentinelConfiguration" class="org.springframework.data.redis.connection.RedisSentinelConfiguration">
  10. <constructor-arg name="propertySource" ref="propertySource"/>
  11. <!-- <property name="sentinels"> <set> <bean name="sentinelNode1" class="org.springframework.data.redis.connection.RedisNode"> <constructor-arg name="host" value="192.168.48.31"/> <constructor-arg name="port" value="26379"/> </bean> <bean name="sentinelNode2" class="org.springframework.data.redis.connection.RedisNode"> <constructor-arg name="host" value="192.168.48.32"/> <constructor-arg name="port" value="26379"/> </bean> <bean name="sentinelNode3" class="org.springframework.data.redis.connection.RedisNode"> <constructor-arg name="host" value="192.168.48.33"/> <constructor-arg name="port" value="26379"/> </bean> </set> </property> <property name="master"> <bean name="masterNode" class="org.springframework.data.redis.connection.RedisNode"> <!–必须指定主节点名称–> <property name="name" value="mymaster"/> <constructor-arg name="host" value="192.168.48.31"/> <constructor-arg name="port" value="6379"/> </bean> </property>-->
  12. </bean>
  13. <bean name="propertySource" class="org.springframework.core.io.support.ResourcePropertySource">
  14. <constructor-arg name="location" value="classpath:spring-redis-sentinel.properties" />
  15. </bean>
  16. <!-- JedisPoolConfig definition -->
  17. <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig" >
  18. <property name="minIdle" value="${redis.minIdle}" />
  19. <property name="maxIdle" value="${redis.maxIdle}" />
  20. <property name="maxTotal" value="${redis.maxActive}" />
  21. <property name="maxWaitMillis" value="${redis.maxWait}" />
  22. <property name="testOnBorrow" value="${redis.testOnBorrow}" />
  23. <property name="testOnReturn" value="true" />
  24. <property name="testWhileIdle" value="true" />
  25. </bean>
  26. <!-- redis template definition -->
  27. <bean id="stringRedisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate" p:connection-factory-ref="jedisConnectionFactory"/>

sentinel故障转移

为验证故障转移,我们关闭Master节点

  1. shutdown

关闭Master节点

Sentinel监控日志:

Sentinel监控日志

简单解释下日志信息:

  • +sdown 表示监控节点主观下线(单个Sentinel实例对服务器做出的下线判断)
  • +odown 表示监控节点客观下线(多个Sentinel实例对服务器做出的sdown判断),sentinel.config中配置达到2个sdown(quorum参数配置)便判断为客观下线
  • +switch-master 从slave节点中选个作为新的主节点,Slave1(192.168.48.32:6379)

查看该节点信息,发现他的角色已经变为master,原先的Slave0(192.168.48.33:6379)变成新master的slave:

  1. redis-cli -h 192.168.48.32 info Replication

新Master节点

接下来再来测试一种情况:在故障节点重启之前发送写命令,故障节点重启后数据是否会复制过来?

  1. valueOperations.set("foo1","bar1");

结果:

新master的foo1值

重启原来的Master节点(192.168.48.31:6379)

重启原Master节点后

原先的Master重启后变成了新Master的从节点了,试着查看foo1的值

foo1值同步过来了

可见,重启后,新的从节点会复制新的master节点,保证数据一致性。

参考

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

中文官网:http://www.redis.cn/topics/sentinel.html

发表评论

表情:
评论列表 (有 0 条评论,395人围观)

还没有评论,来说两句吧...

相关阅读