話不多說,發(fā)車發(fā)車!
提綱
為什么要有哨兵機制?
在 redis 的主從架構(gòu)中,由于主從模式是讀寫分離的,如果主節(jié)點(master)掛了,那么將沒有主節(jié)點來服務(wù)客戶端的寫操作請求,也沒有主節(jié)點給從節(jié)點(slave)進行數(shù)據(jù)同步了。
主節(jié)點掛了
這時如果要恢復(fù)服務(wù)的話,需要人工介入,選擇一個「從節(jié)點」切換為「主節(jié)點」,然后讓其他從節(jié)點指向新的主節(jié)點,同時還需要通知上游那些連接 Redis 主節(jié)點的客戶端,將其配置中的主節(jié)點 IP 地址更新為「新主節(jié)點」的 IP 地址。
這樣也不太“智能”了,要是有一個節(jié)點能監(jiān)控「主節(jié)點」的狀態(tài),當(dāng)發(fā)現(xiàn)主節(jié)點掛了 ,它自動將一個「從節(jié)點」切換為「主節(jié)點」的話,那么可以節(jié)省我們很多事情啊!
Redis 在 2.8 版本以后提供的哨兵(Sentinel)機制,它的作用是實現(xiàn)主從節(jié)點故障轉(zhuǎn)移。它會監(jiān)測主節(jié)點是否存活,如果發(fā)現(xiàn)主節(jié)點掛了,它就會選舉一個從節(jié)點切換為主節(jié)點,并且把新主節(jié)點的相關(guān)信息通知給從節(jié)點和客戶端。
哨兵機制是如何工作的?
哨兵其實是一個運行在特殊模式下的 Redis 進程,所以它也是一個節(jié)點。從“哨兵”這個名字也可以看得出來,它相當(dāng)于是“觀察者節(jié)點”,觀察的對象是主從節(jié)點。
當(dāng)然,它不僅僅是觀察那么簡單,在它觀察到有異常的狀況下,會做出一些“動作”,來修復(fù)異常狀態(tài)。
哨兵節(jié)點主要負責(zé)三件事情:監(jiān)控、選主、通知。
哨兵的職責(zé)
所以,我們重點要學(xué)習(xí)這三件事情:
- 哨兵節(jié)點是如何監(jiān)控節(jié)點的?又是如何判斷主節(jié)點是否真的故障了?
- 根據(jù)什么規(guī)則選擇一個從節(jié)點切換為主節(jié)點?
- 怎么把新主節(jié)點的相關(guān)信息通知給從節(jié)點和客戶端呢?
如何判斷主節(jié)點真的故障了?
哨兵會周期性地給所有主從節(jié)點發(fā)送 PING 命令,當(dāng)主從節(jié)點收到 PING 命令后,會發(fā)送一個響應(yīng)命令給哨兵,這樣就可以判斷它們是否在正常運行。
哨兵監(jiān)控主從節(jié)點
如果主節(jié)點或者從節(jié)點沒有在規(guī)定的時間內(nèi)響應(yīng)哨兵的 PING 命令,哨兵就會將它們標(biāo)記為「主觀下線」。這個「規(guī)定的時間」是配置項 down-after-milliseconds 參數(shù)設(shè)定的,單位是毫秒。
主觀下線?難道還有客觀下線?
是的沒錯,客觀下線只適用于主節(jié)點。
之所以針對「主節(jié)點」設(shè)計「主觀下線」和「客觀下線」兩個狀態(tài),是因為有可能「主節(jié)點」其實并沒有故障,可能只是因為主節(jié)點的系統(tǒng)壓力比較大或者網(wǎng)絡(luò)發(fā)送了擁塞,導(dǎo)致主節(jié)點沒有在規(guī)定時間內(nèi)響應(yīng)哨兵的 PING 命令。
所以,為了減少誤判的情況,哨兵在部署的時候不會只部署一個節(jié)點,而是用多個節(jié)點部署成哨兵集群(最少需要三臺機器來部署哨兵集群),通過多個哨兵節(jié)點一起判斷,就可以就可以避免單個哨兵因為自身網(wǎng)絡(luò)狀況不好,而誤判主節(jié)點下線的情況。同時,多個哨兵的網(wǎng)絡(luò)同時不穩(wěn)定的概率較小,由它們一起做決策,誤判率也能降低。
具體是怎么判定主節(jié)點為「客觀下線」的呢?
當(dāng)一個哨兵判斷主節(jié)點為「主觀下線」后,就會向其他哨兵發(fā)起命令,其他哨兵收到這個命令后,就會根據(jù)自身和主節(jié)點的網(wǎng)絡(luò)狀況,做出贊成投票或者拒絕投票的響應(yīng)。
當(dāng)這個哨兵的贊同票數(shù)達到哨兵配置文件中的 quorum 配置項設(shè)定的值后,這時主節(jié)點就會被該哨兵標(biāo)記為「客觀下線」。
例如,現(xiàn)在有 3 個哨兵,quorum 配置的是 2,那么一個哨兵需要 2 張贊成票,就可以標(biāo)記主節(jié)點為“客觀下線”了。這 2 張贊成票包括哨兵自己的一張贊成票和另外兩個哨兵的贊成票。
PS:quorum 的值一般設(shè)置為哨兵個數(shù)的二分之一加1,例如 3 個哨兵就設(shè)置 2。
哨兵判斷完主節(jié)點客觀下線后,哨兵就要開始在多個「從節(jié)點」中,選出一個從節(jié)點來做新主節(jié)點。
如何選新主節(jié)點?
那么多「從節(jié)點」,到底選擇哪個從節(jié)點作為新主節(jié)點的?
隨機的方式好嗎?隨機的方式,實現(xiàn)起來很簡單,但是如果選到一個網(wǎng)絡(luò)狀態(tài)不好的從節(jié)點作為新主節(jié)點,那么可能在將來不久又要做一次主從故障遷移。
所以,我們首先要把網(wǎng)絡(luò)狀態(tài)不好的從節(jié)點給過濾掉。首先把已經(jīng)下線的從節(jié)點過濾掉,然后把以往網(wǎng)絡(luò)連接狀態(tài)不好的從節(jié)點也給過濾掉。
怎么判斷從節(jié)點之前的網(wǎng)絡(luò)連接狀態(tài)不好呢?
Redis 有個叫 down-after-milliseconds * 10 配置項,其down-after-milliseconds 是主從節(jié)點斷連的最大連接超時時間。如果在 down-after-milliseconds 毫秒內(nèi),主從節(jié)點都沒有通過網(wǎng)絡(luò)聯(lián)系上,我們就可以認為主從節(jié)點斷連了。如果發(fā)生斷連的次數(shù)超過了 10 次,就說明這個從節(jié)點的網(wǎng)絡(luò)狀況不好,不適合作為新主節(jié)點。
至此,我們就把網(wǎng)絡(luò)狀態(tài)不好的從節(jié)點過濾掉了,接下來要對所有從節(jié)點進行三輪考察:優(yōu)先級、復(fù)制進度、ID 號。在進行每一輪考察的時候,哪個從節(jié)點優(yōu)先勝出,就選擇其作為新主節(jié)點。
- 第一輪考察:哨兵首先會根據(jù)從節(jié)點的優(yōu)先級來進行排序,優(yōu)先級越小排名越靠前,
- 第二輪考察:如果優(yōu)先級相同,則查看復(fù)制的下標(biāo),哪個從「主節(jié)點」接收的復(fù)制數(shù)據(jù)多,哪個就靠前。
- 第三輪考察:如果優(yōu)先級和下標(biāo)都相同,就選擇從節(jié)點 ID 較小的那個。
第一輪考察:優(yōu)先級最高的從節(jié)點勝出
Redis 有個叫 slave-priority 配置項,可以給從節(jié)點設(shè)置優(yōu)先級。
每一臺從節(jié)點的服務(wù)器配置不一定是相同的,我們可以根據(jù)服務(wù)器性能配置來設(shè)置從節(jié)點的優(yōu)先級。
比如,如果 「 A 從節(jié)點」的物理內(nèi)存是所有從節(jié)點中最大的, 那么我們可以把「 A 從節(jié)點」的優(yōu)先級設(shè)置成最高。這樣當(dāng)哨兵進行第一輪考慮的時候,優(yōu)先級最高的 A 從節(jié)點就會優(yōu)先勝出,于是就會成為新主節(jié)點。
第二輪考察:復(fù)制進度最靠前的從節(jié)點勝出
如果在第一輪考察中,發(fā)現(xiàn)優(yōu)先級最高的從節(jié)點有兩個,那么就會進行第二輪考察,比較兩個從節(jié)點哪個復(fù)制進度。
什么是復(fù)制進度?主從架構(gòu)中,主節(jié)點會將寫操作同步給從節(jié)點,在這個過程中,主節(jié)點會用 master_repl_offset 記錄當(dāng)前的最新寫操作在 repl_backlog_buffer 中的位置,而從節(jié)點會用 slave_repl_offset 這個值記錄當(dāng)前的復(fù)制進度。
如果某個從節(jié)點的 slave_repl_offset 最接近 master_repl_offset,說明它的復(fù)制進度是最靠前的,于是就可以將它選為新主節(jié)點。
第三輪考察:ID 號小的從節(jié)點勝出
如果在第二輪考察中,發(fā)現(xiàn)有兩個從節(jié)點優(yōu)先級和復(fù)制進度都是一樣的,那么就會進行第三輪考察,比較兩個從節(jié)點的 ID 號,ID 號小的從節(jié)點勝出。
什么是 ID 號?每個從節(jié)點都有一個編號,這個編號就是 ID 號,是用來唯一標(biāo)識從節(jié)點的。
到這里,選主的事情終于結(jié)束了。簡單給大家總結(jié)下:
- 過濾掉已經(jīng)離線的從節(jié)點;
- 過濾掉歷史網(wǎng)絡(luò)連接狀態(tài)不好的從節(jié)點;
- 將剩下的從節(jié)點,進行三輪考察:優(yōu)先級、復(fù)制進度、ID 號。在每一輪考察過程中,如果找到了一個勝出的從節(jié)點,就將其作為新主節(jié)點。
由哪個哨兵進行主從故障轉(zhuǎn)移?
前面說過,為了更加“客觀”的判斷主節(jié)點故障了,一般不會只由單個哨兵的檢測結(jié)果來判斷,而是多個哨兵一起判斷,這樣可以減少誤判概率,所以哨兵是以哨兵集群的方式存在的。
那在選定了即將作為主節(jié)點的從節(jié)后,由哨兵集群中的哪個節(jié)點進行主從故障轉(zhuǎn)移呢?
所以這時候,還需要在哨兵集群中選出一個 leeder,讓 Leader 來執(zhí)行主從切換。
選舉 leeder 的過程其實是一個投票的過程,在投票開始前,肯定得有個「候選者」。
那誰來作為候選者呢?
哪個哨兵節(jié)點判斷主節(jié)點為「客觀下線」,這個哨兵節(jié)點就是候選者,所謂的候選者就是想當(dāng) Leader 的哨兵。
舉個例子,假設(shè)有三個哨兵。當(dāng)哨兵 A 先判斷到主節(jié)點「主觀下線后」,就會給其他實例發(fā)送 is-master-down-by-addr 命令。接著,其他哨兵會根據(jù)自己和主節(jié)點的網(wǎng)絡(luò)連接情況,做出贊成投票或者拒絕投票的響應(yīng)。
當(dāng)哨兵 A 收到贊成票數(shù)達到哨兵配置文件中的 quorum 配置項設(shè)定的值后,就會將主節(jié)點標(biāo)記為「客觀下線」,此時的哨兵 A 就是一個Leader 候選者。
候選者如何選舉成為 Leader?
候選者會向其他哨兵發(fā)送命令,表明希望成為 Leader 來執(zhí)行主從切換,并讓所有其他哨兵對它進行投票。
每個哨兵只有一次投票機會,如果用完后就不能參與投票了,可以投給自己或投給別人,但是只有候選者才能把票投給自己。
那么在投票過程中,任何一個「候選者」,要滿足兩個條件:
- 第一,拿到半數(shù)以上的贊成票;
- 第二,拿到的票數(shù)同時還需要大于等于哨兵配置文件中的 quorum 值。
舉個例子,假設(shè)哨兵節(jié)點有 3 個,quorum 設(shè)置為 2,那么任何一個想成為 Leader 的哨兵只要拿到 2 張贊成票,就可以選舉成功了。如果沒有滿足條件,就需要重新進行選舉。
這時候有的同學(xué)就會問了,如果某個時間點,剛好有兩個哨兵節(jié)點判斷到主節(jié)點為客觀下線,那這時不就有兩個候選者了?這時該如何決定誰是 Leader 呢?
每位候選者都會先給自己投一票,然后向其他哨兵發(fā)起投票請求。如果投票者先收到「候選者 A」的投票請求,就會先投票給它,如果投票者用完投票機會后,收到「候選者 B」的投票請求后,就會拒絕投票。這時,候選者 A 先滿足了上面的那兩個條件,所以「候選者 A」就會被選舉為 Leader。
為什么哨兵節(jié)點至少要有 3 個?
如果哨兵集群中只有 2 個哨兵節(jié)點,此時如果一個哨兵想要成功成為 Leader,必須獲得 2 票,而不是 1 票。
所以,如果哨兵集群中有個哨兵掛掉了,那么就只剩一個哨兵了,如果這個哨兵想要成為 Leader,這時票數(shù)就沒辦法達到 2 票,就無法成功成為 Leader,這時是無法進行主從節(jié)點切換的。
因此,通常我們至少會配置 3 個哨兵節(jié)點。這時,如果哨兵集群中有個哨兵掛掉了,那么還剩下兩個個哨兵,如果這個哨兵想要成為 Leader,這時還是有機會達到 2 票的,所以還是可以選舉成功的,不會導(dǎo)致無法進行主從節(jié)點切換。
當(dāng)然,你要問,如果 3 個哨兵節(jié)點,掛了 2 個怎么辦?這個時候得人為介入了,或者增加多一點哨兵節(jié)點。
再說一個問題,Redis 1 主 4 從,5 個哨兵 ,quorum 設(shè)置為 3,如果 2 個哨兵故障,當(dāng)主節(jié)點宕機時,哨兵能否判斷主節(jié)點“客觀下線”?能否自動切換?
- 哨兵集群可以判定主節(jié)點“客觀下線”。哨兵集群還剩下 3 個哨兵,當(dāng)一個哨兵判斷主節(jié)點“主觀下線”后,詢問另外 2 個哨兵后,有可能能拿到 3 張贊同票,這時就達到了 quorum 的值,因此,哨兵集群可以判定主節(jié)點為“客觀下線”。
- 哨兵集群可以完成主從切換。當(dāng)有個哨兵標(biāo)記主節(jié)點為「客觀下線」后,就會進行選舉 Leader 的過程,因為此時哨兵集群還剩下 3 個哨兵,那么還是可以拿到半數(shù)以上(5/2+1=3)的票,而且也達到了 quorum 值,滿足了選舉 Leader 的兩個條件, 所以就能選舉成功,因此哨兵集群可以完成主從切換。
如果 quorum 設(shè)置為 2 的話,并且有 3 個哨兵故障。此時哨兵集群還是可以判定主節(jié)點為“客觀下線”,但是哨兵不能完成主從切換了,大家可以自己推演下。
quorum 的值建議設(shè)置為哨兵個數(shù)的二分之一加1,例如 3 個哨兵就設(shè)置 2,5 個哨兵設(shè)置為 3,而且哨兵節(jié)點的數(shù)量應(yīng)該是奇數(shù)。
如何通知客戶端新主節(jié)點的信息?
經(jīng)過前面一系列的操作后,哨兵集群終于完成了主從故障遷移,那么新主節(jié)點的信息要如何通知給客戶端呢?
這主要通過 Redis 的發(fā)布者/訂閱者機制來實現(xiàn)的。每個哨兵節(jié)點提供發(fā)布者/訂閱者機制,客戶端可以從哨兵訂閱消息。
比如,客戶端訂閱了主從切換的事件,當(dāng)哨兵把新主節(jié)點選擇出來后,就會發(fā)布新主節(jié)點的 IP 地址和端口信息,這個時候客戶端就可以收到這條信息,然后用這里面的新主節(jié)點的 IP 地址和端口進行通信了。
哨兵集群是如何組成的?
前面提到了 Redis 的發(fā)布者/訂閱者機制,那就不得不提一下哨兵集群的組成方式,因為它也用到了這個技術(shù)。
在我第一次搭建哨兵集群的時候,當(dāng)時覺得很詫異。因為在配置哨兵的信息時,竟然只需要填下面這幾個參數(shù),設(shè)置主節(jié)點名字、主節(jié)點的 IP 地址和端口號以及 quorum 值。
sentinel monitor <master-name> <ip> <redis-port> <quorum>
不需要填其他哨兵節(jié)點的信息,我就好奇它們是如何感知對方的,又是如何組成哨兵集群的?
后面才了解到,哨兵節(jié)點之間是通過 Redis 的發(fā)布者/訂閱者機制來相互發(fā)現(xiàn)的。
在主從集群中,主節(jié)點上有一個名為__sentinel__:hello的頻道,不同哨兵就是通過它來相互發(fā)現(xiàn),實現(xiàn)互相通信的。
在下圖中,哨兵 A 把自己的 IP 地址和端口的信息發(fā)布到__sentinel__:hello 頻道上,哨兵 B 和 C 訂閱了該頻道。那么此時,哨兵 B 和 C 就可以從這個頻道直接獲取哨兵 A 的 IP 地址和端口號。然后,哨兵 B、C 可以和哨兵 A 建立網(wǎng)絡(luò)連接。
通過這個方式,哨兵 B 和 C 也可以建立網(wǎng)絡(luò)連接,這樣一來,哨兵集群就形成了。
哨兵集群會對「從節(jié)點」的運行狀態(tài)進行監(jiān)控,那哨兵集群如何知道「從節(jié)點」的信息?
主節(jié)點知道所有「從節(jié)點」的信息,所以哨兵會向主節(jié)點發(fā)送 INFO 命令來獲取所有「從節(jié)點」的信息。
如下圖所示,哨兵 B 給主節(jié)點發(fā)送 INFO 命令,主節(jié)點接受到這個命令后,就會把從節(jié)點列表返回給哨兵。接著,哨兵就可以根據(jù)從節(jié)點列表中的連接信息,和每個從節(jié)點建立連接,并在這個連接上持續(xù)地對從節(jié)點進行監(jiān)控。哨兵 A 和 C 可以通過相同的方法和從節(jié)點建立連接。
正式通過 Redis 的發(fā)布者/訂閱者機制,哨兵之間可以相互感知,然后組成集群,同時,哨兵又通過 INFO 命令,在主節(jié)點里獲得了所有從節(jié)點連接信息,于是就能和從節(jié)點建立連接,并進行監(jiān)控了。
參考資料:
- 《Redis 核心技術(shù)與實戰(zhàn)》
- 《Redis 設(shè)計與實現(xiàn)》
總結(jié)
Redis 在 2.8 版本以后提供的哨兵(Sentinel)機制,它的作用是實現(xiàn)主從故障自動轉(zhuǎn)移。它會監(jiān)測主節(jié)點是否存活,如果發(fā)現(xiàn)主節(jié)點掛了,它就會選舉一個從節(jié)點切換為主節(jié)點,并且把新主節(jié)點的相關(guān)信息通知給從節(jié)點和客戶端。
哨兵一般是以集群的方式部署,至少需要 3 個哨兵節(jié)點,哨兵集群主要負責(zé)三件事情:監(jiān)控、選主、通知。
哨兵節(jié)點通過 Redis 的發(fā)布者/訂閱者機制,哨兵之間可以相互感知,相互連接,然后組成哨兵集群,同時哨兵又通過 INFO 命令,在主節(jié)點里獲得了所有從節(jié)點連接信息,于是就能和從節(jié)點建立連接,并進行監(jiān)控了。
哨兵集群會通過投票的方式判定主節(jié)點是否「客觀下線」,如果判定主節(jié)點為客觀下線,那么就會從所有的「從節(jié)點」中選擇一個作為新主節(jié)點,選擇的規(guī)則有以下步驟:
- 過濾掉已經(jīng)離線的從節(jié)點;
- 過濾掉歷史網(wǎng)絡(luò)連接狀態(tài)不好的從節(jié)點;
- 將剩下的從節(jié)點,進行三輪考察:優(yōu)先級、復(fù)制進度、ID 號。在每一輪考察過程中,如果找到了一個勝出的從節(jié)點,就將其作為新主節(jié)點。
選擇好從節(jié)點后,就需要從哨兵集群選擇一個 leader 執(zhí)行主從切換。選舉 leader 的過程,也是一個投票的過程,任何一個想成為 leader 的哨兵節(jié)點,要滿足兩個條件:
- 第一,拿到半數(shù)以上的贊成票;
- 第二,拿到的票數(shù)同時還需要大于等于哨兵配置文件中的 quorum 值。
選舉完 leader 哨兵節(jié)點后,就執(zhí)行主從切換。完成主從切換后,通過 Redis 的發(fā)布者/訂閱者機制通知客戶端新主節(jié)點的 IP 地址和端口。
原文鏈接:
https://mp.weixin.qq.com/s/HJHNq3MOSJD_C0cPAFz_Iw作者:小林coding