五. redis sentinel
- 參考視訊:https://coding.imooc.com/class/151.html
- 筆記下載下傳位址
- 歡迎star、fork、follow素質三連
redis單執行個體遠遠不能提供高效、具備資料備援和高可用(High Availability)能力的鍵值存儲服務。持久化和主從複制可以解決資料備援備份問題,但是沒有人工幹預情況下,redis伺服器當機後不可能自動回複,是以單純的主從複制和持久化技術不具備高可用特性。
高可用用來描述一個系統經過專門的設計,進而減少停工時間,進而保持服務的高度可用性。redis2.6提供了一個原生支援的技術:Sentinel(哨兵),利用Sentinel可以輕松地建構具備容錯能力的redis服務。
5.1 redis sentinel架構
5.1.1 主從複制問題
- 手動故障轉移:當master節點當機之後,如果不使用redis sentinel保證高可用,隻能進行手動故障轉移
- 寫能力和存儲能力受限:隻有一個master節點,master節點才有寫權限
5.2.2 redis sentinel架構

當master節點出現故障時,redis sentinel戶進行自動故障轉移過程:
- 多個sentinel發現并确認master出現問題
- 選舉出一個sentinel作為上司
- 選出一個slave作為master
- 上司sentinel通知其它slave結點成為新maste節點的slave節點
- 通知用戶端主從變化
- 等待當機的master節點上線,并成為新master節點的slave節點
除此之外,一套redis sentinel系統可以監控多套master-slave系統,通過master-name區分,可以大大節省系統資源。
5.2 sentinel配置
實作一個master-兩個slave主從複制模式,并且有三個sentinel節點對這三個節點進行監
注意
- master、slave、sentinel節點bind配置時不要使用預設值,指定需要bind的ip或者設定protecteed-mode no
# *** IMPORTANT ***
#
# By default Sentinel will not be reachable from interfaces different than
# localhost, either use the 'bind' directive to bind to a list of network
# interfaces, or disable protected mode with "protected-mode no" by
# adding it to this configuration file.
#
# Before doing that MAKE SURE the instance is protected from the outside
# world via firewalling or other means.
# For example you may use one of the following:
#
# bind 127.0.0.1 192.168.1.1 (這裡隐含了protected-mode yes)
#
# protected-mode no
- master、slave節點如果設定了密碼應該保持一緻(因為slave節點也有可能成為master節點),并且在sentinel配置檔案中要進行配置:
sentinel auth-pass mymaster xxxxxx
- 從節點應該設定:masterauth xxxxxx
- 注意不同機器之間開放端口或者直接關閉防火牆
5.2.1 master-slave節點配置
在192.168.227.128機器上配置一個master(端口7000),兩個slave節點(端口7001、7002):
# 主節點 simple-cnf-7000.conf
port 7000
daemonize yes
pidfile /var/run/redis-7000.pid
logfile "log-7000.log"
dir "/opt/soft/redis/data"
protected-mode no
requirepass tanglei
# 從節點
>> sed "s/7000/7001/g" simple-cnf-7000.conf > simple-cnf-7001.conf
>> sed "s/7000/7002/g" simple-cnf-7000.conf > simple-cnf-7002.conf
>> echo "slaveof 192.168.227.128 7000" >> simple-cnf-7001.conf
>> echo "masterauth tanglei" >> simple-cnf-7001.conf
>> echo "slaveof 192.168.227.128 7000" >> simple-cnf-7002.conf
>> echo "masterauth tanglei" >> simple-cnf-7002.conf
# 使用info指令檢視master狀态,slave節點狀态類似
>> ./redis-cli -p 7000 info replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=7001,state=online,offset=406,lag=1
slave1:ip=127.0.0.1,port=7002,state=online,offset=406,lag=1
master_replid:345b7892002943b979ba12f7580ef1b6e6aca742
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:406
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:406
5.2.2 sentinel配置
在192.168.227.129機器上配置三個sentinel節點,端口分别為:27000、27001、27002:
# sentinel sentinel-27000.conf
port 27000
daemonize yes
pidfile "/var/run/redis-sentinel-27000.pid"
logfile "log-27000"
dir "/opt/soft/redis/data"
# 監聽的master-slave,取名為mymaster,2:至少2個sentinel認為該mater有問題,才認為真正不可用
sentinel monitor mymaster 192.168.227.128 7001 2
# 監聽master是否可用的心跳線
sentinel down-after-milliseconds mymaster 15000
# 如果failover開始後18000ms内仍然沒有觸發failover操作,認為此次failover失敗
sentinel failover-timeout mymaster 180000
# 當發生failover準備切換時,指定最多可以有多少個slave同時對新的master進行同步
# 該數字越小,failover時間越長,但是越大,導緻越多的slave因為replication而不可用,不推薦修改
sentinel parallel-syncs mymaster 1
sentinel auth-pass mymaster tanglei
protected-mode no
# sentinel其它兩個節點
sed "s/27000/27001/g" sentinel-27000.conf > sentinel-27001.conf
sed "s/27000/27002/g" sentinel-27000.conf > sentinel-27002.conf
啟動三個sentinel節點:
>> ./redis-sentinel ../config/sentinel-27000.conf
>> ./redis-sentinel ../config/sentinel-27001.conf
>> ./redis-sentinel ../config/sentinel-27002.conf
# 檢視sentinel-27000節點sentinel相關資訊
>> ./redis-cli -p 27000 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=192.168.227.128:7000,slaves=2,sentinels=3
# 原本sentinel-27000.conf配置檔案也有變換,增加了以下内容:
# Generated by CONFIG REWRITE
sentinel leader-epoch mymaster 1
sentinel known-replica mymaster 192.168.227.128 7001
sentinel known-replica mymaster 192.168.227.128 7002
sentinel known-sentinel mymaster 192.168.227.129 27001 ee0a91044eb6a1bd9af1cb143b2924039fb76843
sentinel known-sentinel mymaster 192.168.227.129 27002 92ada4bb2d0fdd5c227a37b1dc7eb8cd737f57d8
sentinel current-epoch 1
檢視log-27000日志檔案:
1663:X 06 Jan 2020 03:19:06.672 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1663:X 06 Jan 2020 03:19:06.672 # Redis version=5.0.7, bits=64, commit=00000000, modified=0, pid=1663, just started
1663:X 06 Jan 2020 03:19:06.672 # Configuration loaded
1664:X 06 Jan 2020 03:19:06.673 * Increased maximum number of open files to 10032 (it was originally set to 1024).
1664:X 06 Jan 2020 03:19:06.673 * Running mode=sentinel, port=27000.
1664:X 06 Jan 2020 03:19:06.673 # Sentinel ID is 93009b479b2832edc4ac0312e81edb2f58170396
1664:X 06 Jan 2020 03:19:06.673 # +monitor master mymaster 192.168.227.128 7000 quorum 2
1664:X 06 Jan 2020 03:19:36.692 # +sdown slave 192.168.227.128:7001 127.0.0.1 7001 @ mymaster 192.168.227.128 7000
1664:X 06 Jan 2020 03:19:36.692 # +sdown slave 192.168.227.128:7002 127.0.0.1 7002 @ mymaster 192.168.227.128 7000
5.3 示範故障轉移
5.3.1 Java用戶端程式
package fun.sherman.sentinel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisSentinelPool;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
* @author sherman
*/
public class FailOver {
private static Logger logger = LoggerFactory.getLogger(FailOver.class);
public static void main(String[] args) {
final String masterName = "mymaster";
final String password = "tanglei";
Set<String> sentinels = new HashSet<String>();
sentinels.add("192.168.227.129:27000");
sentinels.add("192.168.227.129:27001");
sentinels.add("192.168.227.129:27002");
JedisSentinelPool sentinelPool = new JedisSentinelPool(masterName, sentinels);
int cnt = 0;
while (true) {
Jedis jedis = null;
try {
jedis = sentinelPool.getResource();
jedis.auth(password);
++cnt;
String key = "key-" + cnt;
String value = "value-" + cnt;
jedis.set(key, value);
TimeUnit.MICROSECONDS.sleep(10);
if (cnt % 100 == 0) {
logger.info("{} value is {}", key, jedis.get(key));
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
} finally {
if (jedis != null) {
jedis.close();
}
}
}
}
}
5.3.2 shutdown主節點
手動shutdown 192.168.227.128:7000主節點,上述Java一段時間ConnectException異常後能夠正常執行,并檢視sentinel對應的日志:
除此之外,還可以檢視兩個slave節點的配置檔案變換:
- 成為新的master節點的slave節點(7001):
# replicaof 192.168.128 7000
# masterauth "tanglei"
# 以上兩條指令已經被删除
port 7001
daemonize yes
pidfile "/var/run/redis-7001.pid"
logfile "log-7001.log"
dir "/opt/soft/redis/data"
protected-mode no
requirepass "tanglei"
- 另一個沒有晉升為master節點的slave節點(7002):
port 7002
daemonize yes
pidfile "/var/run/redis-7002.pid"
logfile "log-7002.log"
dir "/opt/soft/redis/data"
protected-mode no
requirepass "tanglei"
# replicaof 對應的端口發生變化,從7000轉換成7001
replicaof 192.168.227.128 7001
masterauth "tanglei"
- 當當機的master節點(7000)重新上線,檢視對應配置檔案:simple-cnf-7000.conf:
port 7000
daemonize yes
pidfile "/var/run/redis-7000.pid"
logfile "log-7000.log"
dir "/opt/soft/redis/data"
protected-mode no
requirepass "tanglei"
# 現在該節點已經是slave節點了
# Generated by CONFIG REWRITE
replicaof 192.168.227.128 7001
- 檢視sentinel-27000.conf中指定的日志檔案log-27000:
# 多了一句話:
2956:X 06 Jan 2020 06:15:38.717 * +convert-to-slave slave 192.168.227.128:7000 192.168.227.128 7000 @ mymaster 192.168.227.128 7001
5.4 三個定時任務
- 定時任務一:每10s每個sentinel對master和slave節點執行一次info操作,進而發現slave節點,确認主從關系
- 定時任務二:每2s每個sentinel通過master節點的_sentinel_:hello頻道進行交換資訊,例如判斷master是否當機、上司者選舉等都是通過該頻道進行互動
127.0.0.1:7000> pubsub channels
1) "__sentinel__:hello"
- 每1秒每個sentinel節點對其它sentinel和redis節點執行ping指令,用于心跳檢測和失敗判斷依據,和配置sentinel down-after-milliseconds mymaster 30000一起使用。當ping指令檢測失敗時,再過30秒如果仍然心跳檢測失敗,則認為該節點已經主觀當機,但是此時不能認為是真正當機,還需要進行投票超過quorum數量才能認為已經客觀當機。
5.5 主觀下線和客觀下線
- 主觀下線:每個sentinel節點對redis節點失敗的“偏見”,第三個定時任務中,如果某個sentinel進行心跳檢測失敗,并且在down-after-milliseconds mymaster配置的時間後仍然檢測失敗,則認為該redis是已經主觀下線
- 客觀下線:多個sentinel對某個redis節點進行投票,投票數量超過quorum規定的數量之後,就認為該redis節點已經客觀下線。注意客觀下線隻會針對master節點,slave節點不需要進行故障轉移:sentinel is-master-down-by-addr
5.6 上司者選舉
故障轉移時隻需要一個sentinel節點進行,是以需要多個sentinel進行上司者選舉,執行最終的故障轉移過程,通過:sentinel is-master-down-by-addr指令都希望自己成為上司者:
- 每個做主觀下線的sentinel節點向其它sentinel節點發送指令,要求将它設定為上司者
- 收到指令的sentinel節點如果沒有同意過其它sentinel節點發送的指令,那麼将同意該請求,否則拒絕
- 如果該sentinel節點發現自己的票數超過sentinel集合半數并且超過quorum數,那麼它将成為新的上司者
5.7 故障轉移
上司者選舉完成後,需要進行故障轉移:
- 從slave節點中選擇一個“合适的”節點作為master節點
- 對上面“合适的”節點執行slaveof no one指令,讓其成為新的maste節點
- 向剩餘的slave節點發送指令,讓它們成為新的master節點的slave節點,複制規則和parallel-syncs參數有關
- 更新對原來master節點配置的slave,并保持關注(由第三個定時任務完成),當恢複後,指令它成為新的master節點的slave節點
如何選擇“合适的”slave節點
- 選擇slave-priority(slave節點優先級)更高的salve節點,一般情況下,多個slave的優先級是相同的,除非不同slave節點所在的機器配置不同,我們希望當故障發生時,優先由機器性能更好的slave節點成為master節點,才進行配置
- 選擇複制偏移量最大的slave節點(該節點複制的最完整)
- 選擇run id最小的salve節點,即選擇最早啟動的節點
5.8 總結
故障轉移過程:
- 第三個定時任務(每秒ping其它sentinel和redis節點)檢測失敗時,對該節點标記為主觀下線(+sdown)
- 為防止虛假警報,版本标記了+sdown的哨兵,發送請求給其它sentinel,要求它們檢查該sentinel的狀态,當最終投票結果大于quorum時,判定該節點為客觀下線(+odown),并進行上司選舉
- 選舉出新的上司者後,開始進行故障轉移過程,包括:新的master節點執行slaveof no one,其他slave節點執行slaveof <新master>,監聽當機的master節點,一旦它上線,讓它成為新master的slave節點
幾個重要的消息:
+sdown:主觀下線
+odown:客觀下線
+switch-master:切換主節點(從節點晉升為master節點)
+convert-to-slave:切換從節點(原master節點降為從節點)