天天看點

redis哨兵(sentinel)系統1. 啟動并初始化Sentinel2. 擷取主伺服器資訊3. 擷取從伺服器資訊4. 向主伺服器和從伺服器發送消息5. 接收來自主伺服器和從伺服器的頻道資訊6. 檢測主觀下線狀态7. 檢測客觀下線狀态8. 選舉領頭sentinel9. 故障轉移

sentinel(哨兵)是redis的高可用性(high availability)解決方案:由一個或多個sentinel執行個體組成的Sentinel系統,監視任意多個主伺服器及這些主伺服器下的從伺服器,并在被監視的主伺服器進入下線狀态時,自動将下線主伺服器屬下的某個從伺服器更新為新的主伺服器,代替已下線的主伺服器繼續處理指令請求。

下圖是Sentinel系統監視伺服器的例子:

redis哨兵(sentinel)系統1. 啟動并初始化Sentinel2. 擷取主伺服器資訊3. 擷取從伺服器資訊4. 向主伺服器和從伺服器發送消息5. 接收來自主伺服器和從伺服器的頻道資訊6. 檢測主觀下線狀态7. 檢測客觀下線狀态8. 選舉領頭sentinel9. 故障轉移

1. 啟動并初始化Sentinel

參考:http://redisbook.com/preview/sentinel/init_sentinel.html

啟動一個Sentinel的指令:

redis> redis-sentinel /path/to/your/sentinel.conf
           

或者

這兩個效果完全相同。當啟動一個sentinel時,需要執行以下步驟:

1. 初始化伺服器

2. 将普通redis伺服器使用的代碼替換成sentinel專用代碼

3. 初始化sentinel狀态

4. 根據配置檔案,初始化sentinel的監視主伺服器清單

5. 建立連向主伺服器的網絡連接配接

1.1 初始化伺服器

Sentinel 本質上是一個運作在特殊模式下的redis伺服器,是以啟動Sentinel的第一步(類似)初始化一個普通的redis伺服器。

因為 Sentinel 執行的工作和普通 Redis 伺服器執行的工作不同, 是以 Sentinel 的初始化過程和普通 Redis 伺服器的初始化過程并不完全相同。

普通伺服器在初始化時會通過載入 RDB 檔案或者 AOF 檔案來還原資料庫狀态, 但是因為 Sentinel 并不使用資料庫, 是以初始化 Sentinel 時就不會載入 RDB 檔案或者 AOF 檔案。

Sentinel 模式下 Redis 伺服器主要功能的使用情況

功能 使用情況
資料庫和鍵值對方面的指令, 比如 SET 、 DEL 、 FLUSHDB 。 不使用。
事務指令, 比如 MULTI 和 WATCH 。 不使用。
腳本指令,比如 EVAL 。 不使用。
RDB 持久化指令, 比如 SAVE 和 BGSAVE 。 不使用。
AOF 持久化指令, 比如 BGREWRITEAOF 。 不使用。
複制指令,比如 SLAVEOF 。 Sentinel 内部可以使用,但用戶端不可以使用。
釋出與訂閱指令, 比如 PUBLISH 和 SUBSCRIBE 。 SUBSCRIBE 、 PSUBSCRIBE 、 UNSUBSCRIBE PUNSUBSCRIBE 四個指令在 Sentinel 内部和用戶端都可以使用, 但 PUBLISH 指令隻能在 Sentinel 内部使用。
檔案事件處理器(負責發送指令請求、處理指令回複)。 Sentinel 内部使用, 但關聯的檔案事件處理器和普通 Redis 伺服器不同。
時間事件處理器(負責執行 serverCron 函數)。 Sentinel 内部使用, 時間事件的處理器仍然是 serverCron 函數, serverCron 函數會調用 sentinel.c/sentinelTimer 函數, 後者包含了 Sentinel 要執行的所有操作。

1.2 使用 Sentinel 專用代碼

将一部分普通 Redis 伺服器使用的代碼替換成 Sentinel 專用代碼

(1)端口

普通 Redis 伺服器使用 redis.h/REDIS_SERVERPORT 常量的值作為伺服器端口:

而 Sentinel 則使用 sentinel.c/REDIS_SENTINEL_PORT 常量的值作為伺服器端口:

(2)指令表

普通 Redis 伺服器使用 redis.c/redisCommandTable 作為伺服器的指令表:

struct redisCommand redisCommandTable[] = {
    {"get",getCommand,,"r",,NULL,,,,,},
    {"set",setCommand,-,"wm",,noPreloadGetKeys,,,,,},
    {"setnx",setnxCommand,,"wm",,noPreloadGetKeys,,,,,},
    // ...
    {"script",scriptCommand,-,"ras",,NULL,,,,,},
    {"time",timeCommand,,"rR",,NULL,,,,,},
    {"bitop",bitopCommand,-,"wm",,NULL,,-,,,},
    {"bitcount",bitcountCommand,-,"r",,NULL,,,,,}
}
           

