Sentinel(哨兵)是Redis 的高可用性解決方案:經過哨兵能夠建立一個當主服務器出現故障時自動將從服務器升級爲主服務器的一個分佈式系統。解決了主從複製出現故障時須要人爲干預的問題。
這篇介紹哨兵的搭建,以及哨兵是如何進行哨兵發現和主從切換等功能。git
在原先主從的基礎上,每臺機器啓動一個哨兵。架構圖以下
github
配置文件以下redis
daemonize yes bind 0.0.0.0 port 26379 dir "/usr/soft/redis" loglevel notice logfile "/usr/soft/redis/sentinel.log" # 修改改爲5秒 sentinel monitor learnSentinelMaster 192.168.17.101 6379 2 sentinel down-after-milliseconds learnSentinelMaster 5000 sentinel config-epoch learnSentinelMaster 1
有兩種方式服務器
src/redis-sentinel sentinel.conf
src/redis-server sentinel.conf --sentinel
哨兵搭建的過程以下網絡
哨兵集羣搭建完畢後,日誌內容以下架構
啓動後配置文件sentinel.conf會增長內容異步
daemonize yes bind 0.0.0.0 port 26379 dir "/usr/soft/redis" loglevel notice logfile "/usr/soft/redis/sentinel.log" # 修改改爲5秒 sentinel myid b457cbbcda1991f540d56c6e8faea123a668b16c sentinel monitor learnSentinelMaster 192.168.17.101 6379 2 sentinel down-after-milliseconds learnSentinelMaster 5000 # Generated by CONFIG REWRITE sentinel config-epoch learnSentinelMaster 1 sentinel leader-epoch learnSentinelMaster 0 sentinel known-slave learnSentinelMaster 192.168.17.102 6379 sentinel known-slave learnSentinelMaster 192.168.17.103 6379 sentinel known-sentinel learnSentinelMaster 192.168.17.101 26379 f0230d4fdf1ffc7865852de71f16b3017cc1617c sentinel known-sentinel learnSentinelMaster 192.168.17.102 26379 5b1099513713310eba94e69513dba76cf0ac2222 sentinel current-epoch 1
接下來看看哨兵集羣啓動過程當中,Redis內部發生了什麼。步驟以下分佈式
Sentinel 本質上只是一個運行在特殊模式下的Redis服務器,因此初始化時和不一樣的Redis服務器初始化沒什麼較大的區別。有區別的就是哨兵服務器並不會載入RDB文件和AOF文件,還有一些命令功能哨兵服務器不使用。測試
初始化服務器以後,哨兵服務器會將一部分普通Redis的服務器使用的代碼替換成哨兵專用的代碼。如下就是哨兵的命令列表,代碼文件在https://github.com/antirez/redis/blob/unstable/src/sentinel.cui
struct redisCommand sentinelcmds[] = { {"ping",pingCommand,1,"",0,NULL,0,0,0,0,0}, {"sentinel",sentinelCommand,-2,"",0,NULL,0,0,0,0,0}, {"subscribe",subscribeCommand,-2,"",0,NULL,0,0,0,0,0}, {"unsubscribe",unsubscribeCommand,-1,"",0,NULL,0,0,0,0,0}, {"psubscribe",psubscribeCommand,-2,"",0,NULL,0,0,0,0,0}, {"punsubscribe",punsubscribeCommand,-1,"",0,NULL,0,0,0,0,0}, {"publish",sentinelPublishCommand,3,"",0,NULL,0,0,0,0,0}, {"info",sentinelInfoCommand,-1,"",0,NULL,0,0,0,0,0}, {"role",sentinelRoleCommand,1,"l",0,NULL,0,0,0,0,0}, {"client",clientCommand,-2,"rs",0,NULL,0,0,0,0,0}, {"shutdown",shutdownCommand,-1,"",0,NULL,0,0,0,0,0} };
在應用了哨兵專用的代碼以後,哨兵會初始化狀態,這個哨兵狀態結構包含了服務器中全部和哨兵功能有關的狀態。結構體代碼位置在也在entinel.c文件中,結構體代碼以下
/* Main state. */ struct sentinelState { char myid[CONFIG_RUN_ID_SIZE+1]; /* 當前哨兵ID. */ uint64_t current_epoch; /* 當前紀元. */ dict *masters; /* 存放哨兵監視的主服務器,key是主服務器的名字,value是指向主服務器的指針tances. */ int tilt; /* 是否處於TILT模式? */ int running_scripts; /* 目前執正在執行的腳本數量 */ mstime_t tilt_start_time; /* 進入TITL開始的時間 */ mstime_t previous_time; /* 最後一次執行時間處理器的時間*/ list *scripts_queue; /* 包含了全部須要執行的用戶腳本 */ char *announce_ip; /* 當配置文件中的announce_ip不爲空時,記錄着這些IP地址 */ int announce_port; /* 配置文件中的announce_port */ unsigned long simfailure_flags; /* 故障模擬 */ int deny_scripts_reconfig; /* 是否容許哨兵在運行時修改腳本位置? */ } sentinel;
啓動哨兵出現的日誌以下
# oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo # Redis version=4.9.103, bits=64, commit=00000000, modified=0, pid=2100, just started # Configuration loaded * Increased maximum number of open files to 10032 (it was originally set to 1024). * Running mode=sentinel, port=26379. # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
哨兵id以下
# Sentinel ID is b457cbbcda1991f540d56c6e8faea123a668b16c
初始化哨兵的最後一步是建立連向被監視的主服務器的網絡鏈接,哨兵將會成爲主服務器的客戶端。哨兵會向主服務器建立兩個異步網絡鏈接
啓動哨兵過程到這裏就結束了,接下來將進入下個環節。
獲取信息階段會獲取主服務器信息和從服務器信息以及哨兵的相關信息。
哨兵默認會以10s一次的頻率,發送命令鏈接向被監視的主服務器發送INFO命令,並經過分析INFO命令的回覆來獲取主服務器的當前信息。
監控主服務器
# +monitor master learnSentinelMaster 192.168.17.101 6379 quorum 2
經過分析主服務器返回的信息,能夠獲取到兩方面的信息
獲取到從服務器信息以後,哨兵會更新保存主服務器實例結構的slaves字典。
當哨兵發現主服務器有新的從服務器出現時,哨兵會爲這個新的從服務器建立相應的實例結構以外,還會建立到從服務器的命令鏈接和訂閱鏈接。
發現新的從服務器會出現以下日誌
* +slave slave 192.168.17.102:6379 192.168.17.102 6379 @ learnSentinelMaster 192.168.17.101 6379 * +slave slave 192.168.17.103:6379 192.168.17.103 6379 @ learnSentinelMaster 192.168.17.101 6379
在建立命令鏈接以後,會發送INFO命令獲取信息。經過從服務器回覆的信息中,能夠得到如下內容
獲取到這些信息以後,會對以前建立的從服務器實例結構進行更新。
在獲取其餘哨兵的信息以前,先要知道向主服務器和從服務器發送信息
和接收來自主服務器和從服務器的頻道信息
。
發送的命令格式以下
PUBLISH __sentinel__:hello "<s_ip>,<s_port>,<s_runid>,<s_epoch>,<m_name>,<m_ip>,<m_port>,<m_epoch>"
PUBLISH是發佈消息的命令,__sentinel__:hello
是頻道的名稱,後面就是一些參數,參數信息以下
當Sentinel與一個主服務器或者從服務器創建起訂閱鏈接以後,Sentinel就會經過訂閱鏈接,向服務器發送命令:
SUBSCRIBE __sentinel__:hello
表示哨兵訂閱__sentinel__:hello
這個頻道,接收這個頻道的消息。
其餘哨兵能夠經過接收這個頻道的消息來發現其餘哨兵的存在。
經過接收__sentinel__:hello
頻道的消息能夠發現其餘哨兵的存在。當哨兵接收到一條來自__sentinel__:hello
頻道的消息時,會出現下方
注:sentinels字典是專門保存哨兵信息的
當Sentinel經過頻道信息發現一個新的Sentinel時,不只會在自身的sentinels字典中爲新Sentinel建立實例結構,還會建立一個連向新Sentinel的命令鏈接,同時新的Sentinel也會建立一個連向這個Sentinel的命令鏈接,最終多個Sentinel將造成一個互相鏈接的網絡。
注:哨兵之間不會建立訂閱鏈接
發現哨兵的日誌以下
* +sentinel sentinel f0230d4fdf1ffc7865852de71f16b3017cc1617c 192.168.17.101 26379 @ learnSentinelMaster 192.168.17.101 6379 * +sentinel sentinel 5b1099513713310eba94e69513dba76cf0ac2222 192.168.17.102 26379 @ learnSentinelMaster 192.168.17.101 6379
模擬101主服務器掉錢的過程以下
斷線重連的日誌內容以下
接下來開始分析斷線過程當中的每一步驟
在默認狀況下,Sentinel會以每秒一次的頻率向全部與它建立了命令鏈接的實例(主,從,其餘Sentinel)發送 PING命令 ,經過判斷返回的內容來判斷是否在線,命令分爲有效回覆和無效回覆兩種。
配置文件中的down-after-milliseconds參數能夠設置指定時間,在這個時間段內沒有收到回覆則斷定該服務器處於主觀下線狀態。
sentinel down-after-milliseconds learnSentinelMaster 5000
如今101客戶端上輸入如下命令,讓服務器睡眠30秒
debug sleep 30
此時查看哨兵日誌,等待5秒後出現如下內容
# +sdown master learnSentinelMaster 192.168.17.101 6379
當一個哨兵將一個主服務器判斷爲主觀下線以後,會向其餘監視該主服務器的哨兵進行詢問,當有足夠數量的哨兵斷定主服務器下線時,會執行故障轉移操做 。
注:這裏不對哨兵之間互相發送的消息進行說明
在配置中能夠決定斷定主服務器進入客觀下線狀態所須要的服務器數量,下方配置的最後一個參數就是所需的哨兵數量,這裏填寫的是2
sentinel monitor learnSentinelMaster 192.168.17.101 6379 2
下面的日誌說明了主服務器101已經進入客觀下線狀態
# +odown master learnSentinelMaster 192.168.17.101 6379 #quorum 2/2
當前紀元被更新 ,試圖故障恢復
# +new-epoch 2 # +try-failover master learnSentinelMaster 192.168.17.101 6379
此時開始準備選舉領頭哨兵進行故障轉移
當主服務器被斷定爲客觀下線以後,各個哨兵服務器將會選舉出一個領頭哨兵,有這個領頭哨兵對下線服務器進行故障轉移操做,選舉領頭哨兵的規則以下:
下方就是選舉領頭哨兵的日誌內容
# +vote-for-leader b457cbbcda1991f540d56c6e8faea123a668b16c 2 # 5b1099513713310eba94e69513dba76cf0ac2222 voted for b457cbbcda1991f540d56c6e8faea123a668b16c 2 # f0230d4fdf1ffc7865852de71f16b3017cc1617c voted for b457cbbcda1991f540d56c6e8faea123a668b16c 2
在選舉出領頭哨兵以後,領頭哨兵須要執行故障轉移操做,操做主要分爲三個步驟
此時,領頭哨兵須要選出新的主服務器,而後向新的主服務器發送SLAVEOF no one命令,將這個從服務器轉換爲主服務器。
選擇過程會過濾掉不符合要求的服務器:
新的主服務器只選擇經過上面的測試,並在上面的標準基礎上排序:
選出合適的從節點做爲新的主節點
2101:X 31 Jul 19:13:35.709 # +failover-state-select-slave master learnSentinelMaster 192.168.17.101 6379 2101:X 31 Jul 19:13:35.793 # +selected-slave slave 192.168.17.102:6379 192.168.17.102 6379 @ learnSentinelMaster 192.168.17.101 6379
開始講102轉換爲主節點
* +failover-state-send-slaveof-noone slave 192.168.17.102:6379 192.168.17.102 6379 @ learnSentinelMaster 192.168.17.101 6379 * +failover-state-wait-promotion slave 192.168.17.102:6379 192.168.17.102 6379 @ learnSentinelMaster 192.168.17.101 6379 # +promoted-slave slave 192.168.17.102:6379 192.168.17.102 6379 @ learnSentinelMaster 192.168.17.101 6379 # +failover-state-reconf-slaves master learnSentinelMaster 192.168.17.101 6379
當新的主服務器出現以後,領頭哨兵會向其餘從服務器發送slaveof 命令去複製新的主服務器。
下方記錄了領頭哨兵向從服務器發送 SALVEOF命令去複製新的主服務器。
* +slave-reconf-sent slave 192.168.17.103:6379 192.168.17.103 6379 @ learnSentinelMaster 192.168.17.101 6379 * +slave-reconf-inprog slave 192.168.17.103:6379 192.168.17.103 6379 @ learnSentinelMaster 192.168.17.101 6379 * +slave-reconf-done slave 192.168.17.103:6379 192.168.17.103 6379 @ learnSentinelMaster 192.168.17.101 6379
這時候若是下線的主服務器重啓上線了怎麼辦?這也是故障轉移要作的最後一步,將已下線的主服務器設置爲新的主服務器的從服務器。當下線的主服務器從新上線時,哨兵就會向它發送SLAVEOF命令,讓他成爲新的主服務器的從服務器。
此時101服務器上線
# -odown master learnSentinelMaster 192.168.17.101 6379
故障轉移成功完成。全部slaves被從新配置爲新master的從
# +failover-end master learnSentinelMaster 192.168.17.101 6379 # +switch-master learnSentinelMaster 192.168.17.101 6379 192.168.17.102 6379
轉換101狀態
* +slave slave 192.168.17.101:6379 192.168.17.101 6379 @ learnSentinelMaster 192.168.17.102 6379 # +sdown slave 192.168.17.101:6379 192.168.17.101 6379 @ learnSentinelMaster 192.168.17.102 6379 # -sdown slave 192.168.17.101:6379 192.168.17.101 6379 @ learnSentinelMaster 192.168.17.102 6379 * +convert-to-slave slave 192.168.17.101:6379 192.168.17.101 6379 @ learnSentinelMaster 192.168.17.102 6379
這時候再次查看配置文件會發現多了一行sentinel current-epoch 2
#後臺啓動 daemonize yes bind 0.0.0.0 port 26379 dir "/usr/soft/redis" loglevel notice logfile "/usr/soft/redis/sentinel.log" # 修改改爲5秒 sentinel myid f0230d4fdf1ffc7865852de71f16b3017cc1617c sentinel monitor learnSentinelMaster 192.168.17.102 6379 2 sentinel down-after-milliseconds learnSentinelMaster 5000 # Generated by CONFIG REWRITE sentinel config-epoch learnSentinelMaster 2 sentinel leader-epoch learnSentinelMaster 2 sentinel known-slave learnSentinelMaster 192.168.17.103 6379 sentinel known-slave learnSentinelMaster 192.168.17.101 6379 sentinel known-sentinel learnSentinelMaster 192.168.17.103 26379 b457cbbcda1991f540d56c6e8faea123a668b16c sentinel known-sentinel learnSentinelMaster 192.168.17.102 26379 5b1099513713310eba94e69513dba76cf0ac2222 sentinel current-epoch 2
# Example sentinel.conf # 綁定IP地址 # bind 127.0.0.1 192.168.1.1 # 保護模式(是否禁止外部連接,除綁定的ip地址外) # protected-mode no # 當前Sentinel服務運行的端口 port 26379 # # sentinel announce-ip <ip> # sentinel announce-port <port> # Sentinel服務運行時使用的臨時文件夾 dir /tmp # 監聽地址爲ip:port的一個master sentinel monitor mymaster 127.0.0.1 6379 2 # 設置鏈接master和slave時的密碼,注意的是sentinel不能分別爲master和slave設置不一樣的密碼,所以master和slave的密碼應該設置相同。 # sentinel auth-pass mymaster MySUPER--secret-0123passw0rd # 指定了Sentinel認爲Redis實例已經失效所需的毫秒數 sentinel down-after-milliseconds mymaster 30000 # 指定了在執行故障轉移時,最多能夠有多少個從Redis實例在同步新的主實例,在從Redis實例較多的狀況下這個數字越小,同步的時間越長,完成故障轉移所需的時間就越長 sentinel parallel-syncs mymaster 1 # 若是在該時間(ms)內未能完成故障轉移操做,則認爲該故障轉移失敗 sentinel failover-timeout mymaster 180000 # 指定sentinel檢測到該監控的redis實例指向的實例異常時,調用的報警腳本。該配置項可選,可是很經常使用 # sentinel notification-script mymaster /var/redis/notify.sh # 當一個master因爲failover而發生改變時,這個腳本將會被調用,通知相關的客戶端關於master地址已經發生改變的信息。 # sentinel client-reconfig-script mymaster /var/redis/reconfig.sh
哨兵的配置文件:https://github.com/rainbowda/learnWay/tree/master/learnRedis/sentinel,有須要的能夠下載。