jinfeng_wang

          G-G-S,D-D-U!

          BlogJava 首頁(yè) 新隨筆 聯(lián)系 聚合 管理
            400 Posts :: 0 Stories :: 296 Comments :: 0 Trackbacks
          http://weizijun.cn/2015/04/30/redis%20sentinel%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0/ 



          Sentinel是Redis官方自帶的工具,中文意思是哨兵,顧名思義,就是守衛(wèi)Redis的好幫手。NCR(網(wǎng)易云redis)準(zhǔn)備開(kāi)發(fā)高可用的Redis集群,有計(jì)劃使用Sentinel。本文接下來(lái)介紹下Sentinel的設(shè)計(jì)與實(shí)現(xiàn)。本文介紹的Sentinel是用的2.8.19版本(最新的3.0.0版本Sentinel的功能只比2.8.19多了一個(gè)client命令)。

          先引用官方的說(shuō)法介紹下Sentinel的作用。Redis Sentinel是一個(gè)幫助管理Redis實(shí)例的系統(tǒng),它提供以下功能:

          • 監(jiān)控(Monitoring):Sentinel會(huì)不斷的檢查你的主節(jié)點(diǎn)和從節(jié)點(diǎn)是否正常工作。
          • 通知(Notification):被監(jiān)控的Redis實(shí)例如果出現(xiàn)問(wèn)題,Sentinel可以通過(guò)API(pub)通知系統(tǒng)管理員或者其他程序。
          • 自動(dòng)故障轉(zhuǎn)移(Automatic failover):如果一個(gè)master離線,Sentinel會(huì)開(kāi)始進(jìn)行故障轉(zhuǎn)移,master下的一個(gè)slave會(huì)被選為新的master,其他的slave會(huì)開(kāi)始復(fù)制新的master。應(yīng)用可以通過(guò)Redis服務(wù)的通知機(jī)制更新新的master地址。
          • 配置提供者(Configuration provider):客戶端可以把Sentinel作為權(quán)威的配置發(fā)布者來(lái)獲得最新的master地址。如果發(fā)生了故障轉(zhuǎn)移,Sentinel集群會(huì)通知客戶端新的master地址。

          Sentinel是如何運(yùn)行的?又是如何提供對(duì)Redis的監(jiān)控和故障轉(zhuǎn)移呢?

          image

          特殊狀態(tài)的Redis

          一個(gè)Sentinel就是一個(gè)運(yùn)行在特殊狀態(tài)下的Redis,以至于可以用啟動(dòng)Redis Server的方式啟動(dòng)Sentinel。不過(guò)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},     {"role",sentinelRoleCommand,1,"l",0,NULL,0,0,0,0,0},     {"shutdown",shutdownCommand,-1,"",0,NULL,0,0,0,0,0} }; 

          上面就是Sentinel支持的所有命令了。后面會(huì)對(duì)這些命令進(jìn)行詳細(xì)的說(shuō)明。

          配置Redis主節(jié)點(diǎn),自動(dòng)發(fā)現(xiàn)從節(jié)點(diǎn)

          Sentinel運(yùn)行起來(lái)后,會(huì)根據(jù)配置文件的"sentinel monitor <master-name> <ip> <redis-port> <quorum>"去連接master,這里我們把master-name下的所有節(jié)點(diǎn)看成一個(gè)group,一個(gè)group由一個(gè)master,若干個(gè)slave組成。Sentinel只需配置主節(jié)點(diǎn)的ip、port即可。Sentinel會(huì)通過(guò)向master發(fā)送INFO命令來(lái)獲取master下的slave,然后把slave加入group。

          自動(dòng)發(fā)現(xiàn)監(jiān)控同一個(gè)group的其他Sentinel

          Sentinel連接Redis節(jié)點(diǎn),會(huì)創(chuàng)建兩條對(duì)節(jié)點(diǎn)的連接,一條用來(lái)向節(jié)點(diǎn)發(fā)送命令,另一條用來(lái)訂閱“__sentinel__:hello”頻道(hello頻道)發(fā)來(lái)的消息,這個(gè)頻道用做Gossip協(xié)議發(fā)現(xiàn)其他監(jiān)控此Redis節(jié)點(diǎn)的Sentinel。每個(gè)Sentinel會(huì)定時(shí)向頻道發(fā)送消息,然后也會(huì)接收到其他Sentinel從hello頻道發(fā)來(lái)的消息,消息的格式如下:sentinel_ip,sentinel_port,sentinel_runid,current_epoch,master_name,master_ip,master_port,master_config_epoch(127.0.0.1,26381,99ce8dc79e55ce9de040b0cd13d152900db9a7e1,24,mymaster2,127.0.0.1,8000,0)。

          這樣監(jiān)控同一個(gè)group的的Sentinel就組成了一個(gè)集群,每個(gè)Sentinel會(huì)創(chuàng)建一條連向其他Sentinel的連接,這是在做自動(dòng)故障轉(zhuǎn)移的時(shí)候可以像其他節(jié)點(diǎn)發(fā)送命令。

          推送消息

          Sentinel在監(jiān)控Redis和其他Sentinel的時(shí)候,發(fā)現(xiàn)的異常以及完成的操作都會(huì)通過(guò)Publish的方式推送出去,客戶端想了解Sentinel的處理結(jié)果,只要訂閱相應(yīng)的消息類型即可。Sentinel使用的推送方式用的是Redis現(xiàn)有的Pub/Sub方式。Sentinel基本上會(huì)把它整個(gè)處理流程都推送出來(lái),然而客戶端一般只要關(guān)注主從切換的消息即可。

          故障檢查

          Sentinel會(huì)跟group的每個(gè)節(jié)點(diǎn)以及監(jiān)控同一個(gè)group的其他Sentinel保持心跳,Sentinel會(huì)定時(shí)向這些節(jié)點(diǎn)發(fā)送Ping命令,然后等待Pong命令回復(fù)。如果一段時(shí)間沒(méi)有收到Pong命令。Sentinel就會(huì)主觀的認(rèn)為該節(jié)點(diǎn)離線。對(duì)于其他Sentinel和group內(nèi)的slave掛了,Sentinel檢測(cè)到他們離線也不需要做什么事情,只是簡(jiǎn)單的推送一條+sdown的消息。如果檢測(cè)到master離線,Sentinel就要確定是否需要進(jìn)行主從切換。此時(shí)Sentinel會(huì)向其他Sentinel發(fā)送is-master-down-by-addr命令(命令格式:SENTINEL IS-master-DOWN-BY-ADDR <ip> <port> <current-epoch> <runid>),這個(gè)命令有2個(gè)功能,這時(shí)候的用法是用來(lái)向其他節(jié)點(diǎn)獲取master是否離線的信息。還有一個(gè)用法是用來(lái)選舉leader的,該命令會(huì)讓其他Sentinel給自己投票,已經(jīng)投過(guò)票的Sentinel會(huì)返回投票的結(jié)果。Sentinel在監(jiān)控group的時(shí)候會(huì)配置一個(gè)quorum,Sentinel接收到超過(guò)quorum個(gè)Sentinel認(rèn)為master掛了(quorum包含自己),Sentinel就會(huì)認(rèn)為該master是客觀下線了。接著Sentinel就進(jìn)入了自動(dòng)故障轉(zhuǎn)移狀態(tài)。

          Sentinel間投票選leader

          Sentinel認(rèn)為master客觀下線了,就開(kāi)始故障轉(zhuǎn)移流程,故障轉(zhuǎn)移的第一步就是競(jìng)選leaderSentinel采用了Raft協(xié)議實(shí)現(xiàn)了Sentinel間選舉Leader的算法,不過(guò)也不完全跟論文描述的步驟一致。Sentinel集群運(yùn)行過(guò)程中故障轉(zhuǎn)移完成,所有Sentinel又會(huì)恢復(fù)平等。Leader僅僅是故障轉(zhuǎn)移操作出現(xiàn)的角色。

          選舉流程

          Sentinel采用了Raft協(xié)議實(shí)現(xiàn)了Sentinel間選舉Leader的算法,不過(guò)也不完全跟論文描述的步驟一致。

          • 1、某個(gè)Sentinel認(rèn)定master客觀下線的節(jié)點(diǎn)后,該Sentinel會(huì)先看看自己有沒(méi)有投過(guò)票,如果自己已經(jīng)投過(guò)票給其他Sentinel了,在2倍故障轉(zhuǎn)移的超時(shí)時(shí)間自己就不會(huì)成為L(zhǎng)eader。相當(dāng)于它是一個(gè)Follower。
          • 2、如果該Sentinel還沒(méi)投過(guò)票,那么它就成為Candidate。
          • 3、和Raft協(xié)議描述的一樣,成為Candidate,Sentinel需要完成幾件事情
            • 1)更新故障轉(zhuǎn)移狀態(tài)為start
            • 2)當(dāng)前epoch加1,相當(dāng)于進(jìn)入一個(gè)新term,在Sentinel中epoch就是Raft協(xié)議中的term。
            • 3)更新自己的超時(shí)時(shí)間為當(dāng)前時(shí)間隨機(jī)加上一段時(shí)間,隨機(jī)時(shí)間為1s內(nèi)的隨機(jī)毫秒數(shù)。
            • 4)向其他節(jié)點(diǎn)發(fā)送is-master-down-by-addr命令請(qǐng)求投票。命令會(huì)帶上自己的epoch。
            • 5)給自己投一票,在Sentinel中,投票的方式是把自己master結(jié)構(gòu)體里的leader和leader_epoch改成投給的Sentinel和它的epoch。
          • 4、其他Sentinel會(huì)收到Candidate的is-master-down-by-addr命令。如果Sentinel當(dāng)前epoch和Candidate傳給他的epoch一樣,說(shuō)明他已經(jīng)把自己master結(jié)構(gòu)體里的leader和leader_epoch改成其他Candidate,相當(dāng)于把票投給了其他Candidate。投過(guò)票給別的Sentinel后,在當(dāng)前epoch內(nèi)自己就只能成為Follower。
          • 5、Candidate會(huì)不斷的統(tǒng)計(jì)自己的票數(shù),直到他發(fā)現(xiàn)認(rèn)同他成為L(zhǎng)eader的票數(shù)超過(guò)一半而且超過(guò)它配置的quorum(quorum可以參考《redis sentinel(哨兵) 設(shè)計(jì)與實(shí)現(xiàn)》)。Sentinel比Raft協(xié)議增加了quorum,這樣一個(gè)Sentinel能否當(dāng)選Leader還取決于它配置的quorum。
          • 6、如果在一個(gè)選舉時(shí)間內(nèi),Candidate沒(méi)有獲得超過(guò)一半且超過(guò)它配置的quorum的票數(shù),自己的這次選舉就失敗了。
          • 7、如果在一個(gè)epoch內(nèi),沒(méi)有一個(gè)Candidate獲得更多的票數(shù)。那么等待超過(guò)2倍故障轉(zhuǎn)移的超時(shí)時(shí)間后,Candidate增加epoch重新投票。
          • 8、如果某個(gè)Candidate獲得超過(guò)一半且超過(guò)它配置的quorum的票數(shù),那么它就成為了Leader。
          • 9、與Raft協(xié)議不同,Leader并不會(huì)把自己成為L(zhǎng)eader的消息發(fā)給其他Sentinel。其他Sentinel等待Leader從slave選出master后,檢測(cè)到新的master正常工作后,就會(huì)去掉客觀下線的標(biāo)識(shí),從而不需要進(jìn)入故障轉(zhuǎn)移流程。

          關(guān)于Sentinel超時(shí)時(shí)間的說(shuō)明

          Sentinel超時(shí)機(jī)制并不像Raft協(xié)議描述的那樣只使用了一個(gè)隨機(jī)超時(shí)機(jī)制。它有幾個(gè)超時(shí)概念。

          • failover_start_time 下一選舉啟動(dòng)的時(shí)間。默認(rèn)是當(dāng)前時(shí)間加上1s內(nèi)的隨機(jī)毫秒數(shù)
          • failover_state_change_time 故障轉(zhuǎn)移中狀態(tài)變更的時(shí)間。
          • failover_timeout 故障轉(zhuǎn)移超時(shí)時(shí)間。默認(rèn)是3分鐘。
          • election_timeout 選舉超時(shí)時(shí)間,是默認(rèn)選舉超時(shí)時(shí)間和failover_timeout的最小值。默認(rèn)是10s。

          Follower成為Candidate后,會(huì)更新failover_start_time為當(dāng)前時(shí)間加上1s內(nèi)的隨機(jī)毫秒數(shù)。更新failover_state_change_time為當(dāng)前時(shí)間。

          Candidate的當(dāng)前時(shí)間減去failover_start_time大于election_timeout,說(shuō)明Candidate還沒(méi)獲得足夠的選票,此次epoch的選舉已經(jīng)超時(shí),那么轉(zhuǎn)變成Follower。需要等到mstime() - failover_start_time < failover_timeout*2的時(shí)候才開(kāi)始下一次獲得成為Candidate的機(jī)會(huì)。

          如果一個(gè)Follower把某個(gè)Candidate設(shè)為自己認(rèn)為的Leader,那么它的failover_start_time會(huì)設(shè)置為當(dāng)前時(shí)間加上1s內(nèi)的隨機(jī)毫秒數(shù)。這樣它就進(jìn)入了上面說(shuō)的需要等到mstime() - failover_start_time < failover_timeout*2的時(shí)候才開(kāi)始下一次獲得成為Candidate的機(jī)會(huì)。

          因?yàn)槊總€(gè)Sentinel判斷節(jié)點(diǎn)客觀下線的時(shí)間不是同時(shí)開(kāi)始的,一般都有先后,這樣先開(kāi)始的Sentinel就更有機(jī)會(huì)贏得更多選票,另外failover_state_change_time為1s內(nèi)的隨機(jī)毫秒數(shù),這樣也把各個(gè)節(jié)點(diǎn)的超時(shí)時(shí)間分散開(kāi)來(lái)。本人嘗試過(guò)很多次,Sentinel間的Leader選舉過(guò)程基本上一個(gè)epoch內(nèi)就完成了。

          故障轉(zhuǎn)移

          Sentinel一旦確定自己是leader后,就開(kāi)始從slave中選出一個(gè)節(jié)點(diǎn)來(lái)作為master。以下是選擇slave的流程:

          • 1、過(guò)濾掉slave列表中主觀、客觀下線和離線的slave。
          • 2、過(guò)濾掉slave列表中5s沒(méi)響應(yīng)ping的slave。
          • 3、過(guò)濾掉slave列表中優(yōu)先級(jí)為0的slave。(slave的優(yōu)先級(jí)是redis的配置參數(shù)slave-priority,默認(rèn)是100)
          • 3、過(guò)濾掉slave列表中一段時(shí)間沒(méi)有回復(fù)INFO的slave。
          • 4、過(guò)濾掉slave列表中很久沒(méi)跟master連接的slave。
          • 5、比較篩選后的slave,優(yōu)先級(jí)小的slave被選為新master。
          • 6、如果優(yōu)先級(jí)相同,比較slave對(duì)原master的復(fù)制偏移量,偏移量大的slave被選為新master。
          • 7、如果復(fù)制偏移量相同,那就直接比較slave的運(yùn)行id,字符串小的slave被選為新master。

          如果依據(jù)選擇流程沒(méi)有選出可用的slave,leader Sentinel會(huì)終止本次故障轉(zhuǎn)移。

          如果選擇除了可用的slave,那么leader Sentinel會(huì)給該slave發(fā)送slaveof no one命令,表示該slave不再?gòu)?fù)制其他節(jié)點(diǎn),成為了master。然后leader Sentinel就會(huì)一直等待選出slave的INFO信息里面確認(rèn)了自己的master身份。如果等待超時(shí)了,leader Sentinel只得終止本次故障轉(zhuǎn)移。

          如果選出的slave確認(rèn)了自己的master身份,leader Sentinel會(huì)讓其他slave復(fù)制新的master,由于初次復(fù)制會(huì)帶來(lái)很大的IO開(kāi)銷,Sentinel有個(gè)parallel_syncs參數(shù),用來(lái)確定一次讓多少個(gè)slave復(fù)制新master。一個(gè)slave復(fù)制master如果超過(guò)10s,leader Sentinel會(huì)重新發(fā)送復(fù)制命令。如果在指定的故障轉(zhuǎn)移時(shí)間內(nèi)還沒(méi)有完成全部的復(fù)制工作,leader Sentinel就會(huì)忽略那些沒(méi)復(fù)制的slave。leader Sentinel只是向這些slave發(fā)送一次復(fù)制命令,不等待他們復(fù)制成功就直接完成了全部故障轉(zhuǎn)移工作。

          全部故障轉(zhuǎn)移工作完成后,leader Sentinel就會(huì)推送+switch-master消息,同時(shí)重置master,重置操作會(huì)釋放掉原來(lái)master全部的slave對(duì)象和監(jiān)聽(tīng)該master的其他Sentinel對(duì)象,然后創(chuàng)建出新的slave對(duì)象。

          TILT保護(hù)模式

          TILT模式是Sentinel發(fā)現(xiàn)進(jìn)程出現(xiàn)異常時(shí)候的一種保護(hù)模式。Sentinel定時(shí)器默認(rèn)每100ms執(zhí)行一次,Sentinel每次啟動(dòng)定時(shí)任務(wù)的時(shí)候會(huì)檢查下上次定時(shí)任務(wù)執(zhí)行的時(shí)間,如果超過(guò)2s或者小于0了,Sentinel就認(rèn)為操作系統(tǒng)出現(xiàn)異常,導(dǎo)致一次任務(wù)的執(zhí)行時(shí)間過(guò)長(zhǎng),就會(huì)進(jìn)入TILT模式。TILT模式下,Sentinel不再執(zhí)行任何操作。其他Sentinel發(fā)送的SENTINEL is-master-down-by-addr命令會(huì)直接返回負(fù)值。

          如果TILT模式下的Sentinel正常運(yùn)行超過(guò)30s,Sentinel就會(huì)解除TILT模式。

          客戶端處理流程

          客戶端可以通過(guò)Sentinel獲得group的信息。官方給出了客戶端操作的推薦方式。看了下Jedis的實(shí)現(xiàn),基本就是按照官方的操作流程進(jìn)行的。首先客戶端配置監(jiān)聽(tīng)該group的全部Sentinel。連接第一個(gè)Sentinel,如果連不上就重新連接下一個(gè),直到連上一個(gè)Sentinel。

          連上Sentinel后,發(fā)送SENTINEL get-master-addr-by-name master-name命令可以得到該group的master,如果該Sentinel返回了null,那就重復(fù)上面的流程,重新連接下一個(gè)Sentinel。

          得到group的master后,連上master,發(fā)送ROLE命令,確認(rèn)該master自身確實(shí)是作為master在運(yùn)行。這個(gè)確認(rèn)是必須的,如果Sentinel和group網(wǎng)絡(luò)分區(qū)了,那么該Sentinel認(rèn)為的master就不會(huì)變化了,而group如果出現(xiàn)主從切換,此時(shí)Sentinel就拿不到真實(shí)的master了。如果ROLE得到的不再是master了,客戶端需要重復(fù)最前面的流程,重新連接下一個(gè)Sentinel。

          確認(rèn)好master的ROLE也是master后,客戶端可以從每個(gè)Sentinel上訂閱消息。一般客戶端只要關(guān)心+switch-master即可,這個(gè)消息會(huì)告訴客戶端發(fā)生了主從切換,并把新老master的ip、port都推送在消息里。客戶端根據(jù)新的master,發(fā)送ROLE命令確認(rèn)后,就可以和新的master通信了。

          有些客戶端希望把讀流量分給slave,那么可以通過(guò)SENTINEL slaves master-name命令來(lái)獲得該group下的slave列表。

          如果客戶端需要重連master,那么建議按照初始化連接的方式重新從Sentinel獲取master。

          如果采用連接池的方式,官方建議在每次有連接斷開(kāi)需要重連的時(shí)候所有的連接都關(guān)閉,從而重建連接池。這么做也是為了防止新的連接獲取的master跟原來(lái)不一致了。

          客戶端還可以通過(guò)SENTINEL sentinels <master-name>命令更新自己的Sentinel列表,從而獲得最新存活的Sentinel。

          狀態(tài)持久化

          Sentinel的狀態(tài)會(huì)持久化到配置文件。例如每一次通過(guò)set命令設(shè)置新的配置,或者加入新節(jié)點(diǎn)的監(jiān)控,修改的配置會(huì)持久化到配置文件,同時(shí)持久化配置的epoch,這意味著停止和重啟Sentinel是安全的。

          分區(qū)問(wèn)題

          Sentinel集群會(huì)有分區(qū)問(wèn)題,這個(gè)在官方文檔上有說(shuō)明。

          在該圖中,Redis 3原來(lái)是master,網(wǎng)絡(luò)分區(qū)后,Sentinel1和Sentinel2會(huì)把Redis 1選舉為master。此時(shí)問(wèn)題出現(xiàn)了,由于Sentinel3和Redis3處于另外分區(qū),所以Sentinel3依然認(rèn)為Redis3是master,此時(shí)處于分區(qū)內(nèi)的Client B從Sentinel3獲得的master就是Redis3,而Redis3也認(rèn)為自己是Redis3,客戶端依然能夠操作group,此時(shí)Client A和Client B操作的master已經(jīng)不一樣了,同一個(gè)group出現(xiàn)了不一致現(xiàn)象。Redis官方給出了兩個(gè)建議。

          等分區(qū)恢復(fù)后,Client B在Redis3上的寫(xiě)數(shù)據(jù)會(huì)丟失,如果你把Redis當(dāng)做緩存,能夠接受這種現(xiàn)象,那么可以忽略這個(gè)問(wèn)題。

          如果不想忽略這個(gè)問(wèn)題,那么對(duì)于redis可以這樣配置

          min-slaves-to-write 1 min-slaves-max-lag 10 

          min-slaves-to-write設(shè)置為1,保證了master認(rèn)為至少有一個(gè)slave連接正常,master才能正常工作,上面Redis3因?yàn)橐呀?jīng)與原來(lái)的兩個(gè)slave無(wú)法連接,所以Redis3此時(shí)已經(jīng)無(wú)效了。(min-slaves-max-lag是主從ack延遲的最大時(shí)間。單位是秒)

          連接爆炸

          Sentinel還有個(gè)連接爆炸的問(wèn)題。Sentinel的所有操作都是基于group進(jìn)行的,不同group之間流程完全不干擾,Sentinel會(huì)去發(fā)現(xiàn)監(jiān)控相同group的其他Sentinel,即使一個(gè)在其他group的Sentinel已經(jīng)和本Sentinel建立了連接,在這個(gè)group內(nèi),也仍然會(huì)繼續(xù)建立連接,同時(shí)從這個(gè)連接發(fā)送ping命令確定其他Sentinel的存活,這個(gè)帶來(lái)的好處是流程簡(jiǎn)潔,代碼清晰。但缺點(diǎn)就是帶來(lái)了連接數(shù)的消耗和大量的重復(fù)消息。下面我們從代碼的層面看下出現(xiàn)連接爆炸的原因。整個(gè)Sentinel主要就使用了兩個(gè)結(jié)構(gòu)體。一個(gè)是sentinelState,用來(lái)記錄Sentinel的全局狀態(tài)。另一個(gè)是sentinelRedisInstance,用來(lái)記錄Sentinel監(jiān)控的每一個(gè)節(jié)點(diǎn)的信息。sentinelState有一個(gè)記錄所有g(shù)roup的hash表,dict *masters。masters記錄了所有主節(jié)點(diǎn)的信息,hash表的鍵是主節(jié)點(diǎn)的名稱,值是一個(gè)sentinelRedisInstance結(jié)構(gòu)。

          struct sentinelState {     ...     dict *masters;      /* Dictionary of master sentinelRedisInstances.                            Key is the instance name, value is the                            sentinelRedisInstance structure pointer. */     ... } sentinel; 

          sentinelRedisInstance結(jié)構(gòu)里面記錄了整個(gè)group的詳細(xì)信息,其中包括slave的hash表和sentinel的hash表。

          typedef struct sentinelRedisInstance {     ...     dict *sentinels;    /* Other sentinels monitoring the same master. */     dict *slaves;       /* slaves for this master instance. */     ... } sentinelRedisInstance;     

          dict *sentinels記錄了監(jiān)控該group除自己外的其他Sentinel。問(wèn)題就出在這里。前面介紹了Sentinel發(fā)現(xiàn)其他Sentinel是通過(guò)訂閱hello頻道的消息。Sentinel會(huì)訂閱每個(gè)group的消息,然后當(dāng)在一個(gè)group的hello頻道發(fā)現(xiàn)一個(gè)新的Sentinel后,Sentinel會(huì)為這個(gè)Sentinel生成一個(gè)新的sentinelRedisInstance結(jié)構(gòu),加入該group的sentinels的hash表里面。每次生成新的sentinelRedisInstance結(jié)構(gòu),都是從內(nèi)存重新分配數(shù)據(jù),重新和Sentinel建立連接。這樣如果另外也有一個(gè)Sentinel和自己監(jiān)聽(tīng)了相同group列表的話,他們會(huì)針對(duì)每個(gè)group彼此都建立一條連接。隨著Sentinel監(jiān)聽(tīng)的group針對(duì),連接將成倍數(shù)的增加!這里還沒(méi)結(jié)束。Sentinel有個(gè)輪詢線程會(huì)監(jiān)聽(tīng)每個(gè)節(jié)點(diǎn)的狀態(tài)。這樣,對(duì)另一個(gè)Sentinel,本Sentinel會(huì)對(duì)每個(gè)group上建立的連接向另一個(gè)Sentinel發(fā)送Ping命令,從而產(chǎn)生了大量的重復(fù)消息。

          Sentinel命令列表

          以下對(duì)Sentinel的每個(gè)命令做個(gè)說(shuō)明。

          * ping 用來(lái)探測(cè)節(jié)點(diǎn)的存活,正常會(huì)返回pong。 * sentinel 下面也很多子命令        masters(SENTINEL masterS) 獲得Sentinel監(jiān)控的所有master信息        master  (SENTINEL master <name>) 獲得某個(gè)master信息        slaves  (SENTINEL slaveS <master-name>) 獲得某個(gè)master信息        sentinels  (SENTINEL SENTINELS <master-name>) 獲得監(jiān)控同一個(gè)group的其他sentinel信息        is-master-down-by-addr  (SENTINEL IS-master-DOWN-BY-ADDR <ip> <port> <current-epoch> <runid>*/ 獲得該Sentinel對(duì)于某個(gè)master存活狀態(tài),同時(shí)讓該Sentinel為自己投票        reset  (SENTINEL RESET <pattern>) 重置指定pattern的group信息        get-master-addr-by-name  (SENTINEL GET-master-ADDR-BY-NAME <master-name>)    獲得某個(gè)group下master信息        failover  (SENTINEL FAILOVER <master-name>) 主動(dòng)觸發(fā)一次故障轉(zhuǎn)移        pending-scripts  (SENTINEL PENDING-SCRIPTS) 執(zhí)行腳本        monitor  (SENTINEL MONITOR <name> <ip> <port> <quorum>) 開(kāi)始監(jiān)控某個(gè)group        remove  (SENTINEL REMOVE <name>) 移除對(duì)該group的監(jiān)控        set       (SENTINEL SET <mastername> [<option> <value> ...]) sentinel動(dòng)態(tài)設(shè)置Sentinel配置命令             down-after-milliseconds  (down-after-millisecodns <milliseconds>) 設(shè)置該<mastername> ping多長(zhǎng)時(shí)間未響應(yīng)才判定為離線,默認(rèn)是30s             failover-timeout            (failover-timeout <milliseconds>) 故障轉(zhuǎn)移的超時(shí)時(shí)間,默認(rèn)是180s             parallel-syncs            (parallel-syncs <milliseconds>) 同時(shí)讓多少個(gè)slave復(fù)制新的master,默認(rèn)是1             notification-script       (notification-script <path>) 用于通知管理員的腳本的地址             client-reconfig-script       (client-reconfig-script <path>) 需要執(zhí)行的腳本的地址             auth-pass                 (auth-pass <password>) group的master密碼             quorum                      (quorum <count>) 用于配置多少個(gè)Sentinel認(rèn)為節(jié)點(diǎn)下線才認(rèn)為客觀下線的數(shù)量。 * subscribe 訂閱某個(gè)頻道 * unsubscribe 退訂某個(gè)頻道 * psubscribe 訂閱某個(gè)模式,可以批量訂閱一些頻道 * punsubscribe 退訂某個(gè)模式 * publish 發(fā)布消息,用作測(cè)試。只能發(fā)布hello頻道的消息 * info 查看Sentinel信息。 * role    查看Sentinel和負(fù)責(zé)監(jiān)控的master列表。 * shutdown 關(guān)閉Sentinel 

          推送的消息內(nèi)容

          以下是Sentinel推送的所有消息。從Sentinel的推送消息,基本上可以看到Sentinel運(yùn)行的整個(gè)流程。

          • +monitor quorum 有新的master被監(jiān)控。
          • +reset-master master器已被重置。
          • +slave 一個(gè)新的slave已經(jīng)被Sentinel檢測(cè)到并關(guān)聯(lián)到對(duì)應(yīng)的master。
          • -pubsub-link 訂閱連接斷線。
          • -cmd-link 命令連接斷開(kāi)。
          • +pubsub-link 推送訂閱連接連上的消息。
          • +cmd-link 推送命令連接連上的消息。
          • -cmd-link-reconnection 命令連接重連出錯(cuò)。
          • -pubsub-link-reconnection 訂閱連接重連出錯(cuò)。
          • +reboot 節(jié)點(diǎn)重啟,更換了新的runid。
          • +role-change new reported role is 通過(guò)解析info信息發(fā)現(xiàn)role可能有變化,role發(fā)生變化。
          • -role-change new reported role is 通過(guò)解析info信息發(fā)現(xiàn)role可能有變化,role沒(méi)有發(fā)生變化。
          • +promoted-slave 故障轉(zhuǎn)移過(guò)測(cè)中,新的master自己確認(rèn)了master的角色。
          • +failover-state-reconf-slaves 該消息緊接著+promoted-slave消息,此時(shí)故障轉(zhuǎn)移已經(jīng)進(jìn)入了reconf-slaves狀態(tài)。
          • +convert-to-slave 新的master一段時(shí)間沒(méi)有確認(rèn)自己的master角色,而且它原來(lái)的master已經(jīng)運(yùn)行正常了,則重新復(fù)制原來(lái)的master,自己重新做回slave。
          • +fix-slave-config slave現(xiàn)在的master地址和Sentinel保存的master不一致,則讓slave重新復(fù)制Sentinel認(rèn)為的master。
          • +slave-reconf-inprog slave正在復(fù)制master。
          • +slave-reconf-done slave復(fù)制master完成。 * -dup-sentinel #duplicate of : or 通過(guò)hello頻道得到的Sentinel信息與已經(jīng)保存的Sentinel信息產(chǎn)生沖突,需要被移除 —— 當(dāng) Sentinel 實(shí)例重啟的時(shí)候,就會(huì)出現(xiàn)這種情況。
          • +sentinel 一個(gè)新的Sentinel已經(jīng)被Sentinel檢測(cè)到并關(guān)聯(lián)到對(duì)應(yīng)的master。
          • +new-epoch 當(dāng)前epoch被更新。
          • +config-update-from leader完成故障轉(zhuǎn)移后,其他Sentinel通過(guò)hello頻道獲得新的配置信息。
          • +switch-master <master-name> <old master ip> <old master port> <new master ip> <new master port> 配置變更,maseter的IP和port已經(jīng)改變。這是絕大多數(shù)外部用戶都關(guān)心的信息。
          • -monitor 去掉了對(duì)該master的監(jiān)控。
          • +set
          • +sdown 給定的實(shí)例現(xiàn)在處于主觀下線狀態(tài)。
          • -sdown 給定的實(shí)例已經(jīng)不再處于主觀下線狀態(tài)。
          • +odown #quorum / 給定的實(shí)例現(xiàn)在處于客觀下線狀態(tài)。
          • -odown 給定的實(shí)例已經(jīng)不再處于客觀下線狀態(tài)。
          • +vote-for-leader 把某個(gè)Sentinel設(shè)置為leader。
          • +try-failover 嘗試故障遷移操作,等待被大多數(shù)Sentinel選中。
          • -failover-abort-not-elected Sentinel 的當(dāng)選時(shí)間已過(guò),取消故障轉(zhuǎn)移計(jì)劃。
          • +elected-leader 贏得指定epoch的選舉,可以進(jìn)行故障遷移操作。
          • +failover-state-select-slave 故障轉(zhuǎn)移步驟中開(kāi)始選擇slave
          • -failover-abort-no-good-slave Sentinel沒(méi)有找到合適的slave提升為master,一段時(shí)間后將重試,但是也可能在重試的時(shí)候出現(xiàn)相同的找不到合適slave的情況。
          • +selected-slave 故障轉(zhuǎn)移中,選出了slave作為新的master。
          • +failover-state-send-slaveof-noone 故障轉(zhuǎn)移中,選出了slave作為新的master后準(zhǔn)備把slave提升為master。
          • -failover-abort-slave-timeout slave提升為master超時(shí)。
          • +failover-state-wait-promotion 新master執(zhí)行slaveof no one成功。
          • +failover-end-for-timeout 故障轉(zhuǎn)移超過(guò)時(shí)間,還有slave沒(méi)有復(fù)制完新的master。
          • +failover-end 故障轉(zhuǎn)移順利結(jié)束。
          • +slave-reconf-sent-be 故障轉(zhuǎn)移超過(guò)時(shí)間,還有slave沒(méi)有復(fù)制完新的master。這些slave將直接發(fā)送復(fù)制新master命令后就完成了整個(gè)故障轉(zhuǎn)移操作。
          • -slave-reconf-sent-timeout 超時(shí)的slave也正常完成復(fù)制工作。
          • +slave-reconf-sent leader Sentinel 向slave發(fā)送了復(fù)制命令,為slave設(shè)置新的master。
          • -tilt #tilt mode exited 退出 tilt 模式。
          • +tilt #tilt mode entered 進(jìn)入 tilt 模式。

          參考資料:

          Redis 2.8.19 source code

          http://redis.io/topics/sentinel

          http://redis.io/topics/sentinel-clients

          http://redisdoc.com/topic/sentinel.html

          http://www.wzxue.com/redis核心解讀-集群管理工具redis-sentinel

          《In Search of an Understandable Consensus Algorithm》 Diego Ongaro and John Ousterhout Stanford University

          《Redis設(shè)計(jì)與實(shí)現(xiàn)》黃健宏 機(jī)械工業(yè)出版社

          posted on 2016-12-14 18:33 jinfeng_wang 閱讀(1569) 評(píng)論(0)  編輯  收藏 所屬分類: 2016-REDIS
          主站蜘蛛池模板: 平昌县| 读书| 昭通市| 丽水市| 乌拉特后旗| 古蔺县| 彰化市| 布尔津县| 富阳市| 原平市| 淮阳县| 浠水县| 万载县| 芜湖县| 大港区| 谢通门县| 上杭县| 彩票| 通城县| 黎平县| 伊川县| 亚东县| 诸暨市| 津市市| 宽城| 新营市| 阳泉市| 新密市| 隆昌县| 阳原县| 龙岩市| 海阳市| 哈尔滨市| 高尔夫| 行唐县| 延寿县| 灵川县| 郸城县| 昌图县| 洛隆县| 淮阳县|