而 Sentinel 則使用 sentinel.c/sentinelcmds 作為伺服器的指令表, 并且其中的 INFO 指令會使用 Sentinel 模式下的專用實作 sentinel.c/sentinelInfoCommand 函數, 而不是普通 Redis 伺服器使用的實作 redis.c/infoCommand 函數:

struct redisCommand sentinelcmds[] = {
    {"ping",pingCommand,,"",,NULL,,,,,},
    {"sentinel",sentinelCommand,-,"",,NULL,,,,,},
    {"subscribe",subscribeCommand,-,"",,NULL,,,,,},
    {"unsubscribe",unsubscribeCommand,-,"",,NULL,,,,,},
    {"psubscribe",psubscribeCommand,-,"",,NULL,,,,,},
    {"punsubscribe",punsubscribeCommand,-,"",,NULL,,,,,},
    {"info",sentinelInfoCommand,-,"",,NULL,,,,,}
};
           

1.3 初始化 Sentinel 狀态

伺服器會初始化一個 sentinel.c/sentinelState 結構(“Sentinel 狀态”), 儲存了伺服器中所有和 Sentinel 功能有關的狀态 (伺服器的一般狀态仍然由 redis.h/redisServer 結構儲存):

struct sentinelState {

    // 目前紀元,用于實作故障轉移
    uint64_t current_epoch;

    // 儲存了所有被這個 sentinel 監視的主伺服器的相關資訊
    // 字典的鍵是主伺服器的名字
    // 字典的值則是一個指向 sentinelRedisInstance 結構的指針
    dict *masters;

    // 是否進入了 TILT 模式?
    int tilt;

    // 目前正在執行的腳本的數量
    int running_scripts;

    // 進入 TILT 模式的時間
    mstime_t tilt_start_time;

    // 最後一次執行時間處理器的時間
    mstime_t previous_time;

    // 一個 FIFO 隊列,包含了所有需要執行的使用者腳本
    list *scripts_queue;

} sentinel;
           

1.4 初始化 Sentinel 狀态的 masters 屬性

Sentinel 狀态中的 masters 字典記錄了所有被 Sentinel 監視的主伺服器的相關資訊,是根據被載入的 Sentinel 配置檔案對其進行初始化。

1. 字典的鍵是被監視主伺服器的名字。

2. 字典的值則是被監視主伺服器對應的 sentinel.c/sentinelRedisInstance 結構。

typedef struct sentinelRedisInstance {

    // 辨別值,記錄了執行個體的類型,以及該執行個體的目前狀态
    int flags;

    // 執行個體的名字
    // 主伺服器的名字由使用者在配置檔案中設定
    // 從伺服器以及 Sentinel 的名字由 Sentinel 自動設定
    // 格式為 ip:port ,例如 "127.0.0.1:26379"
    char *name;

    // 執行個體的運作 ID
    char *runid;

    // 配置紀元,用于實作故障轉移
    uint64_t config_epoch;

    // 執行個體的位址:ip位址和port
    sentinelAddr *addr;

    // SENTINEL down-after-milliseconds 選項設定的值
    // 執行個體無響應多少毫秒之後才會被判斷為主觀下線(subjectively down)
    mstime_t down_after_period;

    // SENTINEL monitor <master-name> <IP> <port> <quorum> 選項中的 quorum 參數
    // 判斷這個執行個體為客觀下線(objectively down)所需的支援投票數量
    int quorum;

    // SENTINEL parallel-syncs <master-name> <number> 選項的值
    // 在執行故障轉移操作時,可以同時對新的主伺服器進行同步的從伺服器數量
    int parallel_syncs;

    // SENTINEL failover-timeout <master-name> <ms> 選項的值
    // 重新整理故障遷移狀态的最大時限
    mstime_t failover_timeout;

    // ...

} sentinelRedisInstance;
           
redis哨兵(sentinel)系統1. 啟動并初始化Sentinel2. 擷取主伺服器資訊3. 擷取從伺服器資訊4. 向主伺服器和從伺服器發送消息5. 接收來自主伺服器和從伺服器的頻道資訊6. 檢測主觀下線狀态7. 檢測客觀下線狀态8. 選舉領頭sentinel9. 故障轉移

1.5 建立連向主伺服器的網絡連接配接

對于每個被 Sentinel 監視的主伺服器來說, Sentinel 會建立兩個連向主伺服器的異步網絡連接配接:

  • 一個指令連接配接, Sentinel 将成為主伺服器的用戶端, 這個連接配接專門用于向主伺服器發送指令, 并接收指令回複。
  • 一個訂閱連接配接, 這個連接配接專門用于訂閱主伺服器的 __sentinel__:hello 頻道。
