redis源碼分析(七):集群--哨兵模式

redis在啟動時,如果進(jìn)程名是"redis-sentinel",或者參數(shù)中帶了"--sentinel",這時redis便以哨兵的方式運行。一個sentinel可以監(jiān)控多個master。

sentinel的配置如下


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

//每個Sentinel節(jié)點都要定期PING命令來判斷Redis數(shù)據(jù)節(jié)點和其余Sentinel節(jié)點是否可達(dá),如果超過30000毫秒且沒有回復(fù),則判定不可達(dá)
sentinel down-after-milliseconds mymaster 30000

//當(dāng)Sentinel節(jié)點集合對主節(jié)點故障判定達(dá)成一致時,Sentinel領(lǐng)導(dǎo)者節(jié)點會做故障轉(zhuǎn)移操作,選出新的主節(jié)點,原來的從節(jié)點會向新的主節(jié)點發(fā)起復(fù)制操作,限制每次向新的主節(jié)點發(fā)起復(fù)制操作的從節(jié)點個數(shù)為1
sentinel parallel-syncs mymaster 1

//故障轉(zhuǎn)移超時時間為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}
};

sentinel的時間事件也區(qū)別于一般服務(wù),入口是sentinelTimer。

void sentinelTimer(void) {

    // 記錄本次 sentinel 調(diào)用的事件,
    // 并判斷是否需要進(jìn)入 TITL 模式
    sentinelCheckTiltCondition();

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

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

    // 清理已執(zhí)行完畢的腳本,并重試出錯的腳本
    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;
}

一次時間事件中,主要做了以下幾件事情。

  • 1.判斷是否上次執(zhí)行時間事件距今是否差了超過了2秒,則進(jìn)入默認(rèn)持續(xù)30秒的TITL模式,進(jìn)入了此模式的sentinel認(rèn)為當(dāng)前自身的狀態(tài)可能是有問題的,會閹割一部分選舉master相關(guān)的功能。

  • 2.連接其他master/slave/sentinel,發(fā)送auth命令,master的地址是配置文件配好的,所以可以直接拿到;slave和其他sentinel的地址通過master拿到。

  • 3.向master和slave定期發(fā)送 ping,info命令。其中info命令可以得到對方的運行狀態(tài),主從關(guān)系等。

  • 4.哨兵在連接建立的時候會向master和其slave訂閱頻道"_sentinel_:hello"的消息,同時周期性的發(fā)布一條hello消息,給其他哨兵打招呼,這條消息包含了自身地址,自身runid,自身的epoch,master的基本信息和epoch。
    假設(shè)有個哨兵B也是檢測這個master的,會接受到A發(fā)送的推送,把A保存到自己的哨兵列表中。

  • 5.根據(jù)redis服務(wù)回復(fù)ping的情況,判斷redis是否已經(jīng)主觀下線(Subject Down),如果下線了,將結(jié)果告知給其他sentinel。

  • 6.如果master主觀下線了,判斷這個master是否客觀下線(Object Down),方法就是收集其他sentinel的意見,如果認(rèn)為該master下線的sentinel數(shù)量大于配置,則認(rèn)為這個master下線了,此時開始執(zhí)行故障轉(zhuǎn)移邏輯(Failover)。

  • 7.故障轉(zhuǎn)移,分為以下步驟:
    1)選舉頭領(lǐng)(leader)
    1-1)首先向其他sentinel發(fā)送紀(jì)元編號(為本sentinel自身當(dāng)前的紀(jì)元自增1,同時記錄為故障master的紀(jì)元),如果其他紀(jì)元收到了這條消息,如果比自己的大,則更新為此紀(jì)元,否則忽略;
    1-2)統(tǒng)計這個故障master下的所有sentinel投票結(jié)果,如果已經(jīng)有其他sentinel發(fā)表意見了,則選取最高得票者,投他一票,如果沒有,投自己一票。此時,如果最高得票者的得票數(shù)超過半數(shù)+1,則本次選舉完成,否則進(jìn)入下個紀(jì)元。引入紀(jì)元(epoch)這個參數(shù)是因為,選舉不是每次都成功的,比如所有sentinel都選舉了自己,這種情況下需要重新啟動下一輪投票。
    2)由leader從slave當(dāng)中選擇一個作為新的master。
    2-1) 過濾掉Down掉的slave,判斷slave的Info回復(fù)時長和與master的連接狀態(tài),太差的slave不考慮。
    2-2) 對符合條件的slave排序以選出最優(yōu),依據(jù)分別是slave的優(yōu)先級,從原master復(fù)制的數(shù)據(jù)量大小,runid。

    1. 向選出來的slave發(fā)送 slaveof no one來提升該slave為master。確定該服務(wù)提升為master之后,給其他slave發(fā)送slaveof命令。
sentinel內(nèi)部狀態(tài)發(fā)生變化時,會調(diào)用sentinelEvent方法廣播狀態(tài),我們可以使用redis-cli連接sentinel,用subscribe來查看sentinel的內(nèi)部運行狀態(tài),可以訂閱多個狀態(tài),用空格隔開,本機測試:
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"
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容