Redis奇幻之旅(四)3.哨兵機制

3.哨兵機制

? 在《4.1 集群設計繞不開的話題》中我們提到了單點故障的問題,在《4.2 主從復制》中我們又提到了使用從庫來做主庫的備份,保證數(shù)據(jù)盡量不丟失腻贰。不過問題來了,如果主庫掛掉了,我們?nèi)绾沃滥啬唐埽慷颐看沃鲙鞉斓粑覀兌甲屵\維人員來將從庫手動改成主庫嗎?如果代碼里已經(jīng)將主庫連接寫死了邢羔,是不是還得把所有影響到的項目都重新發(fā)布呢驼抹?這樣顯然不合理,所以拜鹤,就有了哨兵機制框冀。

? 哨兵機制(Redis Sentinel)有兩個主要任務:

  • 監(jiān)控:定時給被監(jiān)控的Redis實例做心跳檢測,看看是否在正常工作
  • 自動故障轉(zhuǎn)移:當主Redis掛掉了敏簿,Sentinel會將從服務器升級為主服務器明也,并且給客戶端提供新的主服務器地址宣虾。

3.1 Sentinel 啟動初始化

? 當一個Sentinel啟動時,它會執(zhí)行以下步驟:

  • 初始化服務器

    這里的初始化服務器并不會像《3.1.2.1 Redis服務啟動流程》中描述的那樣完整的初始化Redis服務器温数,比如它不會載入RDB或AOF文件绣硝。

  • 將普通Redis服務器的代碼替換成Sentinel代碼

    Redis服務器啟動的時候會載入命令表,而Sentinel啟動載入的命令表和正常的不太一樣撑刺,所以它只支持PING鹉胖、SENTINEL、INFO够傍、SUBSCRIBE甫菠、UNSUBSCRIBE、PSUBSCRIBE和PUNSUBSCRIBE這七個命令冕屯。除了這個以外還有一些其他的代碼替換寂诱。

  • 初始化Sentinel狀態(tài)

    這里會初始化一個sentinelState結(jié)構(gòu)體,如下:

    /* Main state. */
    struct sentinelState {
        char myid[CONFIG_RUN_ID_SIZE+1]; /* This sentinel ID. */
        uint64_t current_epoch;         /* Current epoch. */
        dict *masters;      /* Dictionary of master sentinelRedisInstances.
                               Key is the instance name, value is the
                               sentinelRedisInstance structure pointer. */
        int tilt;           /* Are we in TILT mode? */
        int running_scripts;    /* Number of scripts in execution right now. */
        mstime_t tilt_start_time;       /* When TITL started. */
        mstime_t previous_time;         /* Last time we ran the time handler. */
        list *scripts_queue;            /* Queue of user scripts to execute. */
        char *announce_ip;  /* IP addr that is gossiped to other sentinels if
                               not NULL. */
        int announce_port;  /* Port that is gossiped to other sentinels if
                               non zero. */
        unsigned long simfailure_flags; /* Failures simulation. */
        int deny_scripts_reconfig; /* Allow SENTINEL SET ... to change script
                                      paths at runtime? */
    } sentinel;
    
  • 根據(jù)給定的配置文件安聘,初始化Sentinel的監(jiān)視主服務器列表

    上述sentinelState結(jié)構(gòu)體中的masters字典記錄了所有被Sentinel監(jiān)視的主服務器相關信息痰洒。其中key是被監(jiān)視主服務器的名字,value是Redis服務器實例浴韭,用sentinelRedisInstance來保存信息丘喻,如下:

    typedef struct sentinelRedisInstance {
        int flags;      /* See SRI_... defines */
        char *name;     /* Master name from the point of view of this sentinel. */
        char *runid;    /* Run ID of this instance, or unique ID if is a Sentinel.*/
        uint64_t config_epoch;  /* Configuration epoch. */
        sentinelAddr *addr; /* Master host. */
        instanceLink *link; /* Link to the instance, may be shared for Sentinels. */
        mstime_t last_pub_time;   /* Last time we sent hello via Pub/Sub. */
        mstime_t last_hello_time; /* Only used if SRI_SENTINEL is set. Last time
                                     we received a hello from this Sentinel
                                     via Pub/Sub. */
        mstime_t last_master_down_reply_time; /* Time of last reply to
                                                 SENTINEL is-master-down command. */
        mstime_t s_down_since_time; /* Subjectively down since time. */
        mstime_t o_down_since_time; /* Objectively down since time. */
        mstime_t down_after_period; /* Consider it down after that period. */
        mstime_t info_refresh;  /* Time at which we received INFO output from it. */
        dict *renamed_commands;     /* Commands renamed in this instance:
                                       Sentinel will use the alternative commands
                                       mapped on this table to send things like
                                       SLAVEOF, CONFING, INFO, ... */
    
        /* Role and the first time we observed it.
         * This is useful in order to delay replacing what the instance reports
         * with our own configuration. We need to always wait some time in order
         * to give a chance to the leader to report the new configuration before
         * we do silly things. */
        int role_reported;
        mstime_t role_reported_time;
        mstime_t slave_conf_change_time; /* Last time slave master addr changed. */
    
        /* Master specific. */
        dict *sentinels;    /* Other sentinels monitoring the same master. */
        dict *slaves;       /* Slaves for this master instance. */
        unsigned int quorum;/* Number of sentinels that need to agree on failure. */
        int parallel_syncs; /* How many slaves to reconfigure at same time. */
        char *auth_pass;    /* Password to use for AUTH against master & replica. */
        char *auth_user;    /* Username for ACLs AUTH against master & replica. */
    
        /* Slave specific. */
        mstime_t master_link_down_time; /* Slave replication link down time. */
        int slave_priority; /* Slave priority according to its INFO output. */
        mstime_t slave_reconf_sent_time; /* Time at which we sent SLAVE OF <new> */
        struct sentinelRedisInstance *master; /* Master instance if it's slave. */
        char *slave_master_host;    /* Master host as reported by INFO */
        int slave_master_port;      /* Master port as reported by INFO */
        int slave_master_link_status; /* Master link status as reported by INFO */
        unsigned long long slave_repl_offset; /* Slave replication offset. */
        /* Failover */
        char *leader;       /* If this is a master instance, this is the runid of
                               the Sentinel that should perform the failover. If
                               this is a Sentinel, this is the runid of the Sentinel
                               that this Sentinel voted as leader. */
        uint64_t leader_epoch; /* Epoch of the 'leader' field. */
        uint64_t failover_epoch; /* Epoch of the currently started failover. */
        int failover_state; /* See SENTINEL_FAILOVER_STATE_* defines. */
        mstime_t failover_state_change_time;
        mstime_t failover_start_time;   /* Last failover attempt start time. */
        mstime_t failover_timeout;      /* Max time to refresh failover state. */
        mstime_t failover_delay_logged; /* For what failover_start_time value we
                                           logged the failover delay. */
        struct sentinelRedisInstance *promoted_slave; /* Promoted slave instance. */
        /* Scripts executed to notify admin or reconfigure clients: when they
         * are set to NULL no script is executed. */
        char *notification_script;
        char *client_reconfig_script;
        sds info; /* cached INFO output */
    } sentinelRedisInstance;
    
  • 創(chuàng)建連向主服務器的網(wǎng)絡連接

    Sentinel會和被監(jiān)視的主服務器創(chuàng)建兩個異步網(wǎng)絡連接,一個是用來向Redis服務器發(fā)送命令和接收命令用的囱桨,另一個是用來訂閱_sentinel_:hello頻道用的仓犬。