redis哨兵(sentinel)系統1. 啟動并初始化Sentinel2. 擷取主伺服器資訊3. 擷取從伺服器資訊4. 向主伺服器和從伺服器發送消息5. 接收來自主伺服器和從伺服器的頻道資訊6. 檢測主觀下線狀态7. 檢測客觀下線狀态8. 選舉領頭sentinel9. 故障轉移

2. 擷取主伺服器資訊

Sentinel預設會每十秒一次的頻率,通過指令連接配接向被監視的主伺服器發送

INFO指令

redis哨兵(sentinel)系統1. 啟動并初始化Sentinel2. 擷取主伺服器資訊3. 擷取從伺服器資訊4. 向主伺服器和從伺服器發送消息5. 接收來自主伺服器和從伺服器的頻道資訊6. 檢測主觀下線狀态7. 檢測客觀下線狀态8. 選舉領頭sentinel9. 故障轉移

通過分析INFO指令的回複來擷取主伺服器的目前資訊:

1. 主伺服器本身的資訊

2. 主伺服器屬下所有從伺服器的資訊

redis哨兵(sentinel)系統1. 啟動并初始化Sentinel2. 擷取主伺服器資訊3. 擷取從伺服器資訊4. 向主伺服器和從伺服器發送消息5. 接收來自主伺服器和從伺服器的頻道資訊6. 檢測主觀下線狀态7. 檢測客觀下線狀态8. 選舉領頭sentinel9. 故障轉移

3. 擷取從伺服器資訊

當sentinel發現主伺服器有新的從伺服器時,sentinel①為這個從伺服器建立相應的執行個體結構;②建立連接配接到從伺服器的指令連接配接和訂閱連接配接。

4. 向主伺服器和從伺服器發送消息

sentinel會每2秒一次的頻率,通過指令連接配接向所有被監視的(主從)伺服器的_sentinel_:hello頻道發送消息:

PUBLISH _sentinel_:hello "<s_ip>,<s_port>,<s_runid>,<s_epoch>,<m_name>,<m_ip>,<m_epoch>"

1. 以s_開頭的參數記錄sentinel本身的資訊

2. 以m_開頭的參數記錄主伺服器的資訊

5. 接收來自主伺服器和從伺服器的頻道資訊

當sentinel與一個主伺服器或從伺服器建立訂閱連接配接後,通過訂閱連接配接發送訂閱指令

SUBSCRIBE _sentinel_:hello

對于監視同一個伺服器的多個sentinel來說,一個sentinel發送的資訊會被其他sentinel接收,這些資訊用于①更新其他sentinel對發送資訊的sentinel的認知②更新其他sentinel對被監視伺服器的認知。
redis哨兵(sentinel)系統1. 啟動并初始化Sentinel2. 擷取主伺服器資訊3. 擷取從伺服器資訊4. 向主伺服器和從伺服器發送消息5. 接收來自主伺服器和從伺服器的頻道資訊6. 檢測主觀下線狀态7. 檢測客觀下線狀态8. 選舉領頭sentinel9. 故障轉移

(1)更新sentinel字典

sentinel為主伺服器建立的執行個體結構的sentinel字典儲存了除sentinel本身之外,所有同樣監視這個主伺服器的其他sentinel的資料。

(2)建立連向其他sentinel的指令連接配接

redis哨兵(sentinel)系統1. 啟動并初始化Sentinel2. 擷取主伺服器資訊3. 擷取從伺服器資訊4. 向主伺服器和從伺服器發送消息5. 接收來自主伺服器和從伺服器的頻道資訊6. 檢測主觀下線狀态7. 檢測客觀下線狀态8. 選舉領頭sentinel9. 故障轉移

6. 檢測主觀下線狀态

sentinel會以每秒一次的頻率向所有與它從建立了指令連接配接的執行個體(包括主伺服器、從伺服器、其他sentinel在内)發送

PING

指令,并通過執行個體傳回的PING指令回複來判斷執行個體是否線上。

7. 檢測客觀下線狀态

當sentinel在判定一個主伺服器為主觀下線後,為了确認這個主伺服器是否真的下線,詢問同樣監視這個主伺服器的其他sentinel,當收到足夠數量的已下線判斷後,sentinel就會将伺服器判定為客觀下線,并對伺服器執行故障轉移操作。

8. 選舉領頭sentinel

當一個主伺服器被判斷為客觀下線後,監視這個下線伺服器的各個sentinel會進行協商,選舉出一個領頭sentinel,并由領頭sentinel對下線伺服器執行故障轉移操作。

9. 故障轉移

領頭sentinel對下線伺服器執行故障轉移操作,三個步驟:

1. 在已下線主伺服器屬下的所有從伺服器裡面,挑選出一個從伺服器,并将其轉換為主伺服器。

2. 讓已下線伺服器屬下的所有從伺服器改為複制新的主伺服器

3. 将已下線伺服器設定為新的主伺服器的從伺服器,當這個舊的主伺服器重新上線時,它就會成為新的主伺服器的從伺服器。

繼續閱讀