redis系列:哨兵

1 簡介

Sentinel(哨兵)是Redis 的高可用性解決方案:經過哨兵能夠建立一個當主服務器出現故障時自動將從服務器升級爲主服務器的一個分佈式系統。解決了主從複製出現故障時須要人爲干預的問題。
這篇介紹哨兵的搭建,以及哨兵是如何進行哨兵發現和主從切換等功能。git

2 準備工做

在原先主從的基礎上,每臺機器啓動一個哨兵。架構圖以下 github

2.1 配置

配置文件以下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
複製代碼

2.2 啓動方式

有兩種方式bash

src/redis-sentinel sentinel.conf
複製代碼
src/redis-server sentinel.conf --sentinel
複製代碼

3 開始搭建

哨兵搭建的過程以下服務器

哨兵集羣搭建完畢後,日誌內容以下網絡

啓動後配置文件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
複製代碼

4 啓動流程

接下來看看哨兵集羣啓動過程當中,Redis內部發生了什麼。步驟以下異步

  1. 初始化服務器
  2. 使用Sentinel專用代碼
  3. 初始化Sentinel狀態
  4. 建立連向主服務器的網絡鏈接

4.1 初始化服務器

Sentinel 本質上只是一個運行在特殊模式下的Redis服務器,因此初始化時和不一樣的Redis服務器初始化沒什麼較大的區別。有區別的就是哨兵服務器並不會載入RDB文件和AOF文件,還有一些命令功能哨兵服務器不使用。分佈式

4.2 使用Sentinel專用代碼

初始化服務器以後,哨兵服務器會將一部分普通Redis的服務器使用的代碼替換成哨兵專用的代碼。如下就是哨兵的命令列表,代碼文件在github.com/antirez/red…測試

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}
};
複製代碼

4.3 初始化Sentinel狀態

在應用了哨兵專用的代碼以後,哨兵會初始化狀態,這個哨兵狀態結構包含了服務器中全部和哨兵功能有關的狀態。結構體代碼位置在也在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
複製代碼

4.4 建立連向主服務器的網絡鏈接

初始化哨兵的最後一步是建立連向被監視的主服務器的網絡鏈接,哨兵將會成爲主服務器的客戶端。哨兵會向主服務器建立兩個異步網絡鏈接

  1. 命令鏈接,用於向主服務器發送命令,並接受命令。
  2. 訂閱鏈接,專門用於訂閱主服務器的_sentinel_:hello頻道。

啓動哨兵過程到這裏就結束了,接下來將進入下個環節。

5 獲取信息

獲取信息階段會獲取主服務器信息和從服務器信息以及哨兵的相關信息。

5.1 獲取主服務器信息

哨兵默認會以10s一次的頻率,發送命令鏈接向被監視的主服務器發送INFO命令,並經過分析INFO命令的回覆來獲取主服務器的當前信息。

監控主服務器

# +monitor master learnSentinelMaster 192.168.17.101 6379 quorum 2
複製代碼

經過分析主服務器返回的信息,能夠獲取到兩方面的信息

  1. 主服務器自己的信息
  2. 從服務器的信息

獲取到從服務器信息以後,哨兵會更新保存主服務器實例結構的slaves字典。

5.2 獲取從服務器信息

當哨兵發現主服務器有新的從服務器出現時,哨兵會爲這個新的從服務器建立相應的實例結構以外,還會建立到從服務器的命令鏈接和訂閱鏈接。

發現新的從服務器會出現以下日誌

* +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命令獲取信息。經過從服務器回覆的信息中,能夠得到如下內容

  1. 從服務器的運行ID run_id
  2. 從服務器的角色 role
  3. 從服務器的IP地址 master_host,以及主服務器的端口號master_port
  4. 從服務器的鏈接狀態 matser_link_status
  5. 從服務器的優先級 salve_pripority
  6. 從服務器的複製偏移量 slave_repl_offest

獲取到這些信息以後,會對以前建立的從服務器實例結構進行更新。

5.3 獲取其餘Sentinel的信息