3.2 監(jiān)視流程

? Sentinel默認以十秒一次的頻率給主服務器發(fā)送INFO命令,并通過分析INFO命令來獲取主服務器的當前信息舍肠。這里除了獲得主服務器信息以外搀继,還會取到從服務器的一些信息,并且Sentinel會給每個從服務器構(gòu)建一個sentinelRedisInstance結(jié)構(gòu)體來保存從服務器信息翠语。

? 當每次有新的從服務器被加入進來叽躯,Sentinel還會構(gòu)建和從服務器的命令連接和訂閱連接,并且也是十秒一次向從服務器發(fā)送INFO命令肌括。

? 除了發(fā)送INFO命令以外点骑,Sentinel還會以兩秒一次的頻率向所有被監(jiān)視的服務器發(fā)送PUBLISH命令,這樣就能保證所有訂閱了這個服務器的Sentinel都接收到相關的信息谍夭,除此之外黑滴,Sentinel還可以互相知曉彼此的存在。sentinelRedisInstance結(jié)構(gòu)體中的sentinels字典就是用來保存其他Sentinel的地方紧索。每個Sentinel還會互相建立命令連接來相互交換信息袁辈,不過Sentinel不會互相建立訂閱連接。

? Sentinel會以每秒一次珠漂,向所有建立了命令連接的機器發(fā)送PING命令晚缩,如果有機器在down-after-milliseconds時間范圍內(nèi)未響應的話尾膊,這個機器會被Sentinel標記成主觀下線(Sentinel自己認為這個服務器不可用了)。當有機器被Sentinel認為主觀下線之后荞彼,Sentinel會詢問其他監(jiān)視這個機器的Sentinel冈敛,如果返回的信息表示這個機器確實不能提供服務了,這時鸣皂,所有Sentinel會將其標記為客觀下線抓谴。被標記成下線并不意味著不被Sentinel監(jiān)視了,Sentinel會定時監(jiān)聽這個機器寞缝,如果他又可以PING通了齐邦,Sentinel還是會重新將其標記為在線狀態(tài)。如果是master節(jié)點不可用第租,那么當新的master被選出來之后,老的master就會被當做新master的從服務器我纪。

