A. 搞懂Redis (八) - 哨兵機制
哨兵的核心功能是主節點的自動故障轉移
下圖是一個典型的哨兵集群監控的邏輯圖
Redis Sentinel包含了若干個Sentinel 節點,這樣做也帶來了兩個好處:
1、 對於節點的故障判斷是由多個sentinel節點共同完成,這樣可以有效地防止誤判
2、即使個別sentinel節點不可用,整個sentinel集群依然是可用的
哨兵實現了以下功能:
1、監控:每個sentinel節點會對數據節點(Redis master/slave節點)和其餘sentinel節點進行監控
2、通知:sentinel節點會將故障轉移的結果通知給應用方
3、故障轉移:實現slave晉升為master,並維護後續正確的主從關系
4、配置中心:在Redis sentinel模式中,客戶端在初始化的時候連接的是sentinel節點集合,從中獲取主節點信息
其中,監控和自動故障轉移功能,使得哨兵可以及時發現主節點故障並完成轉移;而配置中心和通知功能,則需要在與客戶端的交互中才能體現
1、原理
監控
sentinel節點需要監控master、slave以及其他sentinel節點的狀態。這一過程是通過Redis的pubsub系統實現的。Redis sentinel一共有三個定時監控任務,完成對各個節點發現和監控:
主觀/客觀下線
主觀下線
每個sentinel節點,每隔1s會對數據節點發送ping命令做心跳檢測,當這些節點超過down-after-milliseconds沒有進行有效回復時,sentinel節點會對該節點做失敗判定,這叫主觀下線
客觀下線
客觀下線,是指當大多數sentinel節點都認為master節點宕機了,那這個判定就是客觀的,叫客觀下線。
那大多數是指什麼呢? 其實就是分布式協調中的quorum判定啦,大多數就是指半數。 如哨兵數量是5,那大多數就是5/2+1=3個,哨兵數量是10大多數就是10/2+1=6個。
註:sentinel節點的數量至少為3個,否則不滿足quorum判定條件
哨兵選舉
如果發生了客觀下線,那哨兵節點會選舉出一個leader來進行實際的故障轉移工作。Redis使用了Raft演算法來實現哨兵領導者選舉,大致思路如下:
故障轉移
選舉出的leader sentinel節點將負責故障轉移,也就是進行master/slave節點的主從切換。故障轉移,首先要從slave節點中篩選出一個作為新的master,主要考慮以下slave信息
註:Leader sentinel 節點,會從新的master節點那裡得到一個configuration epoch,本質是個version版本號,每次主從切換的version號都必須是唯一的。其他的哨兵都是根據version來更新自己的master配置
B. 什麼是哨兵結點,有什麼作用,以及如何使用詳細些。。。謝謝啦(PASCAL語言)
雙向循環鏈表:
如果我們把第一個節點的prev指向最後一個節點,而把最後一個節點的next指向第一個節點,這樣就形成了一個雙向循環鏈表。
哨兵(sentinel):
哨兵(sentinel)是個啞元節點(mmy node),可以簡化邊界條件,使代碼更緊湊,但對速度並沒有什麼幫助。
在基於雙向循環鏈表的實現中,可以設置一個啞元節點(mmy node)。這個節點,起哨兵的作用。也就是說它們並不存儲任何實質的數據對象。初始時可以將啞元節點的next指向第一節點,prev指向最後一個節點。
在一個帶哨兵的環形雙向鏈表中,哨兵節點介於頭和尾之間,用nil[L]來表示。可以通過next[nil[L]]來訪問表頭,而用prev[nil[L]]來訪問表尾。同樣地,表尾的next域和表頭的prev域都指向nil[L]。
因為next[nil[L]]指向表頭,我們可以去掉屬性head[L],把對它的引用換成對next[nil[L]]的引用。
一個空鏈表僅含哨兵節點,這時next[nil[L]]和prev[nil[L]]都可以設置成nil[L]。
C. redis主從和哨兵
主從復制:主節點負責寫數據,從節點負責讀數據,主節點定期把數據同步到從節點保證數據的一致性
a,配置主從復制方式一、新增redis6380.conf, 加入 slaveof 192.168.152.128 6379, 在6379啟動完後再啟6380,完成配置;
b,配置主從復制方式二、redis-server --slaveof 192.168.152.128 6379 臨時生效
c,查看狀態:info replication
d,斷開主從復制:在slave節點,執行6380:>slaveof no one
e,斷開後再變成主從復制:6380:> slaveof 192.168.152.128 6379
f,數據較重要的節點,主從復制時使用密碼驗證: requirepass
e, 從節點建議用只讀模式slave-read-only=yes, 若從節點修改數據,主從數據不一致
h,傳輸延遲:主從一般部署在不同機器上,復制時存在網路延時問題,redis提供repl-disable-tcp-nodelay參數決定是否關閉TCP_NODELAY,默認為關閉
參數關閉時:無論大小都會及時發布到從節點,占帶寬,適用於主從網路好的場景,
參數啟用時:主節點合並所有數據成TCP包節省帶寬,默認為40毫秒發一次,取決於內核,主從的同步延遲40毫秒,適用於網路環境復雜或帶寬緊張,如跨機房
a)一主一從:用於主節點故障轉移從節點,當主節點的「寫」命令並發高且需要持久化,可以只在從節點開啟AOF(主節點不需要),這樣即保證了數據的安全性,也避免持久化對主節點的影響
b)一主多從:針對「讀」較多的場景,「讀」由多個從節點來分擔,但節點越多,主節點同步到多節點的次數也越多,影響帶寬,也加重主節點的穩定
c)樹狀主從:一主多從的缺點(主節點推送次數多壓力大)可用些方案解決,主節點只推送一次數據到從節點B,再由從節點B推送到C,減輕主節點推送的壓力。
redis 2.8版本以上使用psync命令完成同步,過程分「全量」與「部分」復制
全量復制:一般用於初次復制場景(第一次建立SLAVE後全量)
部分復制:網路出現問題,從節點再次連接主節點時,主節點補發缺少的數據,每次數據增量同步
心跳:主從有長連接心跳,主節點默認每10S向從節點發ping命令,repl-ping-slave-period控制發送頻率
a)主從復制,若主節點出現問題,則不能提供服務,需要人工修改配置將從變主
b)主從復制主節點的寫能力單機,能力有限
c)單機節點的存儲能力也有限
a)主節點(master)故障,從節點slave-1端執行 slaveof no one後變成新主節點;
b)其它的節點成為新主節點的從節點,並從新節點復制數據;
c)需要人工干預,無法實現高可用。
1. 為什麼要有哨兵機制?
原理:當主節點出現故障時,由Redis Sentinel自動完成故障發現和轉移,並通知應用方,實現高可用性。
其實整個過程只需要一個哨兵節點來完成,首先使用Raft演算法(選舉演算法)實現選舉機制,選出一個哨兵節點來完成轉移和通知
任務1:每個哨兵節點每10秒會向主節點和從節點發送info命令獲取最拓撲結構圖,哨兵配置時只要配置對主節點的監控即可,通過向主節點發送info,獲取從節點的信息,並當有新的從節點加入時可以馬上感知到
任務2:每個哨兵節點每隔2秒會向redis數據節點的指定頻道上發送該哨兵節點對於主節點的判斷以及當前哨兵節點的信息,同時每個哨兵節點也會訂閱該頻道,來了解其它哨兵節點的信息及對主節點的判斷,其實就是通過消息publish和subscribe來完成的
任務3:每隔1秒每個哨兵會向主節點、從節點及其餘哨兵節點發送一次ping命令做一次心跳檢測,這個也是哨兵用來判斷節點是否正常的重要依據
客觀下線:當主觀下線的節點是主節點時,此時該哨兵3節點會通過指令sentinel is-masterdown-by-addr尋求其它哨兵節點對主節點的判斷,當超過quorum(選舉)個數,此時哨兵節點則認為該主節點確實有問題,這樣就客觀下線了,大部分哨兵節點都同意下線操作,也就說是客觀下線
a)每個在線的哨兵節點都可以成為領導者,當它確認(比如哨兵3)主節點下線時,會向其它哨兵發is-master-down-by-addr命令,徵求判斷並要求將自己設置為領導者,由領導者處理故障轉移;
b)當其它哨兵收到此命令時,可以同意或者拒絕它成為領導者;
c)如果哨兵3發現自己在選舉的票數大於等於num(sentinels)/2+1時,將成為領導者,如果沒有超過,繼續選舉…………
a)由Sentinel節點定期監控發現主節點是否出現了故障
sentinel會向master發送心跳PING來確認master是否存活,如果master在「一定時間范圍」內不回應PONG 或者是回復了一個錯誤消息,那麼這個sentinel會主觀地(單方面地)認為這個master已經不可用了
b) 當主節點出現故障,此時3個Sentinel節點共同選舉了Sentinel3節點為領導,負載處理主節點的故障轉移
c) 由Sentinel3領導者節點執行故障轉移,過程和主從復制一樣,但是自動執行
流程:
1. 將slave-1脫離原從節點,升級主節點,
d) 故障轉移後的redis sentinel的拓撲結構圖
a) 過濾掉不健康的(下線或斷線),沒有回復過哨兵ping響應的從節點
b) 選擇salve-priority從節點優先順序最高(redis.conf)的
c) 選擇復制偏移量最大,指復制最完整的從節點
以3個Sentinel節點、2個從節點、1個主節點為例進行安裝部署
1. 前提: 先搭好一主兩從redis的主從復制,和之前的主從復制搭建一樣,搭建方式如下:
A)主節點6379節點(/usr/local/bin/conf/redis6379.conf):
修改 requirepass 12345678,注釋掉#bind 127.0.0.1
B) 從節點redis6380.conf和redis6381.conf: 配置都一樣
修改 requirepass 12345678 ,注釋掉#bind 127.0.0.1,
加上訪問主節點的密碼masterauth 12345678 ,加上slaveof 192.168.152.128 6379
2. redis sentinel哨兵機制核心配置 (也是3個節點):
將三個文件的埠改成: 26379 26380 26381
然後:sentinel monitor mymaster 192.168.152.128 6379 2 //監聽主節點6379
三個配置除埠外,其它一樣。
3. 哨兵其它的配置 :只要修改每個sentinel.conf的這段配置即可:
sentinel monitor mymaster 192.168.152.128 6379 2
//監控主節點的IP地址埠,sentinel監控的master的名字叫做mymaster,2代表,當集群中有2個sentinel認為master死了時,才能真正認為該master已經不可用了
sentinel auth-pass mymaster 12345678 //sentinel連主節點的密碼
sentinel config-epoch mymaster 2 //故障轉移時最多可以有2從節點同時對新主節點進行數據同步
sentinel leader-epoch mymaster 2
sentinel failover-timeout mymasterA **180000 **//故障轉移超時時間180s,
a,如果轉移超時失敗,下次轉移時時間為之前的2倍;
b,從節點變主節點時,從節點執行slaveof no one命令一直失敗的話,當時間超過 180S 時,則故障轉移失敗
c,從節點復制新主節點時間超過 180S 轉移失敗
sentinel down-after-milliseconds mymasterA 300000 //sentinel節點定期向主節點ping命令,當超過了 300S 時間後沒有回復,可能就認定為此主節點出現故障了……
sentinel parallel-syncs mymasterA 1 //故障轉移後, 1 代表每個從節點按順序排隊一個一個復制主節點數據,如果為3,指3個從節點同時並發復制主節點數據,不會影響阻塞,但存在網路和IO開銷
4. 啟動redis服務和sentinel服務:
a)先把之前安裝的redis裡面的標綠色的文件都拷貝到 usr/local/bin目錄下,然後再再bin目錄下新建一個conf文件夾存放配置好的redis主從配置文件和哨兵配置文件
b)啟動主從復制服務,先啟動主再啟動從
主:./redis-server conf/redis6379.conf &
從:
./redis-server conf/redis6380.conf &
./redis-server conf/redis6381.conf &
c)啟動sentinel服務:
./redis-sentinel conf/sentinel_26381.conf &
到此服務全部啟動完畢
連接到6379的redis的服務,可看到6379就是主節點,他有6380和6381兩個從節點
5. 測試: kill -9 6379 殺掉6379的redis服務
可以看到殺掉6379以後6380變為了主節點,6381變為了6380的從節點
重新啟動6379以後變為6380的從節點
看日誌是分配6380 是6381的主節點,當6379服務再啟動時,已變成從節點
假設6380升級為主節點:進入6380>info replication 可以看到role:master
打開sentinel_26379.conf等三個配置,sentinel monitor mymaster 192.168.152.128 6380 2
打開redis6379.conf等三個配置, slaveof 192.168.152.128 6380,也變成了6380
注意:生產環境建議讓redis Sentinel部署到不同的物理機上。
a,sentinel節點應部署在多台物理機(線上環境)
b,至少三個且奇數個sentinel節點
c,通過以上我們知道,3個sentinel可同時監控一個主節點或多個主節點
sentinel參考資料:
redis sentinel的機制與用法一: https://segmentfault.com/a/1190000002680804
redis sentinel的機制與用法二: https://segmentfault.com/a/1190000002685515
D. Redis(7):哨兵機制
哨兵(sentinel),是redis集群架構中非常重要的一個組件,它主要的功能和特性如下:
(1)集群監控,負責監控redis master和slave進程是否正常工作。
(2)消息通知,如果某個redis實例有故障,那麼哨兵負責發送消息作為報警通知給管理員。
(3)故障轉移,如果master node掛掉了,會自動轉移到slave node上。
(4)配置中心,如果故障轉移發生了,通知client客戶端新的master地址。
(5)哨兵本身也是分布式的,哨兵以集群的方式來相互協作。
(6)要進行故障轉移時會涉及到分布式選舉,當大部分哨兵節點都同意時才能進行。
(7)當部分哨兵節點不能正常工作時,哨兵集群還是能夠正常工作。
說到這個特點,我們就要說到兩個參數,第一個是quorum,quorum是我們可以給哨兵集群配置的一個參數,它的作用是:當master節點宕機的時候需要有至少quorum個哨兵節點認為mater宕機才能進行後續的操作,比如主備切換、故障轉移,這個時候還需要一個參數majority來判斷是否可以進行故障轉移,需要有大多數哨兵節點正常工作來同同意這次故障轉移,即正常工作的哨兵節點數要大於majority,majority有一個通用的規則:2個哨兵節點的majority為2,3個哨兵節點的majority為2,4個哨兵節點的majority為2,5個哨兵節點的majority為3,以此類推。如果一個master節點宕機了,同時他的哨兵進程同時掛掉了,那麼現在正常運行的哨兵節點數目為1,即使你把quorum設置為1,滿足了quorum的條件,但是是不能滿足正常運行的哨兵節點大於majority,也就是2,所以是不能保證能夠進行故障轉移的。
第一種情況: maser節點和slave節點之間的數據傳輸不是同步的,是非同步的,當客戶端往master中寫數據的時,master非同步的發送給slave節點,這當中會存在一定的延遲,當master節點掛掉的時候是不能保證這一小部分數據會及時的發送給slave節點。
第二種情況:當master節點與slave節點間發生網路故障,導致無法與slave節點通訊,產生網路分區。這時,老的master節點並沒有視為宕機,仍然在不停的寫數據。此時這個slave節點如果被選舉為新的master節點,這樣就會發生「集群腦裂」,客戶端程序還是會源源不斷地將數據寫入老的master節點,新的master節點卻沒有接收到新的數據,當網路恢復正常,老的master節點降為slave節點的時候,會立即執行全量的主從復制,那麼故障之後的數據就會丟失。
現在我們有一主兩從的這樣一個redis 集群,每個redis節點有一個哨兵,我們設置quorum為2,那麼當master節點宕機的話,此時三個哨兵還剩下兩個,剩下的兩個slave節點一致認為master宕機,此時三個哨兵節點的majority為2,剩下還有2個正常運行的哨兵節點,那麼就可以選舉出新的master節點進行故障轉移。
非同步復制數據丟失,集群腦裂數據丟失的問題我們可以通過設置兩個參數來減少數據丟失:
min-slaves-to-write 1
min-slaves-max-lag 10
上面兩個參數表達的意思為:要求至少有1個slave,數據復制和同步的延遲不能超過10秒。也就是說,如果所有的slave節點都落後於master節點10秒鍾的數據,那麼master節點就不會再接受任何請求了。
(1)減少非同步復制的數據丟失
有了min-slaves-max-lag這個配置,一旦slave復制數據和ack延時太長,就認為可能master宕機後損失的數據太多了,那麼就拒絕寫請求,這樣可以把master宕機時由於部分數據未同步到slave導致的數據丟失降低的可控范圍內,最多也就丟失10秒的數據。
(2)減少腦裂的數據丟失
如果一個master出現了腦裂,跟其他slave丟了連接,那麼上面兩個配置可以確保說,如果不能繼續給指定數量的slave發送數據,而且slave超過10秒沒有給自己ack消息,那麼就直接拒絕客戶端的寫請求。這樣腦裂後的舊master就不會接受client的新數據,也就避免了數據丟失。上面的配置就確保了,如果跟任何一個slave丟了連接,在10秒後發現沒有slave給自己ack,那麼就拒絕新的寫請求。因此在腦裂場景下,最多就丟失10秒的數據。
(3)當master不接受請求之後該如何處理
當master不接受寫請求之後,我們需要給client做降級,讓它直接把數據寫到本地磁碟里,讓client自己處理這些數據,做相應的降級和限流,減慢數據湧入的速度。或者將數據寫入一個消息隊列,每隔一段時間取一次,嘗試將數據重新寫入到redis中。
什麼是sdown,sdown又稱為主觀宕機,即一個哨兵覺得master節點宕機,這就是sdown。odown又稱為客觀宕機,即現在有quorum個哨兵都認為master宕機了。判斷sdown的條件就是:一個哨兵ping一個master,超過了is-master-down-after-milliseconds參數指定的毫秒數之後,就主觀認為master宕機。sdown轉換到odown的條件就是:如果一個哨兵在指定時間內收到了超過quorum個哨兵的認為該master節點宕機的信息,就轉換成了odown。
哨兵互相之間的發現,是通過redis的pub/sub系統實現的,每個哨兵都會往__sentinel__:hello這個channel里發送一個消息,這時候所有其他哨兵都可以消費到這個消息,並感知到其他的哨兵的存在。每隔兩秒鍾,每個哨兵都會往自己監控的某個master+slaves對應的__sentinel__:hello channel里發送一個消息,內容是自己的host、ip和runid還有對這個master的監控配置。每個哨兵也會去監聽自己監控的每個master+slaves對應的__sentinel__:hello channel,然後去感知到同樣在監聽這個master+slaves的其他哨兵的存在。每個哨兵還會跟其他哨兵交換對master的監控配置,互相進行監控配置的同步。
哨兵會負責自動糾正slave的一些配置,比如slave如果要成為潛在的master候選人,哨兵會確保slave在復制現有master的數據; 如果slave連接到了一個錯誤的master上,比如故障轉移之後,那麼哨兵會確保它們連接到正確的master上。
如果一個master被認為odown了,而且majority哨兵都允許了主備切換,那麼某個哨兵就會執行主備切換操作,此時首先要選舉一個slave,會考慮slave的一些信息:
a.跟master斷開連接的時長
b.slave優先順序
c.復制offset
d.run id
如果一個slave跟master斷開連接已經超過了down-after-milliseconds的10倍(加上master宕機的時長),那麼slave就被認為不適合選舉為master。計算公式為:(down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state。接下來會對slave進行排序:
a.按照slave優先順序進行排序,slave priority越低,優先順序就越高
b.如果slave priority相同,那麼看replica offset,哪個slave復制了越多的數據,offset越靠後,優先順序就越高
c.如果上面兩個條件都相同,那麼選擇一個run id比較小的那個slave
每次一個哨兵要做主備切換,首先需要quorum數量的哨兵認為odown,然後選舉出一個哨兵來做切換,這個哨兵還得得到majority哨兵的授權,才能正式執行切換。如果quorum < majority,比如5個哨兵,majority就是3,quorum設置為2,那麼就3個哨兵授權就可以執行切換,但是如果quorum >= majority,那麼必須quorum數量的哨兵都授權,比如5個哨兵,quorum是5,那麼必須5個哨兵都同意授權,才能執行切換。
哨兵會對一套redis master+slave進行監控,有相應的監控的配置,執行切換的那個哨兵,會從要切換到的新master(salve->master)那裡得到一個configuration epoch,這就是一個版本號,每次切換的version號都必須是唯一的。如果第一個選舉出的哨兵切換失敗了,那麼其他哨兵,會等待failover-timeout時間,然後接替繼續執行切換,此時會重新獲取一個新的configuration epoch,作為新的版本號。
哨兵完成切換之後,會在自己本地更新生成最新的master配置,然後同步給其他的哨兵,就是通過之前說的pub/sub消息機制。這里之前的版本號就很重要了,因為各種消息都是通過一個channel去發布和監聽的,所以一個哨兵完成一次新的切換之後,新的master配置是跟著新的版本號,其他的哨兵都是根據版本號的大小來更新自己的master配置的。
E. Sentinel(哨兵)
Redis主從復制模式下,一旦主節點(主伺服器)由於故障不能提供服務,需要人工將節點晉升為主節點,同時還要通知應用方更新主節點的地址,然而應用方無法及時感知到主節點的變化,必然會造成一定的寫數據丟失和讀數據錯誤,所以這是在大多數情況是無法接受的。所以Redis提供了一種高可用的解決方法——哨兵。
Sentinel是Redis的高可用解決方案: 由一個或多個Sentinel實例組成的Sentinel系統可以監視任意多個主伺服器以及這些主伺服器屬下的所有從伺服器,並在被監視的主伺服器進入下線狀態下時,自動將下線主伺服器屬下的某個從伺服器升級為主伺服器,然後由新的主伺服器代替已下線的主伺服器繼續處理命令請求。如果下線的主伺服器重新連接上線的話,它會被Sentinel系統降級為新主伺服器的從伺服器。 而這些過程完全是自動的,不需要人工介。
下面介紹Sentinel系統的具體工作過程。
Sentinel只是一個運行在特殊模式下的Redis伺服器,其本身就是獨立的Redis節點,只不過它不存儲數據,只支持部分命令。
Sentinel會讀入用戶指定的配置文件,為每個要被監視的主伺服器創建相應的實例結構保存在Sentinel狀態(伺服器初始化的一個sentinel結構,用於保存伺服器中所有和Sentinel功能有關的狀態)的masters屬性中。並創建連向主伺服器的網路連接,Sentinel將成為主伺服器的客戶端,並從命令回復中獲取相關信息。
對於每個被Sentinel監視的主伺服器來說,Sentinel會創建兩個連向主伺服器的非同步網路連接:
1) 一個是命令連接,這個連接專門用於向主伺服器的網路連接,並接受命令。
2) 另一個是訂閱連接,這個連接專門用於訂閱主伺服器的_sentinel_:hello頻道。
Sentinel默認每10秒一次的頻率,通過命令連接向被 監視的主伺服器發送 INFO 命令,並通過 INFO 命令的回復來獲取主伺服器以下的信息:
(1) 伺服器本身的信息,包括運行ID以及伺服器的角色(role);
(2) 主服務屬下的所有從伺服器信息,包括從伺服器的IP地址,埠號。 根據這些IP和埠號,Sentinel無須用戶提供從伺服器的地址信息,就可以自動發現從伺服器。
Sentinel根據這些獲取的信息對主服務的實例結構進行更新。
對於上圖,Sentinel將分別為3個從伺服器創建各自的實例結構,並將這些實例結構保存主伺服器實例結構的slaves屬性里。
當Sentinel發現主伺服器有新的從伺服器出現時,Sentinel除了會為這個新的從伺服器創建相應的實例結構外,Sentinel還會為創建連接到從伺服器的命令連接和訂閱連接。
在創建命令連接之後, Sentinel在默認情況下, 會以每10秒 一次的頻率通過命令連接向從伺服器發送 INFO 命令,並獲取從伺服器的回復信息,包括從伺服器運行ID、從伺服器的角色,主伺服器的IP和埠、從服務的優先順序等。根據這些信息對從伺服器實例結構進行更新。
在默認情況下,Sentinel會以每2秒一次的頻率,通過命令連接向所有被監聽的主伺服器和從伺服器的_sentinel_ hello頻道發送一條消息。消息的信息包括Sentinel本身的信息和對主伺服器判斷的信息。
當Sentinel與一個主伺服器或者從伺服器建立起訂閱連接之後, Sentinel就會通過訂閱連接, 向伺服器發送以下命令:SUBSCRIBE _sentinel_:hello 。
Sentinel對_sentinel_:hello頻道的訂閱會一直持續到Sentinel與伺服器的連接斷開為止。
這也就是說, 對於每個與Sentinel連接的伺服器, Sentinel既通過命令連接向伺服器的 sentinel_:hello頻道發送信息, 又通過訂閱連接從伺服器的 sentinel :hello 頻道接收信息。
對於監視同一個伺服器的多個Sentinel 來說, 一個Sentinel發送的信息會被其他 Sentinel接收到, 這些信息會被用於更新其他Sentinel對發送信息Sentinel的認知 ,也會被用於更新其他Sentinel對被監視伺服器的認知。
舉個例子, 假設現在有sentinel1、sentinel 2、sentinel 3三個Sentinel在監視同一個伺服器, 那麼當sentinel1向伺服器的_sentinel_:hello頻道發送一條信息時,所有訂閱了_sentinel_:hello頻道的Sentinel(包括sentinel1自己在內)都會收到這條信息。
當一個Sentinel從_sentinel_:hello頻道收到一條信息時,Sentinel會對這條信息進行分析,提取出信息中的Sentinel IP地址、埠號、Sentinel運行ID等參數,並作以下檢查:
**如果信息中記錄的Sentinel運行ID和接收信息中Sentinel的運行ID 相同,說明這條信息是Sentinel自己發送的,Sentinel將丟棄這條信息,不做進一步處理。
**如果信息中記錄的Sentinel運行ID和接收信息的Sentinel的運行ID不相同,那麼說明這條信息是監視同一個伺服器的其他Sentinel發來的, 接收信息的Sentinel 將根據信息中的各個參數, 對相應主伺服器的實例結構進行更新。
這里的更新包括:主服務sentinels屬性的更新和創建連向其他Sentinel命令連接
(1) Sentinel為主伺服器創建的實例結構中的sentinels屬性保存了除Sentinel本身之外,所有同樣監視這個主伺服器的其他Sentinel的資料。當一個Sentinel接收到其他Sentinel發來的信息時(我們稱呼發送信息的Sentinel為源Sentinel, 接收信息的Sentinel為目標Sentinel),根據信息中提取出主伺服器參數,目標Sentinel會在自己的Sentinel狀態的masters字典中查找相應的主伺服器實例結構, 然後根據提取出的Sentinel參數,檢查主伺服器實例結構的sentinels中,源Sentinel的實例結構是否存在:
因為一個Sentinel可以通過分析接收到的頻道信息來獲取其他Sentinel的存在,並通過發送頻道信息讓其他Sentinel知道自己的存在,所以用戶在使用Sentinel時不需要提供各個Sentinel的地址信息,監視同一個主伺服器的多個Sentinel可以自動發現對方。
(2) 創建連向其他Sentinel命令連接
當Sentinel通過頻道信息發現一個新的Sentinel時, 它不僅會為新Sentinel在sentinels中創建相應的實例結構, 還會創建一個連向新Sentinel的命令連接, 而新Sentinel也同樣會創建連向這個Sentinel的命令連接, 最終監視同一主伺服器的多個Sentinel將形成相互連接的網路。
在默認情況下,Sentinel會以 每秒一次 的頻率向所有與它創建了命令的連接實例 (包括主伺服器、從伺服器、其他Sentinel在內),發送PING命令 ,並通過實例返回PING命令的回復判斷實例是否在線,如果在規定的時間內,連續向Sentinel返回無效的回復(除了+PONG、-LOADING、-MASTERDOWN之外的回復),那麼Sentinel會修改這個實例所對應的實例結構,在結構的flags屬性中打開SRI_S_DOWN標識,以此來標識這個實例已經進入了 主觀下線狀態 。
當Sentinel將一個主伺服器判斷為主觀下線之後,為了確認這個主伺服器是否真的下線了, 它會向同樣監視這一主伺服器的其他Sentinel進行詢問, 看它們是否也認為主伺服器已經進人了下線狀態(可以是主觀下線或者客觀下線)。 當Sentinel從其他Sentinel那裡接收到足夠數量的已下線判斷之後, Sentinel就會將從伺服器判定為客觀下線, Sentinel會將主伺服器實例結構flags屬性的SRI_O_DOWN標識打開,標識主伺服器已經進入了客觀下線狀態。
當一個主伺服器被判斷為客觀下線時,監視這個下線的主伺服器的各個Sentinel會進行協商,選舉出以個領頭Sentinel,並由領頭Sentinel對下線主伺服器執行故障轉移操作。
選舉領頭Sentinel規則和方法:
下面兩幅圖表示當三個Sentinel發現主伺服器已經進入客觀下線狀態後,為了選舉出領頭Sentinel,三個Sentinel將再次向其他Sentinel發送SENTINEL is-master-down-by-addr命令要求其他Sentinel將自己設置為局部領頭Sentinel。根據先到先得規則,如果某個Sentinel發送的命令比其他的快,並最終勝出領頭Sentinel的選舉,然後這個領頭Sentinel就可以開始對伺服器執行故障轉移操作了。
在選舉產生出領頭Sentinel之後,領頭Sentinel將對已下線的主伺服器執行故障轉移操作,該操作包含以下三個步驟:
(1) 選出新的主伺服器
故障轉移的第一步就是在已下線主伺服器屬下的所有從伺服器中挑選出一個狀態良好、數據完成整的從伺服器,然後向這個從伺服器發送slave no one 命令,將這個從伺服器裝換為主伺服器。
下圖展示在一次故障轉移操作中,領頭Sentinel向選中的從伺服器server3發送 SLAVEOF no one 命令。
在發送 SLAVEOF no one命令之後,領頭 Sentinel會以每秒一次的頻率(平時是每10秒一次),向被升級的從服服務發送INFO命令,並觀察命令回復中角色(role)信息,當被升級的從伺服器的role由原來的slave變為master時,領頭Sentinel就知道被選中的從伺服器順利升級為主伺服器了。
(2) 修改從伺服器的復制目標
當新的主伺服器出現之後,領頭Sentinel讓已下線主伺服器屬下的所有從伺服器去復制新的主伺服器,這一動作可以通過向從伺服器發送 SLAVE OF 命令來實現。
(3) 將舊的主伺服器變為從伺服器
故障轉移操作最後要做的是, 將巳下線的主伺服器設置為新的主伺服器的從伺服器。
當serverl1重新上線時,Sentinel就會向它發送 SLAVEOF 命令,讓它成為server3的從伺服器。
Redis的Sentinel實現主要包含以下幾個方面: 三個定時任務、主觀下線和客觀下線檢測、領頭Sentinel的選舉、故障轉移。
(1) 定時任務
(2) 主觀下線和客觀下線檢測
客觀下線:當Sentinel將一個主伺服器判斷為主觀下線後,為了確認這個主服務是否真的下線了,他會向同樣監視這個主伺服器的所有其他Sentinel進行詢問,看它們是否也認為主伺服器是否進入下線狀態,如果有足夠多數量的Sentinel認為主伺服器進入下線狀態時,Sentinel就會將主伺服器判定為客觀下線狀態。
(3) 領頭Sentinel的選舉
在主伺服器被判定為客觀下線後,Sentinel之間會根據一定的規則選出一個領頭Sentinel,故障轉移的工作就是這個領頭Sentinel來完成的。
(4) 故障轉移
註:本文參考《Redis設計與實現》,如發現錯誤,請指正!
F. 演算法-哨兵查找法(OC、Swift、Python)
我們在一個數組中想查找某個對象item我們改如何操作呢?很簡單一層遍歷就可以搞定了,如下:
但是我們有沒有更優的演算法來查找呢?
在數據結構的書中我們可以找到「哨兵查找法」,但是什麼又是「哨兵查找法」呢?什麼又是「哨兵」呢?
所謂「哨兵」就是用一個特殊的值來作為數組的邊界key,可以少用一條判斷語句,目的在於免去查找過程中每一步都要檢測整個表是否查找完畢,以達到提高程序的效率。
相對於一層遍歷,沒有使用」哨兵「的,是有兩個判斷條件的i<array.count和if(array[i] == item);但是使用了」哨兵「只有一個判斷條件if(array[i] == item)!
如果你的數據量非常小的話,相對於一層遍歷來說,差別微乎其微,但是當數據達到十萬或者更多的時候,函數的執行時間就會有明顯差距了!
我們可以將數組的第一個值作為」哨兵「,數據存儲下標index從1開始,則list的0號位表示暫無元素,位」哨兵「Key。
比如數組中有一千個元素,我查找中間那個元素,運行結果如下:
歡迎各位大神提出寶貴的意見和建議,也歡迎大家進群交流365152048!