在獲取其餘哨兵的信息以前,先要知道向主服務器和從服務器發送信息接收來自主服務器和從服務器的頻道信息

5.3.1 向主服務器和從服務器發送信息

發送的命令格式以下

PUBLISH __sentinel__:hello "<s_ip>,<s_port>,<s_runid>,<s_epoch>,<m_name>,<m_ip>,<m_port>,<m_epoch>"
複製代碼

PUBLISH是發佈消息的命令,__sentinel__:hello是頻道的名稱,後面就是一些參數,參數信息以下

5.3.2 接收來自主服務器和從服務器的頻道信息

當Sentinel與一個主服務器或者從服務器創建起訂閱鏈接以後,Sentinel就會經過訂閱鏈接,向服務器發送命令:

SUBSCRIBE __sentinel__:hello
複製代碼

表示哨兵訂閱__sentinel__:hello這個頻道,接收這個頻道的消息。

其餘哨兵能夠經過接收這個頻道的消息來發現其餘哨兵的存在。

5.3.3 發現哨兵

經過接收__sentinel__:hello頻道的消息能夠發現其餘哨兵的存在。當哨兵接收到一條來自__sentinel__:hello頻道的消息時,會出現下方

  1. 判斷該消息是不是本身發送的,是則忽略這條消息
  2. 消息不是本身發送時,說明有新的哨兵
  3. 查看本身是否存有該哨兵的信息,有則更新該哨兵的信息
  4. 沒有則建立一個新的哨兵實例結構,並保存到sentinels字典中

注:sentinels字典是專門保存哨兵信息的

5.3.4 建立連向其餘哨兵的命令鏈接

當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
複製代碼

6 模擬101主服務器掉錢

模擬101主服務器掉錢的過程以下

斷線重連的日誌內容以下

接下來開始分析斷線過程當中的每一步驟

  1. 檢測主觀下線狀態
  2. 檢測客觀下線狀態
  3. 選舉領頭哨兵
  4. 故障轉移

6.1 檢測主觀下線狀態

在默認狀況下,Sentinel會以每秒一次的頻率向全部與它建立了命令鏈接的實例(主,從,其餘Sentinel)發送  PING命令 ,經過判斷返回的內容來判斷是否在線,命令分爲有效回覆和無效回覆兩種。

  • 有效回覆
    • +PING
    • -LOADING
    • -MASTERDOWN
  • 無效回覆
    • 除有效回覆之外的內容
    • 指定時間內沒有回覆

配置文件中的down-after-milliseconds參數能夠設置指定時間,在這個時間段內沒有收到回覆則斷定該服務器處於主觀下線狀態。

sentinel down-after-milliseconds learnSentinelMaster 5000
複製代碼

如今101客戶端上輸入如下命令,讓服務器睡眠30秒

debug sleep 30
複製代碼

此時查看哨兵日誌,等待5秒後出現如下內容

# +sdown master learnSentinelMaster 192.168.17.101 6379
複製代碼

6.2 檢測客觀下線狀態

當一個哨兵將一個主服務器判斷爲主觀下線以後,會向其餘監視該主服務器的哨兵進行詢問,當有足夠數量的哨兵斷定主服務器下線時,會執行故障轉移操做 。

注:這裏不對哨兵之間互相發送的消息進行說明

在配置中能夠決定斷定主服務器進入客觀下線狀態所須要的服務器數量,下方配置的最後一個參數就是所需的哨兵數量,這裏填寫的是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
複製代碼

此時開始準備選舉領頭哨兵進行故障轉移

6.3 選舉領頭哨兵

