Redis的主从复制模式下,一旦主节点由于故障不能提供服务,需要人工将从节点晋升为主节点,同时还要通知应用方更新主节点地址,对于很多应用场景这种故障处理的方式是无法接受的。
Redis从2.8开始正式提供了Redis Sentinel(哨兵)架构来解决这个问题。
一、基本概念
1.主从复制的问题
(1)Redis的主从复制模式可以将主节点的数据改变同步给从节点,这样从节点就可以起到两个作用:
- 作为主节点的一个备份,一旦主节点出了故障不可达的情况,从节点可以作为后备“顶”上来,并且保证数据尽量不丢失(主从复制是最终一致性)。
- 从节点可以扩展主节点的读能力,一旦主节点不能支撑柱大而并发的读操作,从节点可以在一定程度上帮助主节点分担读压力。
(2)主从复制也带来了以下问题:
- 一旦主节点出现故障,需要手动将一个从节点晋升为主节点,同时需要修改应用方的主节点地址,还需要命令其他从节点去复制新的主节点,整个过程都需要人工干预。
- 主节点的写能力受到单机的限制。
- 主节点的存储能力受到单机的限制。
2.非高可用性
Redis主从复制模式下,一旦主节点出现了故障不可达,需要人工干预进行故障转移,无论对于Redis的应用方还是运维方都带来了很大的不便。对于应用方来说无法及时感知到主节点的变化,必然会造成一定的写数据丢失和读数据错误,甚至可能造成应用方服务不可用。对于Redis的运维方来说,整个故障转移的过程是需要人工来介入的,故障转移实时性和准确性上都无法得到保障。
一个1主2从的Redis主从复制模式下的主节点出现故障后,故障转移过程:
- 主节点发生故障后,客户端(client)连接主节点失败,两个从节点与主节点连接失败造成复制中断。
- 如果主节点无法正常启动,需要选出一个从节点(slave-1),对其执行slaveof no one命令使其成为新的主节点。
- 原来的从节点(slave-1)成为新的主节点后,更新应用方的主节点信息,重新启动应用方。
- 客户端命令另一个从节点(slave-2)去复制新的主节点(new-master)
- 原来的主节点恢复后,让它去复制新的主节点。
上述处理过程就可以认为整个服务或者架构的设计不是高可用的,因为整个故障转移的过程需要人介入。
考虑到这点,有些公司把上述流程自动化了,但是仍然存在如下问题:
- 判断节点不可达的机制是否健全和标准。
- 如果有多个从节点,怎样保证只有一个被晋升为主节点。
- 通知客户端新的主节点机制是否足够健壮。
3.Redis Sentinel的高可用性
当主节点出现故障时,Redis Sentinel能自动完成故障发现和故障转移,并通知应用方,从而实现真正的高可用。
Redis Sentinel是一个分布式架构(分布在多个物理节点的架构),其中包含若干个Sentinel节点和Redis数据节点,每个Sentinel节点会对数据节点和其余Sentinel节点进行监控,当它发现节点不可达时,会对节点做下线标识。如果被标识的是主节点,它还会和其他Sentinel节点进行“协商”,当大多数Sentinel节点都认为主节点不可达时,它们会选举出一个Sentinel节点来完成自动故障转移的工作,同时会将这个变化实时通知给Redis应用方。整个过程完全是自动的,不需要人工来介入,所以这套方案很有效地解决了Redis的高可用问题。
Redis Sentinel与Redis主从复制模式只是多了若干Sentinel节点,所以Redis Sentinel并没有针对Redis节点做了特殊处理。
从逻辑架构上看,Sentinel节点集合会定期对所有节点进行监控,特别是对主节点的故障实现自动转移。
以1个主节点、2个从节点、3个Sentinel节点组成的Redis Sentinel为例子进行说明:
- 主节点出现故障,此时两个从节点与主节点失去连接,主从复制失败。
- 每个Sentinel节点通过定期监控发现主节点出现了故障。
- 多个Sentinel节点对主节点的故障达成一致,选举出sentinel-3节点作为领导者负责故障转移。
- Sentinel领导者节点执行了故障转移,整个过程和主从复制的故障转移过程是完全一致的,只不过是自动化完成的。
Redis Sentinel具有以下几个功能
- 监控:Sentinel节点会定期检测Redis数据节点、其余Sentinel节点是否可达。
- 通知:Sentinel节点会将故障转移的结果通知给应用方。
- 主节点故障转移:实现从节点晋升为主节点并维护后续正确的主从关系。
- 配置提供者:在Redis Sentinel结构中,客户端在初始化的时候连接的是Sentinel节点集合,从中获取主节点信息。
同时看到,Redis Sentinel包含了若个Sentinel节点,这样做也带来了两个好处:
- 对于节点的故障判断是由多个Sentinel节点共同完成,这样可以有效地防止误判。
- Sentinel节点集合是由若干个Sentinel节点组成的,这样即使个别Sentinel节点不可用,整个Sentinel节点集合依然是健壮的。
但是Sentinel节点本身就是独立的Redis节点,只不过它们有一些特殊,它们不存储数据,只支持部分命令。
二、安装和部署
1.部署拓扑结构
2.部署Redis数据节点
(1)启动主节点
- 配置主节点配置文件redis-6379.conf(daemonize yes开启守护进程,程序在后台启动,除非kill进程,否则不会停止):
port 6379 daemonize yes logfile "6379.log" dbfilename "dump-6379.rdb" dir "/home/redis/data/"
- 启动主节点
redis-server redis-6379.conf
- 验证主节点已经启动
root@myubuntu:/home/redis# redis-cli -h 127.0.0.1 -p 6379 ping PONG
此时拓扑结构为:
(2)部署从节点
- 配置从节点配置文件(仅以其中一个为例):
port 6381 daemonize yes logfile "6381.log" dbfilename "dump-6381.rdb" dir "/home/redis/data/" slaveof 127.0.0.1 6379
- 启动两个从节点
root@myubuntu:/home/redis# redis-server redis-6380.conf root@myubuntu:/home/redis# redis-server redis-6381.conf
- 验证两个从节点是否启动
root@myubuntu:/home/redis# redis-cli -h 127.0.0.1 -p 6380 ping PONG root@myubuntu:/home/redis# redis-cli -h 127.0.0.1 -p 6381 ping PONG
(3)确定主从关系
- 主节点的主从信息:
root@myubuntu:/home/redis# redis-cli -h 127.0.0.1 -p 6379 info replication # Replication role:master connected_slaves:2 slave0:ip=127.0.0.1,port=6380,state=online,offset=196,lag=0 slave1:ip=127.0.0.1,port=6381,state=online,offset=196,lag=0 master_replid:70c507fb12e1448a8fe88b04dffdc44eebba63c0 master_replid2:0000000000000000000000000000000000000000 master_repl_offset:196 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1 repl_backlog_histlen:196
- 从节点的主从信息(以其中一个为例)
root@myubuntu:/home/redis# redis-cli -h 127.0.0.1 -p 6381 info replication # Replication role:slave master_host:127.0.0.1 master_port:6379 master_link_status:up master_last_io_seconds_ago:5 master_sync_in_progress:0 slave_repl_offset:266 slave_priority:100 slave_read_only:1 connected_slaves:0 master_replid:70c507fb12e1448a8fe88b04dffdc44eebba63c0 master_replid2:0000000000000000000000000000000000000000 master_repl_offset:266 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:15 repl_backlog_histlen:252
此时拓扑结构为:
3.部署Sentinel节点
(1)配置Sentinel节点(以其中一个为例)(vim中全局替换::1,$s/6380/26379/g 或者:%s/6380/26379/g ):
port 26379 daemonize yes logfile "26379.log" dir "/home/redis/data/" sentinel monitor mymaster 127.0.0.1 6379 2 sentinel down-after-milliseconds mymaster 30000 sentinel parallel-syncs mymaster 1 sentinel failover-timeout mymaster 180000
(2)启动三个Sentinel节点:
root@myubuntu:/home/redis# redis-sentinel redis-sentinal-26379.conf root@myubuntu:/home/redis# redis-sentinel redis-sentinal-26380.conf root@myubuntu:/home/redis# redis-sentinel redis-sentinal-26381.conf
(3)查询相关信息确认节点关系:
sentinel_masters是一个;主节点的sentinel节点是sentinels=3三个。
root@myubuntu:/home/redis# redis-cli -h 127.0.0.1 -p 26379 info Sentinel # Sentinel sentinel_masters:1 sentinel_tilt:0 sentinel_running_scripts:0 sentinel_scripts_queue_length:0 sentinel_simulate_failure_flags:0 master0:name=mymaster,status=ok,address=127.0.0.1:6379,slaves=2,sentinels=3
此时的拓扑结构是:
(5)需要说明的两点
- 生产环境中建议Redis Sentinel的所有节点应该分布在不同的物理机上。
- Redis Sentinel中的数据节点和普通的Redis数据节点在配置上没有任何区别,只不过是添加了一些Sentinel节点对它们进行监控。
4.配置优化
(1)配置说明和优化
查看Redis安装目录下的sentinel.conf配置文件:
port 26379 dir /tmp sentinel monitor mymaster 127.0.0.1 6379 2 sentinel down-after-milliseconds mymaster 30000 sentinel parallel-syncs mymaster 1 sentinel failover-timeout mymaster 180000 #sentinel auth-pass <master-name> <password> #sentinel notification-script <master-name> <script-path> #sentinel client-reconfig-script <master-name> <script-path>
- sentinel monitor <master-name> <ip> <port> <quorum>
本配置说明Sentinel节点要监控的是一个名字叫做<master-name>,ip地址和端口为<ip><port>的主节点。<quorum>代表要判定主节点最终不可达所需要的票数。但实际上Sentinel节点会对所有节点进行监控,但是在Sentinel节点的配置中没有看到有关从节点和其余Sentinel节点的配置,那是因为Sentinel节点会从主节点中获取有关从节点以及其余Sentinel节点的相关信息。
例如,在之前配置了三个Sentinel节点并且启动之后,查看其中一个sentinel节点的配置文件,以redis-sentinel-26379.conf为例:
port 26379 daemonize yes logfile "26379.log" dir "/home/redis/data" sentinel myid d2e9756dbe6fbcd5ed8e3eba89831da6770b2437 sentinel monitor mymaster 127.0.0.1 6379 2 sentinel config-epoch mymaster 0 sentinel leader-epoch mymaster 0 # Generated by CONFIG REWRITE sentinel known-slave mymaster 127.0.0.1 6381 sentinel known-slave mymaster 127.0.0.1 6380 sentinel known-sentinel mymaster 127.0.0.1 26381 9c8f75f3a6737dcf6f6c29418a8e1097f4ed5b4b sentinel known-sentinel mymaster 127.0.0.1 26380 006eba2eddff1a1a9e9ab499fabe75a1fe9270bc sentinel current-epoch 0
发现配置文件自己发生了改变,主要体现在三个方面:
- Sentinel节点自动发现了从节点、其余Sentinel节点。
- 去掉了默认配置,例如parallel-syncs、failover-timeout参数。
- 添加了配置纪元相关参数。(发现两个slave节点,还发现了另外两个sentinel节点)
<quorum>参数用于故障发现和判定,例如将quorum配置为2,代表至少有2个Sentinel节点认为主节点不可达,那么这个不可达的判定才是客观的。对于<quorum>设置的越小,那么达到下线的条件越宽松,反之越严格。一般建议将其设置为Sentinel节点的一半加1。
同时<quorum>还与Sentinel节点的领导者选举有关,至少要有max(quorum,num(sentinels)/2+1)个Sentinel节点参与选举,才能选出领导者Sentinel,从而完成故障转移。例如有5个Sentinel节点,quorum=4,那么至少要有max(quorum,num(sentinels)/2+1)=4个在线Sentinel节点才可以进行领导者选举。
- sentinel down-after-milliseconds <master-name> <times>
每个Sentinel节点都要通过定期发送ping命令来判断Redis数据节点和其余Sentinel节点是否可达,如果超过了down-after-milliseconds配置的时间且没有有效的回复,则判定节点不可达,<times>(单位为毫秒)就是超时时间。这个配置是对节点失败判定的重要依据。
优化说明:down-after-milliseconds越大,代表Sentinel节点对于节点不可达的条件越宽松,反之越严格。条件宽松有可能带来的问题是节点确实不可达了,那么应用方需要等待故障转移的时间越长,也就意味着应用方故障时间可能越长。条件严格虽然可以及时发现故障完成故障转移,但是也存在一定的误判率。(down-after-milliseconds虽然以<master-name>为参数,但实际上对Sentinel节点、主节点、从节点的失败判定同时有效。)
- sentinel parallel-syncs <master-name> <nums>
当Sentinel节点集合对主节点故障判定达成一致时,Sentinel领导者节点会做故障转移操作,选出新的主节点,原来的从节点会向新的主节点发起复制操作,parallel-syncs就是用来限制在一次故障转移之后,每次向新的主节点发起复制操作的从节点个数。如果这个参数配置的比较大,那么多个从节点会向新的主节点同时发起复制操作,尽管复制操作通常不会阻塞主节点,但是同时向主节点发起复制,必然会对主节点所在的机器造成一定的网络和磁盘IO开销。
parallel-syncs=3会同时发起复制,parallel-syncs=1时从节点会轮询发起复制。
- sentinel failover-timeout <master-name> <times>
failover-timeout通常被解释成故障转移超时时间,但实际上它作用于故障转移的各个阶段:
a)选出合适从节点。
b)晋升选出的从节点为主节点。
c)命令其余从节点复制新的主节点。
d)等待原主节点恢复后命令它去复制新的主节点。
failover-timeout的作用具体体现在四个方面:
- 如果Redis Sentinel对一个主节点故障转移失败,那么下次再对该主节点做故障转移的起始时间是failover-timeout的2倍。
- 在b)阶段时,如果Sentinel节点向a)阶段选出来的从节点执行slaveof no one一直失败(例如该从节点此时出现故障),当此过程超过failover-timeout时,则故障转移失败。
- 在b)阶段如果执行成功,Sentinel节点还会执行info命令来确认a)阶段选出来的节点确实晋升为主节点,如果此过程执行时间超过failover-timeout时,则故障转移失败。
- 如果c)阶段执行时间超过了failover-timeout(不包含复制时间),则故障转移失败。注意即使超过了这个时间,Sentinel节点也会最终配置从节点去同步最新的主节点。
- sentinel auth-pass <master-name> <password>
如果Sentinel监控的主节点配置了密码,sentinel auth-pass配置通过添加主节点的密码,防止Sentinel节点对主节点无法监控。
- sentinel notification-script <master-name> <script-path>
sentinel notification-script的作用是在故障转移期间,当一些警告级别的Sentinel事件发生(指重要事件,例如-sdown:客观下线、-odown:主观下线)时,会触发对应路径的脚本,并向脚本发送相应的事件参数。
- sentinel client-reconfig-script <master-name> <script-path>
sentinel client-reconfig-script的作用是在故障转移结束后,会触发对应路径的脚本,并向脚本发送故障转移结果的相关参数。和notification-script类似,可以在/opt/redis/scripts/下配置了client-reconfig.sh,该脚本会接收每个Sentinel节点传过来的故障转移结果参数,并触发类似短信和邮件报警。
(2)如何监控多个主节点
Redis Sentinel可以同时监控多个主节点:
根据之前的经验来部署这个拓扑结构:
- 节点配置文件(只给结构):
-rw-r--r-- 1 root root 94 6月 5 20:46 redis-6379.conf -rw-r--r-- 1 root root 117 6月 5 20:50 redis-6380.conf -rw-r--r-- 1 root root 117 6月 5 20:51 redis-6381.conf -rw-r--r-- 1 root root 94 6月 5 21:53 redis-6382.conf -rw-r--r-- 1 root root 117 6月 5 21:54 redis-6383.conf -rw-r--r-- 1 root root 117 6月 5 21:53 redis-6384.conf
- 验证两组节点是否主从关系正确:
root@myubuntu:/home/redis# redis-cli -h 127.0.0.1 -p 6379 info replication # Replication role:master connected_slaves:2 slave0:ip=127.0.0.1,port=6380,state=online,offset=397259,lag=1 slave1:ip=127.0.0.1,port=6381,state=online,offset=397259,lag=1 master_replid:70c507fb12e1448a8fe88b04dffdc44eebba63c0 master_replid2:0000000000000000000000000000000000000000 master_repl_offset:397259 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1 repl_backlog_histlen:397259
root@myubuntu:/home/redis# redis-cli -h 127.0.0.1 -p 6382 info replication # Replication role:master connected_slaves:2 slave0:ip=127.0.0.1,port=6383,state=online,offset=56,lag=0 slave1:ip=127.0.0.1,port=6384,state=online,offset=56,lag=1 master_replid:8f75c8f644d6e00c51b3649be32a7f88444cb2c6 master_replid2:0000000000000000000000000000000000000000 master_repl_offset:56 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1 repl_backlog_histlen:56
- sentinel节点配置:
port 26379 daemonize yes logfile "26379.log" dir "/home/redis/data/" sentinel monitor master-business-1 127.0.0.1 6379 2 sentinel down-after-milliseconds master-business-1 60000 sentinel parallel-syncs master-buiness-1 1 sentinel failover-timeout master-buiness-1 180000 sentinel monitor master-business-1 127.0.0.1 6382 2 sentinel down-after-milliseconds master-business-2 60000 sentinel parallel-syncs master-buiness-2 1 sentinel failover-timeout master-buiness-2 180000
- 三个sentinel节点启动之后sentinel配置变为:
port 26380 daemonize yes logfile "26380.log" dir "/home/redis/data" sentinel myid 8e944964f61e6e888db436849af346472ca457b2 sentinel monitor mymaster-2 127.0.0.1 6382 2 sentinel down-after-milliseconds mymaster-2 60000 sentinel config-epoch mymaster-2 0 sentinel leader-epoch mymaster-2 0 sentinel known-slave mymaster-2 127.0.0.1 6383 sentinel known-slave mymaster-2 127.0.0.1 6384 sentinel known-sentinel mymaster-2 127.0.0.1 26379 98249cec0887f80a4d7996c99f80503fa5f139c5 # Generated by CONFIG REWRITE sentinel known-sentinel mymaster-2 127.0.0.1 26381 7b8272978b840aa395ecfc1ef20450047bef81c4 sentinel monitor mymaster-1 127.0.0.1 6379 2 sentinel down-after-milliseconds mymaster-1 60000 sentinel config-epoch mymaster-1 0 sentinel leader-epoch mymaster-1 0 sentinel known-slave mymaster-1 127.0.0.1 6381 sentinel known-slave mymaster-1 127.0.0.1 6380 sentinel known-sentinel mymaster-1 127.0.0.1 26379 98249cec0887f80a4d7996c99f80503fa5f139c5 sentinel known-sentinel mymaster-1 127.0.0.1 26381 7b8272978b840aa395ecfc1ef20450047bef81c4 sentinel current-epoch 0
- 通过info命令查询sentinel相关信息:
root@myubuntu:/home/redis# redis-cli -h 127.0.0.1 -p 26379 info Sentinel # Sentinel sentinel_masters:2 sentinel_tilt:0 sentinel_running_scripts:0 sentinel_scripts_queue_length:0 sentinel_simulate_failure_flags:0 master0:name=mymaster-2,status=ok,address=127.0.0.1:6382,slaves=2,sentinels=3 master1:name=mymaster-1,status=ok,address=127.0.0.1:6379,slaves=2,sentinels=3
完成部署。
(3)调整配置
sentinel set <param> <value>
调整配置说明:
- sentinel set命令只对当前Sentinel节点有效。
- sentinel set命令如果执行成功会立即刷新配置文件,这点和Redis普通数据节点设置配置需要执行config rewrite刷新到配置文件不同。
- 建议所有Sentinel节点的配置尽可能一致,这样在故障发现和转移时比较容易达成一致。
- Sentinel对外不支持config命令。
5.部署技巧
- Sentinel节点不应该部署在一台物理“机器”上。这里特意强调物理机是因为一台物理机做成了若干虚拟机或者现今比较流行的容器,它们虽然有不同的IP地址,但实际上它们都是同一台物理机,同一台物理机意味着如果这台机器有什么硬件故障,所有的虚拟机都会受到影响,为了实现Sentinel节点集合真正的高可用,请勿将Sentinel节点部署在同一台物理机器上。
- 部署至少三个且奇数个的Sentinel节点。3个以上是通过增加Sentinel节点的个数提高对于故障判定的准确性,因为领导者选举需要至少一半加1个节点,奇数个节点可以在满足该条件的基础上节省一个节点。
- 一套Sentinel,很明显这种方案在一定程度上降低了维护成本,因为只需要维护固定个数的Sentinel节点,集中对多个Redis数据节点进行管理就可以了。但是这同时也是它的缺点,如果这套Sentinel节点集合出现异常,可能会对多个Redis数据节点造成影响。还有如果监控的Redis数据节点较多,会造成Sentinel节点产生过多的网络连接,也会有一定的影响。
- 多套Sentinel,显然这种方案的优点和缺点和上面是相反的,每个Redis主节点都有自己的Sentinel节点集合,会造成资源浪费。但是优点也很明显,每套Redis Sentinel都是彼此隔离的。
三、API
以多套sentinel拓扑为例:
部署节点:
root@myubuntu:/home/redis# redis-server redis-6379.conf root@myubuntu:/home/redis# redis-server redis-6380.conf root@myubuntu:/home/redis# redis-server redis-6381.conf root@myubuntu:/home/redis# redis-server redis-6382.conf root@myubuntu:/home/redis# redis-server redis-6383.conf root@myubuntu:/home/redis# redis-server redis-6384.conf root@myubuntu:/home/redis# redis-sentinel redis-sentinal-26379.conf root@myubuntu:/home/redis# redis-sentinel redis-sentinal-26380.conf root@myubuntu:/home/redis# redis-sentinel redis-sentinal-26381.conf
(1)sentinel masters显示所有被监控的主节点状态以及相关的统计信息。
127.0.0.1:26379> sentinel masters 1) 1) "name" 2) "mymaster-2" 3) "ip" 4) "127.0.0.1" 5) "port" 6) "6382" ... 2) 1) "name" 2) "mymaster-1" 3) "ip" 4) "127.0.0.1" 5) "port" 6) "6379" ...
(2)sentinel master <master name>显示指定主节点状态及相关的统计信息
127.0.0.1:26379> sentinel master mymaster-1 1) "name" 2) "mymaster-1" 3) "ip" 4) "127.0.0.1" 5) "port" 6) "6379" 7) "runid" 8) "b15379ac6af4c351520367d6586e9f3118695b8e" 9) "flags" ...
(3)sentinel slaves <master name>显示指定主节点的从节点的相关统计信息:
127.0.0.1:26379> sentinel slaves mymaster-1 1) 1) "name" 2) "127.0.0.1:6381" 3) "ip" 4) "127.0.0.1" 5) "port" 6) "6381" 7) "runid" 8) "610023f45fe0389ce125abecf84e78217e93f118" 9) "flags" 10) "slave" ... 2) 1) "name" 2) "127.0.0.1:6380" 3) "ip" 4) "127.0.0.1" 5) "port" 6) "6380" 7) "runid" 8) "5257bc394fc63ac1216b1df46e4e7dd26689d791" 9) "flags" 10) "slave" ...
(4)sentinel sentinels <master name>显示指定主节点的Sentinel节点集合(不包含当前Sentinel节点)
127.0.0.1:26379> sentinel sentinels mymaster-1 1) 1) "name" 2) "8e944964f61e6e888db436849af346472ca457b2" 3) "ip" 4) "127.0.0.1" 5) "port" 6) "26380" 7) "runid" 8) "8e944964f61e6e888db436849af346472ca457b2" 9) "flags" 10) "sentinel" .... 2) 1) "name" 2) "7b8272978b840aa395ecfc1ef20450047bef81c4" 3) "ip" 4) "127.0.0.1" 5) "port" 6) "26381" 7) "runid" 8) "7b8272978b840aa395ecfc1ef20450047bef81c4" 9) "flags" 10) "sentinel" ...
(5)sentinel get-master-addr-by-name <master name>显示指定主节点的IP地址和端口
127.0.0.1:26379> sentinel get-master-addr-by-name mymaster-1 1) "127.0.0.1" 2) "6379"
(6)sentinel reset <pattern>当前Sentinel节点对符合<pattren>(通配符风格)主节点的配置进行重置,包含清除主节点的相关状态(例如故障转移),重新发现从节点和Sentinel节点
127.0.0.1:26379> sentinel reset mymaster-1 (integer) 1
(7)sentinel failover <master name>对指定主节点进行故障转移,当故障转移完成后,其他Sentinel节点按照故障转移的结果更新自身配置(mymaster-2 由原来的6382转换为6384(也可能是6383))
127.0.0.1:26379> info sentinel # Sentinel sentinel_masters:2 sentinel_tilt:0 sentinel_running_scripts:0 sentinel_scripts_queue_length:0 sentinel_simulate_failure_flags:0 master0:name=mymaster-2,status=ok,address=127.0.0.1:6382,slaves=2,sentinels=3 master1:name=mymaster-1,status=ok,address=127.0.0.1:6379,slaves=2,sentinels=3 127.0.0.1:26379> sentinel failover mymaster-2 OK 127.0.0.1:26379> info sentinel # Sentinel sentinel_masters:2 sentinel_tilt:0 sentinel_running_scripts:0 sentinel_scripts_queue_length:0 sentinel_simulate_failure_flags:0 master0:name=mymaster-2,status=ok,address=127.0.0.1:6384,slaves=2,sentinels=3 master1:name=mymaster-1,status=ok,address=127.0.0.1:6379,slaves=2,sentinels=3 127.0.0.1:26379> sentinel get-master-addr-by-name mymaster-2 1) "127.0.0.1" 2) "6384"
(8)sentinel ckquorum <master name>检测当前可达的sentinel节点总数是否达到<quorum>的个数,如果达不到个数将无法进行故障转移
127.0.0.1:26379> sentinel ckquorum mymaster-1 OK 3 usable Sentinels. Quorum and failover authorization can be reached
(9)sentinel flushconfig将Sentinel节点的配置强制刷到磁盘上,对于开发和运维人员只有当外部原因(例如磁盘损坏)造成配置文件损坏或者丢失时,这个命令很有用
127.0.0.1:26379> sentinel flushconfig OK
(10)sentinel remove <master name>取消当前sentinel节点对于指定主节点的监控
127.0.0.1:26379> info sentinel # Sentinel sentinel_masters:2 sentinel_tilt:0 sentinel_running_scripts:0 sentinel_scripts_queue_length:0 sentinel_simulate_failure_flags:0 master0:name=mymaster-2,status=ok,address=127.0.0.1:6384,slaves=2,sentinels=3 master1:name=mymaster-1,status=ok,address=127.0.0.1:6379,slaves=2,sentinels=3 127.0.0.1:26379> sentinel remove mymaster-1 OK 127.0.0.1:26379> info sentinel # Sentinel sentinel_masters:1 sentinel_tilt:0 sentinel_running_scripts:0 sentinel_scripts_queue_length:0 sentinel_simulate_failure_flags:0 master0:name=mymaster-2,status=ok,address=127.0.0.1:6384,slaves=2,sentinels=3
(11)sentinel monitor <master name> <ip> <port> <quorum>通过命令的形式完成Sentinel节点对主节点的监控
127.0.0.1:26379> sentinel monitor mymaster-1 127.0.0.1 6379 2 OK 127.0.0.1:26379> info sentinel # Sentinel sentinel_masters:2 sentinel_tilt:0 sentinel_running_scripts:0 sentinel_scripts_queue_length:0 sentinel_simulate_failure_flags:0 master0:name=mymaster-2,status=ok,address=127.0.0.1:6384,slaves=2,sentinels=3 master1:name=mymaster-1,status=ok,address=127.0.0.1:6379,slaves=2,sentinels=3
(13)Sentinel is-master-down-by-addr用于Sentinel节点之间用来交换对主节点是否下线的判断,根据参数的不同,还可以作为Sentinel领导者选举的通信方式
sentinel is-master-down-by-addr <ip> <port> <current_epoch> <runid>
四、客户端连接
1.Redis Sentinel的客户端
Sentinel节点集合具备了监控、通知、自动故障转移、配置提供者若干功能,也就是说实际上最了解主节点信息的就是Sentinel节点集合,而各个主节点可以通过<master-name>进行标识的,所以,无论是哪种编程语言的客户端,如果需要正确地连接Redis Sentinel,必须有Sentinel节点集合和masterName两个参数。
2.Redis Sentinel客户端基本实现原理
- 遍历Sentinel节点集合获取一个可用的Sentinel节点,Sentinel节点之间可以共享数据,所以从任意一个Sentinel节点获取主节点信息都是可以的。
- 通过sentinel get-master-addr-by-name master-name这个API来获取对应主节点的相关信息。
- 验证当前获取的“主节点”是真正的主节点,这样做的目的是为了防止故障转移期间主节点的变化。
- 保持和Sentinel节点集合的“联系”,时刻获取关于主节点的相关“信息”。
3.Java操作Redis Sentinel
Jedis针对Redis Sentinel给出了一个JedisSentinelPool,构造方法为:
public JedisSentinelPool(String masterName, Set<String> sentinels, final GenericObjectPoolConfig poolConfig, final int connectionTimeout, final int soTimeout, final String password, final int database, final String clientName) masterName——主节点名。 sentinels——Sentinel节点集合。 poolConfig——common-pool连接池配置。 connectTimeout——连接超时。 soTimeout——读写超时。 password——主节点密码。 database——当前数据库索引。 clientName——客户端名。
获取JedisSentinelPool并得到Sentinel节点的Jedis对象实例:
JedisSentinelPool jedisSentinelPool = new JedisSentinelPool(masterName, sentinelSet, poolConfig, timeout); Jedis jedis = null; try { jedis = jedisSentinelPool.getResource(); // jedis command } catch (Exception e) { logger.error(e.getMessage(), e); } finally { if (jedis != null) jedis.close(); }
五、Redis Sentinel实现原理
1.三个定时监控任务
一套合理的监控机制是Sentinel节点判定节点不可达的重要保证,Redis Sentinel通过三个定时监控任务完成对各个节点发现和监控:
(1)每隔10秒,每个Sentinel节点会向主节点和从节点发送info命令获取主节点和从节点执行info replication的结果,解析这个结果包括主节点和从节点最新的拓扑结构。
(2)每隔2秒,每个Sentinel节点会向Redis数据节点的__sentinel__:hello频道上发送该Sentinel节点对于主节点的判断以及当前Sentinel节点的信息,同时每个Sentinel节点也会订阅该频道,来了解其他
Sentinel节点以及它们对主节点的判断。①发现新的Sentinel节点:通过订阅主节点的__sentinel__:hello了解其他的Sentinel节点信息,如果是新加入的Sentinel节点,将该Sentinel节点信息保存起来,并与该Sentinel节点创建连接。②Sentinel节点之间交换主节点的状态,作为后面客观下线以及领导者选举的依据。
(3)每隔1秒,每个Sentinel节点会向主节点、从节点、其余Sentinel节点发送一条ping命令做一次心跳检测,来确认这些节点当前是否可达。
2.主观下线和客观下线
- 主观下线:在执行定时监控任务的第三个定时任务的时候,每个Sentinel节点会每隔1秒对主节点、从节点、其他Sentinel节点发送ping命令做心跳检测,当这些节点超过down-after-milliseconds没有进行有效回复,Sentinel节点就会对该节点做失败判定,这个行为叫做主观下线。主观下线是当前Sentinel节点的一家之言,存在误判的可能。
- 客观下线:当Sentinel主观下线的节点是主节点时,该Sentinel节点会通过sentinel is-master-down-by-addr命令向其他Sentinel节点询问对主节点的判断,当超过<quorum>个数,Sentinel节点认为主节点确实有问题,这时该Sentinel节点会做出客观下线的决定,这样客观下线的含义是比较明显了,也就是大部分Sentinel节点都对主节点的下线做了同意的判定,那么这个判定就是客观的。
sentinel is-master-down-by-addr <ip> <port> <current_epoch> <runid> ip:主节点IP。 port:主节点端口。 current_epoch:当前配置纪元。 runid:此参数有两种类型,不同类型决定了此API作用的不同。
当runid等于“*”时,作用是Sentinel节点直接交换对主节点下线的判定。
当runid等于当前Sentinel节点的runid时,作用是当前Sentinel节点希望目标Sentinel节点同意自己成为领导者的请求。 例如sentinel-1节点对主节点做主观下线后,会向其余Sentinel节点(假设sentinel-2和sentinel-3节点)发送该命令: 返回结果包含三个参数,如下所示: down_state:目标Sentinel节点对于主节点的下线判断,1是下线,0是在线。 leader_runid:当leader_runid等于“*”时,代表返回结果是用来做主节点是否不可达,当leader_runid等于具体的runid,代表目标节点同意runid成为领导者。 leader_epoch:领导者纪元。
3.领导者Sentinel节点选举
假如Sentinel节点对于主节点已经做了客观下线,那么是不是就可以立即进行故障转移了?当然不是,实际上故障转移的工作只需要一个Sentinel节点来完成即可,所以Sentinel节点之间会做一个领导者选举的工作,选出一个Sentinel节点作为领导者进行故障转移的工作。
Redis使用了Raft算法实现领导者选举,因为Raft算法相对比较抽象和复杂,以及篇幅所限,所以给出一个Redis Sentinel进行领导者选举的大致思路:
- 每个在线的Sentinel节点都有资格成为领导者,当它确认主节点主观下线时候,会向其他Sentinel节点发送sentinel is-master-down-by-addr命令,要求将自己设置为领导者。
- 收到命令的Sentinel节点,如果没有同意过其他Sentinel节点的sentinelis-master-down-by-addr命令,将同意该请求,否则拒绝。
- 如果该Sentinel节点发现自己的票数已经大于等于max(quorum,num(sentinels)/2+1),那么它将成为领导者。
- 如果此过程没有选举出领导者,将进入下一次选举。
4.故障转移
领导者选举出的Sentinel节点负责故障转移,具体步骤如下:
- 在从节点列表中选出一个节点作为新的主节点。
- Sentinel领导者节点会对第一步选出来的从节点执行slaveof no one命令让其成为主节点。
- Sentinel领导者节点会向剩余的从节点发送命令,让它们成为新主节点的从节点,复制规则和parallel-syncs参数有关。
- Sentinel节点集合会将原来的主节点更新为从节点,并保持着对其关注,当其恢复后命令它去复制新的主节点。
六、开发与运维中的问题
1.故障转移日志分析
模拟故障的几种比较典型的方法:
- 强制杀掉对应节点的进程号,这样可以模拟出宕机的效果。
- 使用Redis的debug sleep命令,让节点进入睡眠状态,这样可以模拟阻塞的效果。
- 使用Redis的shutdown命令,模拟正常的停掉Redis。
2.节点运维
(1)临时下线和永久下线:
- 临时下线:暂时将节点关掉,之后还会重新启动,继续提供服务。
- 永久下线:将节点关掉后不再使用,需要做一些清理工作,如删除配置文件、持久化文件、日志文件。
(2)节点下线:
无论是主节点、从节点还是Sentinel节点,下线的原因:
- 节点所在的机器出现了不稳定或者即将过保被回收。
- 节点所在的机器性能比较差或者内存比较小,无法支撑应用方的需求。
- 节点自身出现服务不正常情况,需要快速处理。
如何进行下线操作:
- 如果需要对主节点进行下线,比较合理的做法是选出一个“合适”(例如性能更高的机器)的从节点,使用sentinel failover功能将从节点晋升主节点。
- 如果需要对从节点或者Sentinel节点进行下线,只需要确定好是临时还是永久下线后执行相应操作即可。如果使用了读写分离,下线从节点需要保证应用方可以感知从节点的下线变化,从而把读取请求路由到其他节点。需要注意的是,Sentinel节点依然会对这些下线节点进行定期监控,会造成一定的网络资源浪费。
(3)节点上线
如何进行上线操作:
- 添加从节点的场景:①使用了读写分离,但现有的从节点无法支撑应用方的流量。②主节点没有可用的从节点,无法支持故障转移。③添加一个更强悍的从节点利用手动failover替换主节点。
- 添加从节点方法:添加slaveof{masterIp}{masterPort}的配置,使用redis-server启动即可,它将被Sentinel节点自动发现。
- 添加Sentinel节点的场景:①当前Sentinel节点数量不够,无法达到Redis Sentinel健壮性要求或者无法达到票数。②原Sentinel节点所在机器需要下线。
- 添加Sentinel节点的方法:添加sentinel monitor主节点的配置,使用redis-sentinel启动即可,它将被其余Sentinel节点自动发现。
- 添加主节点的场景:因为Redis Sentinel中只能有一个主节点,所以不需要添加主节点。
- 添加主节点的方法:可以使用Sentinel failover手动故障转移。
(4)Sentinel节点配置
- Sentinel节点配置尽可能一致,这样在判断节点故障时会更加准确。
- Sentinel节点支持的命令非常有限,例如config命令是不支持的,而Sentinel节点也需要dir、loglevel之类的配置,所以尽量在一开始规划好,不过所幸Sentinel节点不存储数据,如果需要修改配置,重新启动即可。
3.高可用读写分离
(1)从节点的作用
从节点的作用一般有两个:
- 当主节点出现故障时,作为主节点的后备“顶”上来实现故障转移,Redis Sentinel已经实现了该功能的自动化,实现了真正的高可用。
- 扩展主节点的读能力,尤其是在读多写少的场景非常适用,例如如下场景:
如果slave-1节点出现故障,首先客户端client-1将与其失联,其次Sentinel节点只会对该节点做主观下线,因为Redis Sentinel的故障转移是针对主节点的。
所以很多时候,Redis Sentinel中的从节点仅仅是作为主节点一个热备,不让它参与客户端的读操作,就是为了保证整体高可用性,但实际上这种使用方法还是有一些浪费,尤其是在有很多从节点或者确实需要读写分离的场景,所以如何实现从节点的高可用是非常有必要的。
(2)Redis Sentinel读写分离设计思路
Redis Sentinel在对各个节点的监控中,如果有对应事件的发生,都会发出相应的事件消息(见表9-6),其中和从节点变动的事件有以下几个:
- +switch-master:切换主节点(原来的从节点晋升为主节点),说明减少了某个从节点。
- +convert-to-slave:切换从节点(原来的主节点降级为从节点),说明添加了某个从节点。
- +sdown:主观下线,说明可能某个从节点可能不可用(因为对从节点不会做客观下线),所以在实现客户端时可以采用自身策略来实现类似主观下线的功能。
- +reboot:重新启动了某个节点,如果它的角色是slave,那么说明添加了某个从节点。
设计思路:
在设计Redis Sentinel的从节点高可用时,只要能够实时掌握所有从节点的状态,把所有从节点看做一个资源池,无论是上线还是下线从节点,客户端都能及时感知到(将其从资源池中添加或者删除),这样从节点的高可用目标就达到了。