主從複製是Redis高可用的基礎,能夠在主節點掛掉以後,將從節點頂上去成爲新的主節點,同時從節點還能夠緩解主節點讀的壓力。可是主從複製最明顯的缺陷就是,當主節點掛了以後,須要人工去將從節點晉升爲主節點,而且須要修改客戶端的主節點地址以及命令全部從節點去複製新的主節點的數據。整個過程須要人工干預,很是麻煩(若是半夜主節點掛了,運維就慘了)git
Redis Sentinel爲Redis提供高可用性,能夠實現自動化的故障轉移並通知應用方,從節點晉升爲主節點的整個過程不須要人工干預。 根據官網介紹,redis sentinel提供瞭如下功能:redis
爲了實現高可用,sentinel通常都是由集羣組成,相似zookeeper,最少都是3個節點組成。與zookeeper不一樣的是,sentinel採用的分佈式一致性協議是raft協議。 sentinel節點是特殊的redis節點,只是它不存儲數據,並且只支持部分命令。算法
sentinel經過三個定時監控任務實現對各個節點的發現和監控。bash
上面定時任務的第三個,每隔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)。ide
在sentinel中,誰先完成主觀下線,就會得到主動權(最早發送命令獲取其它sentinel對master的判斷,而後最早完成客觀下線)。當sentinel完成客觀下線後,就會立刻再次發送命令向其它sentinel索要投票,因爲每一個sentinel只能投一票,最開始索要的sentinel能夠獲得2票,其它sentinel最多還有1票,因此最後的leader通常就是最開始索要投票的那個sentinel。 svn
選舉出的Leader負責接下來的故障轉移工做。就是從從節點中選出一個做爲新的主節點。ui
先把Redis.conf複製三份到工做目錄。而後修改主節點和從節點的配置。spa
主節點: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。而後進入了一個新紀元。其它的就跟上面模擬宕機的同樣啦。