概述
Redis主从模式当主服务器右机后,需要手动把一台从服务器切换为主从服务器,这就需要人工干预,既费时费力,会造成一段时间内服务不可用,不推荐使用。 可以使用哨兵模式或者集群模式。 这里我们来探讨哨兵模式。
Redis 可以存在多台服务器,并且实现了主从复制的功能。哨兵模式是一种特殊的模式,首先 Redis 提供了哨兵的命令,哨兵是一个独立 的进程,作为进程,它会独立运行。原理是哨兵通过发送命令, 等待 Redis 服务器响应,从而监控运行的多个 Redis 实例。
这里哨兵有两个作用
通过发送命令,让 Redis 服务器返回监测其运行状态,包括主服务器和从服务器。
当 哨兵监测到 master宕机, 会自动将 slave 切换成 master,然后通过发布订阅模式通知到其他的从服务器,修改配置文件,让它们切换主机 。
只是现实中一个哨兵进程对 Redis 服务器进行监控,也可能出现问题,为了处理这个问题,还可以使用多个哨兵的监控,而各个哨兵之间还会相互监控,这样就变为了多个哨兵模式。多个哨兵不仅监控各个 Redis 服务器,而且哨兵之间互相监控 , 看看哨兵们是否还“活 ” 着。 如下图
故障切换( failover)的过程:
假设主服务器岩机,哨兵 1 先监测到这个结果,当时系统并不会马上进行 failover 操作 ,而仅仅是哨兵1 主观地认为主机己经不可用,这个现象被称为主观下线。【SDOWN】
当后面的哨兵监测也监测到了主服务器不可用 , 并且有了 一定数量的哨兵认为主服务器不可用,那么哨兵之间就会形成一次投票,投票的结果由一个哨兵发起,进行 failover 操作,在 failover 操作的过程中切换成功后,就会通过发布订阅方式,让各个哨兵把自己监控的服务器实现切换主机 , 这个过程被称为客观下线【ODOWN】
这样对于客户端而言, 一切都是透明的 。
环境
redis版本: 4.0.11
操作系统: Centos6.5
主机信息 :
Sentinel环境搭建
如果没有足够的主机,那我们就通过VMware来搭建多个主机模拟下集群环境
参考我博文 VMware-使用VMware在本地搭建多个CentOS虚机
同时需要在3台主机上部署Redis,参考我博 Redis-02Redis在linux下的安装及常见问题
完成上面两步之后 ,就可以着手调整部署了
防火墙策略的调整
确保redis和哨兵进程的端口,在防火墙上开放。
redis的默认6379端口,在防火墙上开放,可以在本地通过RedisDeskTopManager来登录
哨兵的默认26379端口,在防火墙上开放,确保哨兵之间的通信。
这里为了方便起见,选择了关闭防火墙。
CentOS6.5永久关闭防火墙
使用root用户
[root@artisan ~]# service iptables stop [root@artisan ~]# service iptables status iptables: Firewall is not running [root@artisan ~]# chkconfig iptables off [root@artisan ~]#
配置修改【最少修改】 (Redis集群带访问密码)
这里说的是Redis集群配置了访问密码的场景。
Redis 的主服务器的配置修改
登录192.168.31.66主机
仅说需要调整的地方,未说明的地方保持默认即可。
#使得 Redis 服务器可以跨网络访问 bind 0.0.0.0 #设置密码 requirepass artisan #主服务器密码 masterauth artisan
因为发生切换时预先设定的master会变成slave,下次再发生故障的时候,如果没有masterauth artisan
会失败。
为了方便观察日志,我们这里 daemonize no
暂时没有修改。
Redis 的从服务器的配置修改
#使得 Redis 服务器可以跨网络访问 bind 0.0.0.0 #设置密码 requirepass artisan #指定主服务器,注意:有关 slaveof 的配置只是配置从服务器,而主服务器不需要配置 slaveof 192.168.31.56 637 #主服务器密码 , 注意:有关 slaveof 的配置只是配置从服务器,而主服务器不需要配置 masterauth artisan
上述内容主要是配置 Redis 服务器,从服务器比主服务器多一个 slaveof 的配置和密码 ,这里配置的 bind 使得 Redis 服务器可以跨网段访问 。 而对于外部的访问还需要提供密码,因此还提供了 requirepass 的配置,用以配置密码 ,这样就配置完了 3 台服务器 。
哨兵sentinel.conf 的配置
配置 3 个哨 兵 , 每一个哨兵的配置都是一样的 ,在 Redis 安装目录下可以找到sentinel.conf 文件,然后对其进行修改
下面对 3 个哨兵的这个文件作出修改,同样也是在原有的基础上进行修改,仅仅列出改动的地方,如下所示
# 禁止保护模式 protected-mode no # 配置监听的主服务器,这里 sentinel monitor 代表监控 , # mymaster 代表服务器名称,可以自定义 # 192.168.31.66 代表监控的主服务器 # 6379 代表端口 # 2 代表只有两个或者两个以上的哨兵认为主服务器不可用的时候,才会做故障切换操作 sentinel monitor mymaster 192.168.31.66 6379 2 # sentinel auth-pass 定义服务的密码 # mymaster 服务名称 # artisan Redis 服务器密码 sentinel auth-pass mymaster artisan
关闭了保护模式,以便测试。 sentinel monitor 是配置一个哨兵主要的内 容 , 首先是自定义服务名称 mymaster,然后映射服务器和端口 。最后的 2 是代表当存在两个或者两个以上的哨兵投票认可当前主服务器不可用后 ,才会进行故障切换,这样可以降低因出错而切换主服务器的概率。 sentinel auth-pass 用于配置服务名称及其密码。
启动顺序
首先是主机 192.168.31.66的 Redis 服务进程 ,然后启动从机的服务进程,最后启动 3 个哨兵的服务进程
哨兵进程启动命令,指定配置文件
./redis-sentinel ../etc/sentinel.conf
测试
写这篇博客的时候,已经模拟过一次主节点故障了,主节点已经被为了 192.168.31.56了 . 所以接着这里继续来看下。
首先启动 Redis主服务器的Redis服务 – > Redis从服务器的Redis服务(2个)。
然后启动 每台主机上对应的哨兵进程
56主机
[redis@artisan bin]$ ./redis-sentinel ../etc/sentinel.conf 2128:X 30 Sep 02:07:23.601 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo 2128:X 30 Sep 02:07:23.602 # Redis version=4.0.11, bits=64, commit=00000000, modified=0, pid=2128, just started 2128:X 30 Sep 02:07:23.602 # Configuration loaded 2128:X 30 Sep 02:07:23.607 * Increased maximum number of open files to 4096 (it was originally set to 1024). _._ _.-``__ ''-._ _.-`` `. `_. ''-._ Redis 4.0.11 (00000000/0) 64 bit .-`` .-```. ```\/ _.,_ ''-._ ( ' , .-` | `, ) Running in sentinel mode |`-._`-...-` __...-.``-._|'` _.-'| Port: 26379 | `-._ `._ / _.-' | PID: 2128 `-._ `-._ `-./ _.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | http://redis.io `-._ `-._`-.__.-'_.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | `-._ `-._`-.__.-'_.-' _.-' `-._ `-.__.-' _.-' `-._ _.-' `-.__.-' 2128:X 30 Sep 02:07:23.611 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128. 2128:X 30 Sep 02:07:23.611 # Sentinel ID is db0d6183683b97f001514c53e36ceabd2bcf2bb4 2128:X 30 Sep 02:07:23.611 # +monitor master mymaster 192.168.31.56 6379 quorum 2
66主机
[redis@artisan bin]$ ./redis-sentinel ../etc/sentinel.conf 2696:X 30 Sep 02:06:52.261 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo 2696:X 30 Sep 02:06:52.261 # Redis version=4.0.11, bits=64, commit=00000000, modified=0, pid=2696, just started 2696:X 30 Sep 02:06:52.261 # Configuration loaded 2696:X 30 Sep 02:06:52.262 * Increased maximum number of open files to 4096 (it was originally set to 1024). _._ _.-``__ ''-._ _.-`` `. `_. ''-._ Redis 4.0.11 (00000000/0) 64 bit .-`` .-```. ```\/ _.,_ ''-._ ( ' , .-` | `, ) Running in sentinel mode |`-._`-...-` __...-.``-._|'` _.-'| Port: 26379 | `-._ `._ / _.-' | PID: 2696 `-._ `-._ `-./ _.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | http://redis.io `-._ `-._`-.__.-'_.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | `-._ `-._`-.__.-'_.-' _.-' `-._ `-.__.-' _.-' `-._ _.-' `-.__.-' 2696:X 30 Sep 02:06:52.269 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128. 2696:X 30 Sep 02:06:52.269 # Sentinel ID is 06f62ff2d7739c81c83bbca1864a6765ca67ab36 2696:X 30 Sep 02:06:52.269 # +monitor master mymaster 192.168.31.56 6379 quorum 2
176主机
[redis@artisan ~]$ cd redis/redis-4.0.11/bin/ [redis@artisan bin]$ ./redis-sentinel ../etc/sentinel.conf 2211:X 30 Sep 02:07:44.182 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo 2211:X 30 Sep 02:07:44.182 # Redis version=4.0.11, bits=64, commit=00000000, modified=0, pid=2211, just started 2211:X 30 Sep 02:07:44.182 # Configuration loaded 2211:X 30 Sep 02:07:44.183 * Increased maximum number of open files to 4096 (it was originally set to 1024). _._ _.-``__ ''-._ _.-`` `. `_. ''-._ Redis 4.0.11 (00000000/0) 64 bit .-`` .-```. ```\/ _.,_ ''-._ ( ' , .-` | `, ) Running in sentinel mode |`-._`-...-` __...-.``-._|'` _.-'| Port: 26379 | `-._ `._ / _.-' | PID: 2211 `-._ `-._ `-./ _.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | http://redis.io `-._ `-._`-.__.-'_.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | `-._ `-._`-.__.-'_.-' _.-' `-._ `-.__.-' _.-' `-._ _.-' `-.__.-' 2211:X 30 Sep 02:07:44.188 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128. 2211:X 30 Sep 02:07:44.188 # Sentinel ID is e95574e02478706f3cd27b8022340523d3a09e6d 2211:X 30 Sep 02:07:44.188 # +monitor master mymaster 192.168.31.56 6379 quorum 2
然后停掉Master 56的redis-server进程,观察哨兵日志的选举
56的哨兵输出
2128:X 30 Sep 02:09:37.657 # +sdown master mymaster 192.168.31.56 6379 2128:X 30 Sep 02:09:37.709 # +odown master mymaster 192.168.31.56 6379 #quorum 3/2 2128:X 30 Sep 02:09:37.710 # +new-epoch 2 2128:X 30 Sep 02:09:37.710 # +try-failover master mymaster 192.168.31.56 6379 2128:X 30 Sep 02:09:37.721 # +vote-for-leader db0d6183683b97f001514c53e36ceabd2bcf2bb4 2 2128:X 30 Sep 02:09:37.730 # e95574e02478706f3cd27b8022340523d3a09e6d voted for db0d6183683b97f001514c53e36ceabd2bcf2bb4 2 2128:X 30 Sep 02:09:37.746 # 06f62ff2d7739c81c83bbca1864a6765ca67ab36 voted for db0d6183683b97f001514c53e36ceabd2bcf2bb4 2 2128:X 30 Sep 02:09:37.777 # +elected-leader master mymaster 192.168.31.56 6379 2128:X 30 Sep 02:09:37.777 # +failover-state-select-slave master mymaster 192.168.31.56 6379 2128:X 30 Sep 02:09:37.861 # +selected-slave slave 192.168.31.176:6379 192.168.31.176 6379 @ mymaster 192.168.31.56 6379 2128:X 30 Sep 02:09:37.861 * +failover-state-send-slaveof-noone slave 192.168.31.176:6379 192.168.31.176 6379 @ mymaster 192.168.31.56 6379 2128:X 30 Sep 02:09:37.961 * +failover-state-wait-promotion slave 192.168.31.176:6379 192.168.31.176 6379 @ mymaster 192.168.31.56 6379 2128:X 30 Sep 02:09:38.822 # +promoted-slave slave 192.168.31.176:6379 192.168.31.176 6379 @ mymaster 192.168.31.56 6379 2128:X 30 Sep 02:09:38.822 # +failover-state-reconf-slaves master mymaster 192.168.31.56 6379 2128:X 30 Sep 02:09:38.879 * +slave-reconf-sent slave 192.168.31.66:6379 192.168.31.66 6379 @ mymaster 192.168.31.56 6379 2128:X 30 Sep 02:09:39.817 * +slave-reconf-inprog slave 192.168.31.66:6379 192.168.31.66 6379 @ mymaster 192.168.31.56 6379 2128:X 30 Sep 02:09:39.818 * +slave-reconf-done slave 192.168.31.66:6379 192.168.31.66 6379 @ mymaster 192.168.31.56 6379 2128:X 30 Sep 02:09:39.891 # -odown master mymaster 192.168.31.56 6379 2128:X 30 Sep 02:09:39.891 # +failover-end master mymaster 192.168.31.56 6379 2128:X 30 Sep 02:09:39.891 # +switch-master mymaster 192.168.31.56 6379 192.168.31.176 6379 2128:X 30 Sep 02:09:39.894 * +slave slave 192.168.31.66:6379 192.168.31.66 6379 @ mymaster 192.168.31.176 6379 2128:X 30 Sep 02:09:39.895 * +slave slave 192.168.31.56:6379 192.168.31.56 6379 @ mymaster 192.168.31.176 6379 2128:X 30 Sep 02:10:09.920 # +sdown slave 192.168.31.56:6379 192.168.31.56 6379 @ mymaster 192.168.31.176 6379
+sdown 表示哨兵主观认为数据库下线
+odown 表示哨兵客观认为数据库下线
+try-failover 表示哨兵开始进行故障恢复
+failover-end 表示哨兵完成故障修复,其中包括了领头哨兵的选举、备选从数据库的选择等等较为复杂的过程
+switch-master表示主数据库从56服务器迁移到176服务器
+slave列出了新的主数据库的2个从数据库,而哨兵并没有彻底清除56服务器的实力信息,这是因为停止的实例有可能会在将来恢复,哨兵会让其重新加入进来
66的哨兵输入
2696:X 30 Sep 02:09:21.731 # +sdown master mymaster 192.168.31.56 6379 2696:X 30 Sep 02:09:21.848 # +new-epoch 2 2696:X 30 Sep 02:09:21.853 # +vote-for-leader db0d6183683b97f001514c53e36ceabd2bcf2bb4 2 2696:X 30 Sep 02:09:22.838 # +odown master mymaster 192.168.31.56 6379 #quorum 3/2 2696:X 30 Sep 02:09:22.838 # Next failover delay: I will not start a failover before Sun Sep 30 02:15:22 2018 2696:X 30 Sep 02:09:22.990 # +config-update-from sentinel db0d6183683b97f001514c53e36ceabd2bcf2bb4 192.168.31.56 26379 @ mymaster 192.168.31.56 6379 2696:X 30 Sep 02:09:22.990 # +switch-master mymaster 192.168.31.56 6379 192.168.31.176 6379 2696:X 30 Sep 02:09:22.993 * +slave slave 192.168.31.66:6379 192.168.31.66 6379 @ mymaster 192.168.31.176 6379 2696:X 30 Sep 02:09:22.993 * +slave slave 192.168.31.56:6379 192.168.31.56 6379 @ mymaster 192.168.31.176 6379 2696:X 30 Sep 02:09:53.008 # +sdown slave 192.168.31.56:6379 192.168.31.56 6379 @ mymaster 192.168.31.176 6379
176的哨兵输出
2211:X 30 Sep 02:09:45.243 # +sdown master mymaster 192.168.31.56 6379 2211:X 30 Sep 02:09:45.336 # +new-epoch 2 2211:X 30 Sep 02:09:45.339 # +vote-for-leader db0d6183683b97f001514c53e36ceabd2bcf2bb4 2 2211:X 30 Sep 02:09:45.341 # +odown master mymaster 192.168.31.56 6379 #quorum 2/2 2211:X 30 Sep 02:09:45.341 # Next failover delay: I will not start a failover before Sun Sep 30 02:15:45 2018 2211:X 30 Sep 02:09:46.492 # +config-update-from sentinel db0d6183683b97f001514c53e36ceabd2bcf2bb4 192.168.31.56 26379 @ mymaster 192.168.31.56 6379 2211:X 30 Sep 02:09:46.492 # +switch-master mymaster 192.168.31.56 6379 192.168.31.176 6379 2211:X 30 Sep 02:09:46.493 * +slave slave 192.168.31.66:6379 192.168.31.66 6379 @ mymaster 192.168.31.176 6379 2211:X 30 Sep 02:09:46.494 * +slave slave 192.168.31.56:6379 192.168.31.56 6379 @ mymaster 192.168.31.176 6379 2211:X 30 Sep 02:10:16.557 # +sdown slave 192.168.31.56:6379 192.168.31.56 6379 @ mymaster 192.168.31.176 6379
结果是选举了 176作为Master
通过客户端也可以看到176的role为master
使用JavaAPI 访问 Redis Sentinel集群
我们先把停掉的56起来,现在master是176主机,56起来之后,肯定是slave状态了。
为了测试数据的干净,我们现在master节点上flushdb清空keys,redis会自动同步到到另外两个slave节点。
如果在slave机器上执行flushdb,会抛出如下异常
"READONLY You can't write against a read only slave."
在 Java 中使用哨兵模式 ,加入关于哨兵的信息即可,非常简单,如下
package com.artisan.redis.sentinel; import java.util.Arrays; import java.util.HashSet; import java.util.Set; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPoolConfig; import redis.clients.jedis.JedisSentinelPool; public class JavaRedisSentinel { public static void main(String[] args) { // 连接池配置 JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); jedisPoolConfig.setMaxIdle(50); jedisPoolConfig.setMaxTotal(100); jedisPoolConfig.setMaxWaitMillis(2000); // 哨兵信息 Set<String> sentinelsSet = new HashSet<String>(Arrays.asList("192.168.31.66:26379", "192.168.31.56:26379", "192.168.31.176:26379")); // 创建连接池 JedisSentinelPool sentinelPool = new JedisSentinelPool("mymaster", sentinelsSet, jedisPoolConfig, "artisan"); // 获取客户端 Jedis jedis = sentinelPool.getResource(); // 执行指令测试下 jedis.set("artisan_key", "artisan_value"); String result = jedis.get("artisan_key"); System.out.println("artisan_key对应的value为:" + result); } }
运行:
九月 29, 2018 6:42:10 下午 redis.clients.jedis.JedisSentinelPool initSentinels 信息: Trying to find master from available Sentinels... 九月 29, 2018 6:42:10 下午 redis.clients.jedis.JedisSentinelPool initSentinels 信息: Redis master running at 192.168.31.176:6379, starting Sentinel listeners... 九月 29, 2018 6:42:10 下午 redis.clients.jedis.JedisSentinelPool initPool 信息: Created JedisPool to master at 192.168.31.176:6379 artisan_key对应的value为:artisan_value
划重点:Redis master running at 192.168.31.176:6379
去redis中查看,三个节点中均存在这个key
停掉master的redis-server ,然后再次操作
为了验证哨兵的作用,我们可以把主机上的 Redis 服务器关闭 , 马上运行,你
就可以发现报错 , 那倒不是因为哨兵失效导致的,而是因为 Redis 哨兵默认超时 3 分钟后才会进行投票切换主机,等超过 3 分钟后再进行测试
输出
九月 29, 2018 6:45:05 下午 redis.clients.jedis.JedisSentinelPool initSentinels 信息: Trying to find master from available Sentinels... 九月 29, 2018 6:45:05 下午 redis.clients.jedis.JedisSentinelPool initSentinels 信息: Redis master running at 192.168.31.56:6379, starting Sentinel listeners... 九月 29, 2018 6:45:05 下午 redis.clients.jedis.JedisSentinelPool initPool 信息: Created JedisPool to master at 192.168.31.56:6379 artisan_key对应的value为:artisan_value
划重点: Redis master running at 192.168.31.56:6379
从日志可以看到 , 我们实际使用的是 192.168.31.56 服务器,这是因为在192.168.31.176服务器不可用后 , 哨兵经过投票切换为 192.168.31.56服务器,通过这样的自动切换就保证服务持续稳定运行了。
使用Spring访问Redis Sentinel集群
<?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:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:property-placeholder location="classpath:redis/redis.properties" /> <!--2,注意新版本2.3以后,JedisPoolConfig的property name,不是maxActive而是maxTotal,而且没有maxWait属性,建议看一下Jedis源码或百度。 --> <!-- redis连接池配置 --> <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"> <!--最大空闲数 --> <property name="maxIdle" value="${redis.maxIdle}" /> <!--连接池的最大数据库连接数 --> <property name="maxTotal" value="${redis.maxTotal}" /> <!--最大建立连接等待时间 --> <property name="maxWaitMillis" value="${redis.maxWaitMillis}" /> <!--逐出连接的最小空闲时间 默认1800000毫秒(30分钟) --> <property name="minEvictableIdleTimeMillis" value="${redis.minEvictableIdleTimeMillis}" /> <!--每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认3 --> <property name="numTestsPerEvictionRun" value="${redis.numTestsPerEvictionRun}" /> <!--逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1 --> <property name="timeBetweenEvictionRunsMillis" value="${redis.timeBetweenEvictionRunsMillis}" /> <property name="testOnBorrow" value="true"></property> <property name="testOnReturn" value="true"></property> <property name="testWhileIdle" value="true"></property> </bean> <!-- 键值序列化器设置为String 类型 --> <bean id="stringRedisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer"/> <!-- jdk序列化器,可保存对象 --> <bean id="jdkSerializationRedisSerializer" class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/> <!-- 哨兵配置 --> <bean id="sentinelConfig" class="org.springframework.data.redis.connection.RedisSentinelConfiguration"> <!-- 服务名称 --> <property name="master"> <bean class="org.springframework.data.redis.connection.RedisNode"> <property name="name" value="mymaster"/> </bean> </property> <!-- 哨兵服务 IP 和端口 --> <property name="sentinels"> <set> <bean class="org.springframework.data.redis.connection.RedisNode"> <constructor-arg name="host" value="192.168.31.56"/> <constructor-arg name="port" value="26379"/> </bean> <bean class="org.springframework.data.redis.connection.RedisNode"> <constructor-arg name="host" value="192.168.31.66"/> <constructor-arg name="port" value="26379"/> </bean> <bean class="org.springframework.data.redis.connection.RedisNode"> <constructor-arg name="host" value="192.168.31.176"/> <constructor-arg name="port" value="26379"/> </bean> </set> </property> </bean> <!--redis连接工厂 --> <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" destroy-method="destroy"> <constructor-arg name="sentinelConfig" ref="sentinelConfig"/> <constructor-arg name="poolConfig" ref="jedisPoolConfig"/> <property name="password" value="artisan"/> </bean> <!-- redis template definition --> <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" p:connection-factory-ref="jedisConnectionFactory" p:keySerializer-ref="stringRedisSerializer" p:defaultSerializer-ref="stringRedisSerializer" p:valueSerializer-ref="jdkSerializationRedisSerializer"> </bean> </beans>
package com.artisan.redis.sentinel; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.data.redis.core.RedisOperations; import org.springframework.data.redis.core.RedisTemplate; public class SpringRedisSentinel { @SuppressWarnings({ "unchecked", "rawtypes", "resource" }) public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:spring/spring-redis-sentinel.xml"); RedisTemplate redisTemplate = ctx.getBean(RedisTemplate.class); String retVaule = (String) redisTemplate.execute((RedisOperations ops) -> { ops.boundValueOps("artisan_k").set("artisan_v"); String value = (String) ops.boundValueOps("artisan_k").get(); return value; }); System.out.println(retVaule); } }
先把停掉的176起来,确保集群正常 .执行应用程序
INFO : org.springframework.context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@73a8dfcc: startup date [Sat Sep 29 19:13:09 CST 2018]; root of context hierarchy INFO : org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loading XML bean definitions from class path resource [spring/spring-redis-sentinel.xml] 九月 29, 2018 7:13:09 下午 redis.clients.jedis.JedisSentinelPool initSentinels 信息: Trying to find master from available Sentinels... 九月 29, 2018 7:13:10 下午 redis.clients.jedis.JedisSentinelPool initSentinels 信息: Redis master running at 192.168.31.56:6379, starting Sentinel listeners... 九月 29, 2018 7:13:10 下午 redis.clients.jedis.JedisSentinelPool initPool 信息: Created JedisPool to master at 192.168.31.56:6379 artisan_v
Redis master running at 192.168.31.56:6379
停掉56 ,马上访问
INFO : org.springframework.context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@73a8dfcc: startup date [Sat Sep 29 19:15:27 CST 2018]; root of context hierarchy INFO : org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loading XML bean definitions from class path resource [spring/spring-redis-sentinel.xml] 九月 29, 2018 7:15:28 下午 redis.clients.jedis.JedisSentinelPool initSentinels 信息: Trying to find master from available Sentinels... 九月 29, 2018 7:15:28 下午 redis.clients.jedis.JedisSentinelPool initSentinels 信息: Redis master running at 192.168.31.56:6379, starting Sentinel listeners... 九月 29, 2018 7:15:28 下午 redis.clients.jedis.JedisSentinelPool initPool 信息: Created JedisPool to master at 192.168.31.56:6379 Exception in thread "main" org.springframework.data.redis.RedisConnectionFailureException: Cannot get Jedis connection; nested exception is redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.fetchJedisConnector(JedisConnectionFactory.java:286) at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.getConnection(JedisConnectionFactory.java:469) at org.springframework.data.redis.core.RedisConnectionUtils.doGetConnection(RedisConnectionUtils.java:132) at org.springframework.data.redis.core.RedisConnectionUtils.bindConnection(RedisConnectionUtils.java:70) at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:250) at com.artisan.redis.sentinel.SpringRedisSentinel.main(SpringRedisSentinel.java:15) Caused by: redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool at redis.clients.util.Pool.getResource(Pool.java:53) at redis.clients.jedis.JedisSentinelPool.getResource(JedisSentinelPool.java:209) at redis.clients.jedis.JedisSentinelPool.getResource(JedisSentinelPool.java:17) at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.fetchJedisConnector(JedisConnectionFactory.java:276) ... 5 more Caused by: redis.clients.jedis.exceptions.JedisConnectionException: java.net.ConnectException: Connection refused: connect at redis.clients.jedis.Connection.connect(Connection.java:207) at redis.clients.jedis.BinaryClient.connect(BinaryClient.java:93) at redis.clients.jedis.BinaryJedis.connect(BinaryJedis.java:1767) at redis.clients.jedis.JedisFactory.makeObject(JedisFactory.java:106) at org.apache.commons.pool2.impl.GenericObjectPool.create(GenericObjectPool.java:868) at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:435) at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:363) at redis.clients.util.Pool.getResource(Pool.java:49) ... 8 more Caused by: java.net.ConnectException: Connection refused: connect at java.net.DualStackPlainSocketImpl.waitForConnect(Native Method) at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:85) at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350) at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206) at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188) at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172) at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392) at java.net.Socket.connect(Socket.java:589) at redis.clients.jedis.Connection.connect(Connection.java:184) ... 15 more
等集群选举完后,再次访问
INFO : org.springframework.context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@73a8dfcc: startup date [Sat Sep 29 19:16:02 CST 2018]; root of context hierarchy INFO : org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loading XML bean definitions from class path resource [spring/spring-redis-sentinel.xml] 九月 29, 2018 7:16:03 下午 redis.clients.jedis.JedisSentinelPool initSentinels 信息: Trying to find master from available Sentinels... 九月 29, 2018 7:16:03 下午 redis.clients.jedis.JedisSentinelPool initSentinels 信息: Redis master running at 192.168.31.176:6379, starting Sentinel listeners... 九月 29, 2018 7:16:03 下午 redis.clients.jedis.JedisSentinelPool initPool 信息: Created JedisPool to master at 192.168.31.176:6379 artisan_v
Redis master running at 192.168.31.176:6379
切换到了176,确保了服务的可用。
哨兵模式的其他配置顶
sentinel down-after-milliseconds 配置项只是一个哨兵在超过其指定的毫秒数依 旧没有得到回答消息后,会自己认为主机不可用。对于其他哨兵而言,并不会认为主机不可用。
哨兵会记录这个消息 , 当拥有认为主观下线的哨兵到达 sentinel monitor 所配置的数量的时候 , 就会发起一次新的投票 , 然后切换主机,此时哨兵会重写 Redis 的哨兵配置文件 ,以适应新场景的需要。