redis源碼分析—集羣--哨兵模式

redis在啓動時,如果進程名是"redis-sentinel",或者參數中帶了"–sentinel",這時redis便以哨兵的方式運行。一個sentinel可以監控多個master。
sentinel的配置如下

// 當前Sentinel節點監控 127.0.0.1:6379 這個主節點
// 2代表判斷主節點失敗至少需要2個Sentinel節點節點同意
// mymaster是主節點的別名
sentinel monitor mymaster 127.0.0.1 6379 2

//每個Sentinel節點都要定期PING命令來判斷Redis數據節點和其餘Sentinel節點是否可達,如果超過30000毫秒且沒有回覆,則判定不可達
sentinel down-after-milliseconds mymaster 30000

//當Sentinel節點集合對主節點故障判定達成一致時,Sentinel領導者節點會做故障轉移操作,選出新的主節點,原來的從節點會向新的主節點發起復制操作,限制每次向新的主節點發起復制操作的從節點個數爲1
sentinel parallel-syncs mymaster 1

//故障轉移超時時間爲180000毫秒
sentinel failover-timeout mymaster 180000

sentinel支持的如下的命令列表:

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},
    {"shutdown",shutdownCommand,-1,"",0,NULL,0,0,0,0,0}
};

【加羣】:857565362
sentinel的時間事件也區別於一般服務,入口是sentinelTimer。

void sentinelTimer(void) {

    // 記錄本次 sentinel 調用的事件,
    // 並判斷是否需要進入 TITL 模式
    sentinelCheckTiltCondition();

    // 執行定期操作
    // 比如 PING 實例、分析主服務器和從服務器的 INFO 命令
    // 向其他監視相同主服務器的 sentinel 發送問候信息
    // 並接收其他 sentinel 發來的問候信息
    // 執行故障轉移操作,等等
    sentinelHandleDictOfRedisInstances(sentinel.masters);

    // 運行等待執行的腳本
    sentinelRunPendingScripts();

    // 清理已執行完畢的腳本,並重試出錯的腳本
    sentinelCollectTerminatedScripts();

    // 殺死運行超時的腳本
    sentinelKillTimedoutScripts();

    /* We continuously change the frequency of the Redis "timer interrupt"
     * in order to desynchronize every Sentinel from every other.
     * This non-determinism avoids that Sentinels started at the same time
     * exactly continue to stay synchronized asking to be voted at the
     * same time again and again (resulting in nobody likely winning the
     * election because of split brain voting). */
    server.hz = REDIS_DEFAULT_HZ + rand() % REDIS_DEFAULT_HZ;
}

【加羣】:857565362
一次時間事件中,主要做了以下幾件事情。
1.判斷是否上次執行時間事件距今是否差了超過了2秒,則進入默認持續30秒的TITL模式,進入了此模式的sentinel認爲當前自身的狀態可能是有問題的,會閹割一部分選舉master相關的功能。
2.連接其他master/slave/sentinel,發送auth命令,master的地址是配置文件配好的,所以可以直接拿到;slave和其他sentinel的地址通過master拿到。
3.向master和slave定期發送 ping,info命令。其中info命令可以得到對方的運行狀態,主從關係等。
4.哨兵在連接建立的時候會向master和其slave訂閱頻道"sentinel:hello"的消息,同時週期性的發佈一條hello消息,給其他哨兵打招呼,這條消息包含了自身地址,自身runid,自身的epoch,master的基本信息和epoch。
假設有個哨兵B也是檢測這個master的,會接受到A發送的推送,把A保存到自己的哨兵列表中。
5.根據redis服務回覆ping的情況,判斷redis是否已經主觀下線(Subject Down),如果下線了,將結果告知給其他sentinel。
6.如果master主觀下線了,判斷這個master是否客觀下線(Object Down),方法就是收集其他sentinel的意見,如果認爲該master下線的sentinel數量大於配置,則認爲這個master下線了,此時開始執行故障轉移邏輯(Failover)。
7.故障轉移,分爲以下步驟:
1)選舉頭領(leader)
1-1)首先向其他sentinel發送紀元編號(爲本sentinel自身當前的紀元自增1,同時記錄爲故障master的紀元),如果其他紀元收到了這條消息,如果比自己的大,則更新爲此紀元,否則忽略;
1-2)統計這個故障master下的所有sentinel投票結果,如果已經有其他sentinel發表意見了,則選取最高得票者,投他一票,如果沒有,投自己一票。此時,如果最高得票者的得票數超過半數+1,則本次選舉完成,否則進入下個紀元。引入紀元(epoch)這個參數是因爲,選舉不是每次都成功的,比如所有sentinel都選舉了自己,這種情況下需要重新啓動下一輪投票。
2)由leader從slave當中選擇一個作爲新的master。
2-1) 過濾掉Down掉的slave,判斷slave的Info回覆時長和與master的連接狀態,太差的slave不考慮。
2-2) 對符合條件的slave排序以選出最優,依據分別是slave的優先級,從原master複製的數據量大小,runid。
3)向選出來的slave發送 slaveof no one來提升該slave爲master。確定該服務提升爲master之後,給其他slave發送slaveof命令。
【加羣】:857565362
sentinel內部狀態發生變化時,會調用sentinelEvent方法廣播狀態,我們可以使用redis-cli連接sentinel,用subscribe來查看sentinel的內部運行狀態,可以訂閱多個狀態,用空格隔開,本機測試:

localhost:Debug jjchen$ redis-cli -p 26379
redis> subscribe +tilt -tilt
Reading messages... (press Ctrl-c to quit)
1. "subscribe"
2. "+tilt"
3. (integer) 1

1. "subscribe"
2. "-tilt"
3. (integer) 2

1. "message"
2. "+tilt"
3. "#tilt mode entered"

1. "message"
2. "-tilt"
3. "#tilt mode exited"

1. "message"
2. "+tilt"
3. "#tilt mode entered"

我這兒整理了比較全面的JAVA相關的面試資料,
需要領取面試資料的同學,請加羣:473984645
在這裏插入圖片描述
獲取更多學習資料,可以加羣:473984645或掃描下方二維碼
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章