主从复制是Redis高可用的基础,可以在主节点挂掉之后,将从节点顶上去成为新的主节点,同时从节点还可以缓解主节点读的压力。但是主从复制最明显的缺陷就是,当主节点挂了之后,需要人工去将从节点晋升为主节点,并且需要修改客户端的主节点地址以及命令所有从节点去复制新的主节点的数据。
Redis Sentinel为Redis提供高可用性,可以实现自动化的故障转移并通知应用方,从节点晋升为主节点的整个过程不需要人工干预。 根据官网介绍,redis sentinel提供了以下功能:
为了实现高可用,sentinel一般都是由集群组成,类似zookeeper,最少都是3个节点组成。与zookeeper不同的是,sentinel采用的分布式一致性协议是raft协议。 sentinel节点是特殊的redis节点,只是它不存储数据,而且只支持部分命令。
sentinel通过三个定时监控任务实现对各个节点的发现和监控。
上面定时任务的第三个,每隔1秒向其它所有节点发送一个ping命令。如果节点超过down-after-milliseconds * 10的时间。(默认是30秒)都没有进行有效回复(+PONG、-LOADING 或者 -MASTERDOWN这三种都是有效回复),sentinel节点就会对该节点做失败判定,这种行为就叫做主观下线。
当sentinel主观下线的节点是主节点时,该sentinel节点就会向其它sentinel节点发送sentinel is-master-down-by-addr(这个命令还可以用于sentinel的leader选举) 命令询问他们对主节点的判断,当大于等于quorum(这个在sentinel的配置文件中配置的,如果sentinel节点是3个,quorum就配置为2。)个的sentinel都认为主节点已经挂了,那么该sentinel节点就会对主节点进行客观下线。(所以客观下线就是大部分的sentinel都认为主节点挂了)
当sentinel对主节点做客观下线之后,还不能开始故障转移。因为故障转移的工作只需要一个sentinel就可以完成,所以sentinel节点间会选举一个leader来完成这个工作。Redis采用了Raft算法实现leader选举,不过redis的实现与论文描述不完全一致(因为sentinel只有在进行故障转移的时候才需要leader,其它时间不需要leader)。
在sentinel中,谁先完成主观下线,就会获得主动权(最先发送命令获取其它sentinel对master的判断,然后最先完成客观下线)。当sentinel完成客观下线后,就会马上再次发送命令向其它sentinel索要投票,由于每个sentinel只能投一票,最开始索要的sentinel可以得到2票,其它sentinel最多还有1票,所以最后的leader一般就是最开始索要投票的那个sentinel。
选举出的Leader负责接下来的故障转移工作。就是从从节点中选出一个作为新的主节点。
先把Redis.conf复制三份到工作目录。然后修改主节点和从节点的配置。
主节点:master.conf
# 后台启动
daemonize yes
# 日志文件对应的位置(日志文件需要自己另行创建,位置可以自己改)
logfile /var/log/redis/master.log
# 端口号
port 6379
# 主节点的密码
# 这里为什么也要配置呢,因为发生故障转移后,当前主节点就会变成从节点
# sentinel只会自动重写主从关系,并不会重写masterauth
masterauth 123456
# 当前结点的密码
requirepass 123456
从节点1: slave1.conf
# 后台启动
daemonize yes
# 日志文件对应的位置
logfile /var/log/redis/slave1.log
# 端口号
port 6378
# 主节点的密码
masterauth 123456
# 当前结点的密码
requirepass 123456
# 指定主节点的地址和端口
slaveof 127.0.0.1 6379
从节点2: slave2.conf
# 后台启动
daemonize yes
# 日志文件对应的位置
logfile /var/log/redis/slave2.log
# 端口号
port 6377
# 主节点的密码
masterauth 123456
# 当前结点的密码
requirepass 123456
# 指定主节点的地址和端口
slaveof 127.0.0.1 6379
分别启动三个节点:
sudo redis-server master.conf
sudo redis-server slave1.conf
sudo redis-server slave2.conf
查看master节点的启动日志,以下是部分日志内容。
Replica 127.0.0.1:6378 asks for synchronization
Starting BGSAVE for SYNC with target: disk
9467:M 01 Feb 2020 16:45:29.290 * Background saving started by pid 9510
9510:C 01 Feb 2020 16:45:29.379 * DB saved on disk
9467:M 01 Feb 2020 16:45:29.526 * Background saving terminated with success
9467:M 01 Feb 2020 16:45:29.527 * Synchronization with replica 127.0.0.1:6378 succeeded
从日志可知,从节点先向主节点发送了一个同步请求,然后master节点开启了一个后台进程进行BGSAVE将当前内存的数据全部快照到磁盘文件中,然后再将快照文件的内容全部传送到从节点。
先把redis-sentinel.conf复制三份到工作目录,分别修改三个节点的以下配置:
节点1: sentinel1.conf
# 端口,也是默认端口
port 26379
# 后台启动
daemonize yes
# sentinel监控Redis主节点的配置
# 对应的命令:sentinel monitor <master-name> <ip> <redis-port> <quorum>
# master-name 指的是主节点的名字,可以自己定义
# ip 指的是主节点的ip地址
# redis-port 指的是主节点的端口
# quorum 这个值既用于主节点的客观下线,又用于sentinel的leader选举,具体可见上面的原理
sentinel monitor mymaster 127.0.0.1 6379 2
# 主节点响应sentinel的最大时间间隔,超过这个时间,sentinel认为主节点下线,默认30秒
sentinel down-after-milliseconds mymaster 3000
# 进行故障转移时,设置最多有多少个slave同时复制新的master
# 由于slave在复制时,会处于不可用的状态(要先清空数据,然后再加载主节点的数据)
# 所以设置一次允许一个slave去复制master
sentinel parallel-syncs master 1
# 故障转移的超时时间,默认3分钟。
# 当前sentinel对一个master节点进行故障转移后,需要等待这个时间后才能
# 再次对这个master节点进行故障转移(此时其它sentinel依然可以对该master进行故障转移)
# 进行故障转移时,配置所有slave复制master的最大时间,如果超时了,就不会按照parallel-syncs规则进行了
# sentinel进行leader选举时,如果没有选出leader,需要等到2倍这个时间才能进行下一次选举
sentinel failover-timeout master 180000
# 主节点如果设置了密码,就在这里配置
sentinel auth-pass mymaster 123456
# log文件的位置
logfile /var/log/redis/sentinel1.log
另外两个节点改一下端口就ok啦,我这里设置的是26378和26377。
启动三个sentinel节点,因为sentinel也是redis节点,所以也使用redis-server命令启动,只不过是用sentinel模式启动。
sudo redis-server sentinel1.conf --sentinel
sudo redis-server sentinel2.conf --sentinel
sudo redis-server sentinel3.conf --sentinel
来看一下sentinel的启动日志:
9791:X 01 Feb 2020 17:10:52.503 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
9791:X 01 Feb 2020 17:10:52.504 # Redis version=5.0.7, bits=64, commit=00000000, modified=0, pid=9791, just started
9791:X 01 Feb 2020 17:10:52.504 # Configuration loaded
9792:X 01 Feb 2020 17:10:52.523 * Increased maximum number of open files to 10032 (it was originally set to 1024).
9792:X 01 Feb 2020 17:10:52.528 * Running mode=sentinel, port=26379.
9792:X 01 Feb 2020 17:10:52.644 # Sentinel ID is 3b9f4dfc0dcf08b0d21478cf58d0227e1ba61564
9792:X 01 Feb 2020 17:10:52.645 # +monitor master mymaster 127.0.0.1 6379 quorum 2
9792:X 01 Feb 2020 17:10:52.647 * +slave slave 127.0.0.1:6377 127.0.0.1 6377 @ mymaster 127.0.0.1 6379
9792:X 01 Feb 2020 17:10:52.675 * +slave slave 127.0.0.1:6378 127.0.0.1 6378 @ mymaster 127.0.0.1 6379
9792:X 01 Feb 2020 17:11:18.371 * +sentinel sentinel 0bf2c2c19fcb5e0a77a79485ea7f2d8d012a03d8 127.0.0.1 26378 @ mymaster 127.0.0.1 6379
9792:X 01 Feb 2020 17:11:24.982 * +sentinel sentinel 7ea991444528f91b83fb86534dcb7db28e862507 127.0.0.1 26377 @ mymaster 127.0.0.1 6379
可以看到Redis是在sentinel模式下启动的,并且没有加载数据的过程(sentinel不需要数据)。然后会给sentinel分配一个ID,在sentinel集群中就是通过这个ID来识别sentinel的。
然后sentinel执行了监控主节点的命令(在配置文件中配置的),通过主节点获取到了两个从节点的信息。然后其它两个sentinel也启动了并加入了集群中。
在sentinel集群启动之后,会自动刷新配置,将主节点的两个从节点信息和其它的sentinel节点的信息写入配置中。
# Generated by CONFIG REWRITE
# sentinel的工作目录
dir "/home/monk-jay/redis/sentinel"
# 保护模式关闭
protected-mode no
# sentinel监控的master节点对应的领导纪元
# 这个就是用于领导选举时投票用的,记录的是当前投票的leader的纪元
# 这个值和当前纪元相同,说明当前节点已经投过票了
sentinel leader-epoch mymaster 5
# master下的两个slave节点信息
sentinel known-replica mymaster 127.0.0.1 6378
sentinel known-replica mymaster 127.0.0.1 6377
# 另外两个sentinel的信息
sentinel known-sentinel mymaster 127.0.0.1 26377 7ea991444528f91b83fb86534dcb7db28e862507
sentinel known-sentinel mymaster 127.0.0.1 26378 0bf2c2c19fcb5e0a77a79485ea7f2d8d012a03d8
# 当前纪元
sentinel current-epoch 5
进入客户端的方式与Redis一样,也是使用redis-cli。由于有多个redis实例,所以需要指定端口。
sudo redis-cli -p 26379
127.0.0.1:26379> info sentinel
# Sentinel
sentinel_masters:1 # 监控master的数量
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
# 监控的master的信息
master0:name=mymaster,status=ok,address=127.0.0.1:6379,slaves=2,sentinels=3
127.0.0.1:26379> sentinel masters
1) 1) "name"
2) "mymaster"
3) "ip"
4) "127.0.0.1"
5) "port"
6) "6379"
7) "runid"
8) "b16c053f53588d72b655e4d91d2d280a591bc5ff"
9) "flags"
10) "master"
11) "link-pending-commands"
12) "0"
13) "link-refcount"
14) "1"
15) "last-ping-sent"
16) "0"
17) "last-ok-ping-reply"
18) "960"
19) "last-ping-reply"
20) "960"
21) "down-after-milliseconds"
22) "30000"
23) "info-refresh"
24) "4073"
25) "role-reported"
26) "master"
27) "role-reported-time"
28) "58473813"
29) "config-epoch"
30) "5"
31) "num-slaves"
32) "2"
33) "num-other-sentinels"
34) "2"
35) "quorum"
36) "2"
37) "failover-timeout"
38) "180000"
39) "parallel-syncs"
40) "1"
sentinel master <master_name>
sentinel slaves <master_name>
127.0.0.1:26379> sentinel get-master-addr-by-name mymaster
1) "127.0.0.1"
2) "6379"
sentinel有个命令:强制当前sentinel执行故障转移(不会跟其他sentinel商量),当故障转移完成后,其它sentinel节点会按照故障转移的结果进行自身配置的更新。该命令可以模拟master宕机,然后进行故障转换。
127.0.0.1:26379> sentinel failover mymaster
OK
然后来看一下当前sentinel的日志。
9792:X 02 Feb 2020 11:19:23.353 # Executing user requested FAILOVER of ‘mymaster‘
9792:X 02 Feb 2020 11:19:23.353 # +new-epoch 6
9792:X 02 Feb 2020 11:19:23.353 # +try-failover master mymaster 127.0.0.1 6379
9792:X 02 Feb 2020 11:19:23.431 # +vote-for-leader 3b9f4dfc0dcf08b0d21478cf58d0227e1ba61564 6
9792:X 02 Feb 2020 11:19:23.431 # +elected-leader master mymaster 127.0.0.1 6379
9792:X 02 Feb 2020 11:19:23.431 # +failover-state-select-slave master mymaster 127.0.0.1 6379
9792:X 02 Feb 2020 11:19:23.523 # +selected-slave slave 127.0.0.1:6378 127.0.0.1 6378 @ mymaster 127.0.0.1 6379
9792:X 02 Feb 2020 11:19:23.524 * +failover-state-send-slaveof-noone slave 127.0.0.1:6378 127.0.0.1 6378 @ mymaster 127.0.0.1 6379
9792:X 02 Feb 2020 11:19:23.617 * +failover-state-wait-promotion slave 127.0.0.1:6378 127.0.0.1 6378 @ mymaster 127.0.0.1 6379
9792:X 02 Feb 2020 11:19:24.611 # +promoted-slave slave 127.0.0.1:6378 127.0.0.1 6378 @ mymaster 127.0.0.1 6379
9792:X 02 Feb 2020 11:19:24.612 # +failover-state-reconf-slaves master mymaster 127.0.0.1 6379
9792:X 02 Feb 2020 11:19:24.659 * +slave-reconf-sent slave 127.0.0.1:6377 127.0.0.1 6377 @ mymaster 127.0.0.1 6379
9792:X 02 Feb 2020 11:19:25.605 * +slave-reconf-inprog slave 127.0.0.1:6377 127.0.0.1 6377 @ mymaster 127.0.0.1 6379
9792:X 02 Feb 2020 11:19:25.606 * +slave-reconf-done slave 127.0.0.1:6377 127.0.0.1 6377 @ mymaster 127.0.0.1 6379
9792:X 02 Feb 2020 11:19:25.671 # +failover-end master mymaster 127.0.0.1 6379
9792:X 02 Feb 2020 11:19:25.672 # +switch-master mymaster 127.0.0.1 6379 127.0.0.1 6378
9792:X 02 Feb 2020 11:19:25.672 * +slave slave 127.0.0.1:6377 127.0.0.1 6377 @ mymaster 127.0.0.1 6378
9792:X 02 Feb 2020 11:19:25.673 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6378
此时再执行info sentinel会发现master节点已经更改。
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,status=ok,address=127.0.0.1:6378,slaves=2,sentinels=3
再去查看slave2的配置文件,会发现slaveof配置被移除了,换成了以下配置。
# 这个是新的master的地址和端口,说明salve2是在复制这个新master
# 而且可以看到它是被配置重写自动生成的
# Generated by CONFIG REWRITE
replicaof 127.0.0.1 6378
master.conf也一样,因为它现在是新master的从节点,也被写入了上面这个配置。
先查看一下当前redis的进程:
$ ps -ef | grep redis
root 9523 1 0 Feb01 ? 00:00:20 redis-server *:6377
root 9792 1 0 Feb01 ? 00:00:28 redis-server *:26379 [sentinel]
root 9810 1 0 Feb01 ? 00:00:29 redis-server *:26378 [sentinel]
root 9821 1 0 Feb01 ? 00:00:28 redis-server *:26377 [sentinel]
monk-jay 9889 8399 0 Feb01 tty4 00:00:00 redis-cli -p 26379
root 10039 1 0 Feb01 ? 00:00:10 redis-server *:6379
root 10105 1 0 Feb01 ? 00:00:16 redis-server *:6378
monk-jay 10254 8035 0 11:44 tty2 00:00:00 grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn redis
因为我当前的master的端口是6378,所以我现在将它对应的进程杀死。
# 10105是6378端口这个进程对应的pid
sudo kill -9 10105
再次输入ps -ef | grep redis
可以看到6378端口对应的进程已经没了,模拟真实的宕机。
下面来看看sentinel的日志:
9810:X 02 Feb 2020 11:47:08.158 # +sdown master mymaster 127.0.0.1 6378
9810:X 02 Feb 2020 11:47:08.231 # +odown master mymaster 127.0.0.1 6378 #quorum 2/2
9810:X 02 Feb 2020 11:47:08.231 # +new-epoch 7
9810:X 02 Feb 2020 11:47:08.231 # +try-failover master mymaster 127.0.0.1 6378
9810:X 02 Feb 2020 11:47:08.339 # +vote-for-leader 0bf2c2c19fcb5e0a77a79485ea7f2d8d012a03d8 7
9810:X 02 Feb 2020 11:47:08.367 # 7ea991444528f91b83fb86534dcb7db28e862507 voted for 7ea991444528f91b83fb86534dcb7db28e862507 7
9810:X 02 Feb 2020 11:47:08.421 # 3b9f4dfc0dcf08b0d21478cf58d0227e1ba61564 voted for 0bf2c2c19fcb5e0a77a79485ea7f2d8d012a03d8 7
9810:X 02 Feb 2020 11:47:08.451 # +elected-leader master mymaster 127.0.0.1 6378
9810:X 02 Feb 2020 11:47:08.451 # +failover-state-select-slave master mymaster 127.0.0.1 6378
9810:X 02 Feb 2020 11:47:08.507 # +selected-slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6378
9810:X 02 Feb 2020 11:47:08.507 * +failover-state-send-slaveof-noone slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6378
9810:X 02 Feb 2020 11:47:08.563 * +failover-state-wait-promotion slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6378
9810:X 02 Feb 2020 11:47:09.443 # +promoted-slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6378
9810:X 02 Feb 2020 11:47:09.444 # +failover-state-reconf-slaves master mymaster 127.0.0.1 6378
9810:X 02 Feb 2020 11:47:09.460 * +slave-reconf-sent slave 127.0.0.1:6377 127.0.0.1 6377 @ mymaster 127.0.0.1 6378
9810:X 02 Feb 2020 11:47:10.423 * +slave-reconf-inprog slave 127.0.0.1:6377 127.0.0.1 6377 @ mymaster 127.0.0.1 6378
9810:X 02 Feb 2020 11:47:10.424 * +slave-reconf-done slave 127.0.0.1:6377 127.0.0.1 6377 @ mymaster 127.0.0.1 6378
9810:X 02 Feb 2020 11:47:10.490 # +failover-end master mymaster 127.0.0.1 6378
9810:X 02 Feb 2020 11:47:10.490 # +switch-master mymaster 127.0.0.1 6378 127.0.0.1 6379
9810:X 02 Feb 2020 11:47:10.491 * +slave slave 127.0.0.1:6377 127.0.0.1 6377 @ mymaster 127.0.0.1 6379
9810:X 02 Feb 2020 11:47:10.491 * +slave slave 127.0.0.1:6378 127.0.0.1 6378 @ mymaster 127.0.0.1 6379
9810:X 02 Feb 2020 11:47:40.537 # +sdown slave 127.0.0.1:6378 127.0.0.1 6378 @ mymaster 127.0.0.1 6379
可以看到第一行,显示sdown,说明sentinel1主观下线了当前master。然后sentinel们开始协商master的状态,有大于等于quorum个sentinel认为master已经挂了,所以sentinel客观下线了master。然后进入了一个新纪元。其它的就跟上面模拟宕机的一样啦。
原文:https://www.cnblogs.com/weifeng1463/p/13561210.html