? master節(jié)點不可用的情況下慎宾,Sentinel會做一次故障轉(zhuǎn)移。但是畢竟一臺服務器可能有多個master浅悉,不可能所有Sentinel都做故障轉(zhuǎn)移趟据,所以在做故障轉(zhuǎn)移之前,Sentinel會選舉出一個領頭的Sentinel來做這件事情术健,選舉的規(guī)則就是每個Sentinel都向其他Sentinel發(fā)送一個SENTINEL is-master-down-by-addr命令汹碱,如果其他Sentinel有半數(shù)都通過了某個Sentinel,那么就由這個有個Sentinel作為領頭有個Sentinel來執(zhí)行故障轉(zhuǎn)移(這里使用了raft算法)荞估。

? 故障轉(zhuǎn)移的時候需要做兩步咳促,一是選舉一個新的master,新master會在所有正常的從節(jié)點中選取一個偏移量最大的節(jié)點作為新master勘伺,這里可能會涉及到一部分的數(shù)據(jù)丟失跪腹,不過這不可避免(可參閱《4.1 集群設計繞不開的話題》)。二是其他從服務器SLAVEOF這個新的master飞醉。

思考:

? 常規(guī)我們都是配置三臺Sentinel來處理冲茸,如果Sentinel有一臺掛了,剛好master服務器也掛了缅帘。Sentinel機制需要選舉一臺領頭Sentinel處理故障轉(zhuǎn)移轴术,但是兩臺Sentinel會出現(xiàn)腦裂情況,所以這個時候Redis服務就不可用了钦无。

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載逗栽,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。
  • 序言:七十年代末铃诬,一起剝皮案震驚了整個濱河市祭陷,隨后出現(xiàn)的幾起案子苍凛,更是在濱河造成了極大的恐慌,老刑警劉巖兵志,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件醇蝴,死亡現(xiàn)場離奇詭異,居然都是意外死亡想罕,警方通過查閱死者的電腦和手機悠栓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來按价,“玉大人惭适,你說我怎么就攤上這事÷ジ洌” “怎么了癞志?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長框产。 經(jīng)常有香客問我凄杯,道長,這世上最難降的妖魔是什么秉宿? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任戒突,我火速辦了婚禮,結(jié)果婚禮上描睦,老公的妹妹穿的比我還像新娘膊存。我一直安慰自己,他們只是感情好忱叭,可當我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布隔崎。 她就那樣靜靜地躺著,像睡著了一般窑多。 火紅的嫁衣襯著肌膚如雪仍稀。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天埂息,我揣著相機與錄音技潘,去河邊找鬼。 笑死千康,一個胖子當著我的面吹牛享幽,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播拾弃,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼值桩,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了豪椿?” 一聲冷哼從身側(cè)響起奔坟,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤携栋,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后咳秉,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體婉支,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年澜建,在試婚紗的時候發(fā)現(xiàn)自己被綠了向挖。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡炕舵,死狀恐怖何之,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情咽筋,我是刑警寧澤溶推,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站奸攻,受9級特大地震影響悼潭,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜舞箍,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望皆疹。 院中可真熱鬧疏橄,春花似錦、人聲如沸略就。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽表牢。三九已至窄绒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間崔兴,已是汗流浹背彰导。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留敲茄,地道東北人位谋。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像堰燎,于是被迫代替她去往敵國和親掏父。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內(nèi)容