當主服務器被斷定爲客觀下線以後,各個哨兵服務器將會選舉出一個領頭哨兵,有這個領頭哨兵對下線服務器進行故障轉移操做,選舉領頭哨兵的規則以下:

  1. 全部在線的Sentinel都有被選爲領頭Sentinel的資格;
  2. 每次進行選舉以後,不論選舉是否成功,全部Sentinel的配置紀元都會自增一次;
  3. 在一個配置紀元裏,全部Sentinel都有一次將某個Sentinel設置爲局部領頭Sentinel的機會,而且局部領頭一旦設置,在這個配置紀元裏就不會再更改;
  4. 每一個發現主服務器進入客觀下線的Sentinel都會要求其餘Sentinel將本身設置爲局部領頭Sentinel;
  5. 當一個Sentinel向另外一個Sentinel發送請求命令,而且命令中的runid不是*而是運行id時,這表示源Sentinel要求目標Sentinel將前者設置爲後者的局部領頭Sentinel。
  6. 設置局部領頭Sentinel的原則是先到先得,以後全部的設置要求都會被拒絕;
  7. 目標Sentinel在收到命令後,會返回一條回覆,回覆中的leader_runid參數和leader_epoch參數分別記錄了目標Sentinel的局部領頭Sentinel的運行ID和配置紀元;
  8. 源Sentinel在收到回覆後,會檢查配置紀元與本身是否相等,若是相同,且leader_runid與本身相同,那麼表示本身成爲了目標的局部領頭;
  9. 若是有某個Sentinel被半數以上的Sentinel設置成了局部領頭Sentinel,那麼它成爲領頭Sentinel;
  10. 由於領頭的產生須要半數哨兵的支持,而且每一個哨兵在每一個配置紀元只能設置一次局部領頭Sentinel,因此在一個配置紀元裏面,只會出現一個領頭Sentinel;
  11. 若是在給定時限內沒有選出領頭Sentinel,那麼各個Sentinel將在一段時間以後再次進行選舉,直到選出來。

下方就是選舉領頭哨兵的日誌內容

# +vote-for-leader b457cbbcda1991f540d56c6e8faea123a668b16c 2
# 5b1099513713310eba94e69513dba76cf0ac2222 voted for b457cbbcda1991f540d56c6e8faea123a668b16c 2
# f0230d4fdf1ffc7865852de71f16b3017cc1617c voted for b457cbbcda1991f540d56c6e8faea123a668b16c 2
複製代碼

6.4 故障轉移

在選舉出領頭哨兵以後,領頭哨兵須要執行故障轉移操做,操做主要分爲三個步驟

  1. 選出新的主服務器
  2. 修改從服務器的複製目標
  3. 將舊的主服務器變爲從服務器

6.4.1 選出新的主服務器

此時,領頭哨兵須要選出新的主服務器,而後向新的主服務器發送SLAVEOF no one命令,將這個從服務器轉換爲主服務器。

選擇過程會過濾掉不符合要求的服務器:

  1. 處於下線或者斷線狀態的從服務器
  2. 最近5秒內沒有回覆過領頭哨兵的INFO信息的從服務器
  3. 與已下線主服務器鏈接斷開超過(down-after-milliseconds * 10)毫秒的從服務器。(與主服務器客觀下線時間進行比較)

新的主服務器只選擇經過上面的測試,並在上面的標準基礎上排序:

  1. Slave經過Redis實例的redis.conf文件配置的slave-priority排序。優先級越低越被優先考慮。
  2. 若是優先級相同,檢查slave的複製偏移量,並選擇接收更多數據的slave。
  3. 若是多個slave有相同的優先級和一樣的處理數據過程,就會執行一個更進一步的驗證,選擇一個有較短run ID的slave。run ID 對於 slave沒太大用,可是很是有助於選擇slave的過程,而不是隨機選擇slave。

選出合適的從節點做爲新的主節點

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
複製代碼

6.4.2 修改從服務器的複製目標

當新的主服務器出現以後,領頭哨兵會向其餘從服務器發送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
複製代碼

6.4.3 將舊的主服務器變爲從服務器

這時候若是下線的主服務器重啓上線了怎麼辦?這也是故障轉移要作的最後一步,將已下線的主服務器設置爲新的主服務器的從服務器。當下線的主服務器從新上線時,哨兵就會向它發送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
複製代碼

7 相關配置

# 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


複製代碼

哨兵的配置文件:github.com/rainbowda/l…,有須要的能夠下載。

相關文章
相關標籤/搜索