??當(dāng)主機宕機后出現(xiàn)故障無法及時恢復(fù),可以在從機執(zhí)行slave no one
命令使其上位變?yōu)橹鳈C阔蛉,其他主機會自動跟隨阵幸,原主機恢復(fù)后也會變?yōu)閺臋C跟隨新的主機。但這樣的人工操作帶來一個問題坡慌,難道半夜主機宕機還要通知運維起來輸命令嗎黔酥?這顯然不符合常理,而且有個弊端洪橘,即宕機后的主機恢復(fù)后就是孤零零的一個普通服務(wù)器絮爷,主從服務(wù)器中就少了一個服務(wù)器,降低了集體性能梨树。為了自動化管理這個過程坑夯,引入了哨兵機制。
什么是哨兵(Sentinel)抡四?
??簡單來講柜蜈,Sentinel
就是一個特殊的Redis服務(wù)器,負(fù)責(zé)監(jiān)控其他服務(wù)器的在線狀態(tài)指巡。
??復(fù)雜一點來說淑履,Sentinel
(哨兵、哨崗)是Redis的高可用性解決方案:由一個個Sentinel
組成的Sentinel
系統(tǒng)(至少由3個哨兵組成)可以監(jiān)視多個主服務(wù)器以及這些主服務(wù)器屬下的所有從服務(wù)器藻雪,并在被監(jiān)視的主服務(wù)器進(jìn)入下線狀態(tài)時秘噪,自動將下線主服務(wù)器屬下的某個從服務(wù)器升級為新的主服務(wù)器,然后由新的主服務(wù)器代替已下線的主服務(wù)器繼續(xù)處理命令請求勉耀。如下圖:
??因為有哨兵機制指煎,所以當(dāng)主服務(wù)器發(fā)生故障時蹋偏,會發(fā)生下面的過程:
??當(dāng)然其中的往細(xì)了講挺復(fù)雜的,比如新的主服務(wù)器該選哪個從服務(wù)器啊至壤,舊主服務(wù)器怎么變成新主服務(wù)器的從服務(wù)器啊等等各種問題具體實現(xiàn)威始。簡單來說,哨兵建立的過程包括:啟動并初始化哨兵像街、獲取主/從服務(wù)器信息黎棠、檢測主觀/客觀下線狀態(tài)、故障轉(zhuǎn)移工作镰绎。
哨兵建立及后續(xù)分析
啟動并初始化Sentinel
??啟動一個Sentinel
需要使用redis-sentinel
命令:
redis-sentinel /usr/locat/etc/redis-sentinel.conf
??其中redis-sentinel.conf
為Sentinel
的配置信息脓斩,和Redis提供的默認(rèn)redis.conf
文件一樣,Redis也提供了一份默認(rèn)的名為redis-sentinel.conf
的哨兵配置文件畴栖,部分內(nèi)容如下:
port 26379 //端口號
daemonize no //是否守護
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 60000
sentinel failover-timeout mymaster 180000
sentinel parallel-syncs mymaster 1
??sentinel monitor mymaster 127.0.0.1 6379 2
代表哨兵監(jiān)控名為mymaster
的主服務(wù)器俭厚,它位于地址127.0.0.1和端口6379,仲裁人數(shù)為2驶臊,仲裁代表當(dāng)主服務(wù)器掛掉后:
- 如果兩個Sentinels同時同意主服務(wù)器無法訪問挪挤,則其中一個將嘗試啟動故障轉(zhuǎn)移。
- 如果至少總共有三個Sentinel可達(dá)关翎,則故障轉(zhuǎn)移將被授權(quán)并實際開始扛门。
??down-after-milliseconds
:主服務(wù)器主觀下線時間
??failover-timeout
??parallel-syncs
初始化的Sentinel
??因為Sentinel
本質(zhì)上是一個運行在特殊模式的Redis服務(wù)器,所以啟動的第一步纵寝,就是初始化一個普通的Redis服務(wù)器论寨,但它的初始化過程和普通服務(wù)器的初始過程不同,它們功能不同:
使用Sentinel專用代碼
??啟動Sentinel
的第二步就是將一部分Redis服務(wù)器使用的代碼換成Sentinel
的專用代碼爽茴。比如普通服務(wù)器的get
葬凳、set
、setnx
等等命令不再使用室奏,而是使用它專屬的代碼處理其他事情火焰,如sentinel
、subscribe
胧沫、info
等等命令負(fù)責(zé)監(jiān)控相關(guān)的功能昌简。本質(zhì)就是原先某些函數(shù)庫不再使用,而是使用新的函數(shù)庫绒怨,畢竟舊功能咱用不到纯赎,新的功能普通Redis服務(wù)器原來沒有,而Sentinel
就加上南蹂,如下圖:
??這也解釋了為什么Sentinel
為什么不能執(zhí)行原先Redis服務(wù)器里的set
犬金、get
等等命令,因為Sentinel
根本沒有載入這些命令
初始化Sentinel狀態(tài)結(jié)構(gòu)
??在應(yīng)用了Sentinel
的專用代碼之后,接下來晚顷,服務(wù)器會初始化一個叫做哨兵狀態(tài)(sentinelState
)的結(jié)構(gòu)峰伙,保存了服務(wù)器中所有和Sentinel
功能相關(guān)的狀態(tài)。
創(chuàng)建與主服務(wù)器的網(wǎng)絡(luò)連接
??初始Sentinel
的最后一步是創(chuàng)建與被監(jiān)視主服務(wù)器的網(wǎng)絡(luò)連接音同,Sentinel
將成為主服務(wù)器的客戶端词爬,它可以向主服務(wù)器發(fā)送命令秃嗜,并從命令回復(fù)中回去相關(guān)的信息权均。
??對于每個被Sentinel
監(jiān)視的主服務(wù)器,Sentinel
都會創(chuàng)建2個與主服務(wù)器的異步網(wǎng)絡(luò)連接:
- 命令連接:專門用于向主服務(wù)器發(fā)送命令锅锨,并接受回復(fù)信息
-
訂閱連接:專門用于訂閱主服務(wù)器的
_sentinel_:hello
頻道
image.png
獲取主/從服務(wù)器信息
??如上圖叽赊,對主服務(wù)器:Sentinel
默認(rèn)會以每十秒一次的頻率,通過命令連接向被監(jiān)視的主服務(wù)器發(fā)送info
命令來獲取主服務(wù)器的當(dāng)前信息(包括主服務(wù)器本身的信息必搞,及其屬下所有從服務(wù)器的信息)必指,根據(jù)這些信息來更新主服務(wù)器的實例結(jié)構(gòu)(若主服務(wù)器重啟的ID將和實例結(jié)構(gòu)不一致,將其更新)及從服務(wù)器的實例結(jié)構(gòu)恕洲。
??如上圖塔橡,對從服務(wù)器:當(dāng)Sentinel
發(fā)現(xiàn)主服務(wù)器有新的從服務(wù)器出現(xiàn)時,其出了會為新的從服務(wù)器創(chuàng)建對應(yīng)的實例結(jié)構(gòu)之外霜第,還會創(chuàng)建連接到從服務(wù)器的命令連接和訂閱連接葛家。
??在創(chuàng)建命令連接,Sentinel
在默認(rèn)情況下泌类,以每十秒一次的頻率通過命令連接向從服務(wù)器發(fā)送info
命令癞谒,獲取其信息,并對從服務(wù)器的實例結(jié)構(gòu)進(jìn)行更新刃榨。
向主/從服務(wù)器發(fā)送信息到頻道
??默認(rèn)情況下弹砚,Sentinel
會以每兩秒一次的頻率,通過命令向所有被監(jiān)控的主和從服務(wù)器發(fā)送以下格式的命令:
??這個命令包含的信息包括
Sentinel
本身的信息枢希,也包括主服務(wù)器的信息桌吃。舉個例子,一個Sentinel
通過publish
命令向主服務(wù)器發(fā)送的信息實例:
"127.0.0.1,26379,e955b4c85598ef5b5f055bc7ebfd5e828dbed4fa,0,mymaster,127.0.0.1,6379,0"
??這些信息包含:
-
Sentinel
的IP地址為127.0.0.1苞轿,端口號為26379读存,運行ID為e955b4c85598ef5b5f055bc7ebfd5e828dbed4fa
,當(dāng)前配置紀(jì)元為0呕屎; - 主服務(wù)器的名字為
mymaster
让簿,IP地址為127.0.0.1,端口號為6379秀睛,當(dāng)前的配置紀(jì)元為0
接受主/從服務(wù)器的頻道信息
??當(dāng)Sentinel
與一個主服務(wù)器或從服務(wù)器建立起訂閱連接之后尔当,Sentinel
就會通過訂閱連接,向服務(wù)器發(fā)送以下命令:
SUBSCRIBE _sentinel_:hello
??Sentinel
對_sentinel_:hello
頻道的訂閱會一致持續(xù)到Sentinel
與服務(wù)器的連接斷開為止。
??這也就是說椭迎,對于每個與Sentinel
連接的服務(wù)器锐帜,Sentinel
既通過命令連接向服務(wù)器的_sentinel_:hello
頻道發(fā)送信息,又通過訂閱連接從
服務(wù)器的_sentinel_:hello
頻道接受信息畜号,如下圖:
??對于監(jiān)視同一個服務(wù)器的多個Sentinel
來說缴阎,一個Sentinel
發(fā)送的信息會被其他Sentinel
接收到,這些信息會被其他Sentinel
用于更新對發(fā)送信息的Sentinel
的認(rèn)知简软,也會被其他Sentinel
用于更新對被監(jiān)視服務(wù)器的認(rèn)知蛮拔,如下圖:
??當(dāng)一個
Sentinel
從_sentinel_:hello
頻道接收到一條信息時,會對該信息進(jìn)行分析(提取信息中的Sentinel
IP痹升、port建炫、run ID等等信息),并進(jìn)行下列檢查:
- 如果信息中記錄的
Sentinel run ID
和接受信息的Sentinel run ID
相同疼蛾,說明是自己發(fā)送的肛跌,將丟棄這條信息,不做處理 - 如果兩者run ID不同察郁,說明是其他
Sentinel
發(fā)送的衍慎,將根據(jù)信息中的各個參數(shù),Sentinel
將對自身創(chuàng)建的主服務(wù)器的實例結(jié)構(gòu)進(jìn)行更新
更新sentinels字典
??Sentinel
為主服務(wù)器創(chuàng)建的實例結(jié)構(gòu)中的sentinels
字典除了保存Sentinel
本身之外皮钠,還保存了所有同樣監(jiān)視這個主服務(wù)器的其他Sentinel
資料(sentinels
字典的鍵為Sentinel
名字稳捆,值為對應(yīng)的Sentinel
的實例結(jié)構(gòu))
??當(dāng)一個Sentinel
(目標(biāo)Sentinel
)接收到其他Sentinel
(源Sentinel
)發(fā)來的信息時,目標(biāo)Sentinel
會從信息分析并提取相關(guān)參數(shù)(源Sentinel
相關(guān)參數(shù)如IP鳞芙、端口號眷柔、run ID和配置紀(jì)元,被監(jiān)視主服務(wù)器參數(shù)如名字原朝、IP驯嘱、端口號和配置紀(jì)元),對自身的實例結(jié)構(gòu)進(jìn)行更新
創(chuàng)建連向其他Sentinel的命令連接
??當(dāng)Sentinel
通過頻道信息發(fā)現(xiàn)一個新的Sentinel
時喳坠,它不僅會為新Sentinel
在sentinels
字典中創(chuàng)建相應(yīng)的實例結(jié)構(gòu)鞠评,還會創(chuàng)建一個連接向新Sentinel
的命令連接,而新Sentinel
也會創(chuàng)建與這個Sentinel
的命令連接壕鹉,最后監(jiān)視同一主服務(wù)器的多個Sentinel
形成一個相互連接的網(wǎng)絡(luò)剃幌,如下圖:
注:``Sentinel
在連接主服務(wù)器或這從服務(wù)器時,會同時創(chuàng)建命令連接和訂閱連接晾浴,但連接其他Sentinel
時负乡,只創(chuàng)建命令連接。因為Sentinel
需要通過接受主服務(wù)器或從服務(wù)器發(fā)來的頻道信息來發(fā)現(xiàn)未知的新Sentinel
脊凰,所以需要建立訂閱連接抖棘,而相互已知的Sentinel
只要使用命令連接來進(jìn)行通信就足夠了。
檢測主觀/客觀下線狀態(tài)
??在默認(rèn)情況下,Sentinel
會以每秒一次的頻率向所有與它創(chuàng)建了命令連接的實例(包括所有主/從服務(wù)器切省,所有其他Sentinel
)發(fā)送ping
命令最岗,并通過實例返回的有效或無效回復(fù)來判斷實例是否在線。
什么是主觀下線狀態(tài)朝捆?
??前面配置文件中的down-after-milliseconds
選項指定了Sentinel
判斷實例進(jìn)入主觀下線的時間長短般渡,如果在該時間內(nèi)實例連續(xù)向Sentinel
發(fā)送無效回復(fù),則Sentinel
會修改這個實例的實例結(jié)構(gòu)芙盘,來表示這個實例已經(jīng)進(jìn)入主觀下線狀態(tài)驯用。
??舉個例子,如果配置文件指定的down-after-milliseconds
的選項值為50000
毫秒何陆,那么當(dāng)主服務(wù)器連續(xù)50000
毫秒都返回給Sentinel
無效回復(fù)是晨汹,Sentinel
就會將主服務(wù)器實例結(jié)構(gòu)修改并標(biāo)記為主觀下線豹储。
??而且配置文件指定的down-after-milliseconds
選項也會被Sentinel
用來判斷主服務(wù)器下所有從服務(wù)器的主觀下線限時時間標(biāo)準(zhǔn)贷盲。
??哦,對了剥扣,因為基本不可能只有一個Sentinel
巩剖,所以其他Sentinel
也會執(zhí)行上面的判斷,只不過判斷的down-after-milliseconds
時間可能不同钠怯,因為配置文件設(shè)置的不同佳魔。所以可能會有這個Sentinel
判斷主服務(wù)器50000
毫秒就主觀下線了,而另一個Sentinel
卻判斷主服務(wù)器是70000
秒才主觀下線的情況發(fā)生晦炊。
什么是客觀下線狀態(tài)鞠鲜?
??當(dāng)Sentinel
將一個主服務(wù)器判斷為主觀下線之后,為了確認(rèn)這個主服務(wù)器是否真的下線了断国,它會向同樣監(jiān)視這一主服務(wù)器的其他Sentinel
進(jìn)行詢問贤姆,看它們是否也認(rèn)為主服務(wù)器已經(jīng)進(jìn)入下線狀態(tài)(可以是主觀下線或客觀下線)。當(dāng)Sentinel
從其他Sentinel
那里接收到足夠數(shù)量(總數(shù)為設(shè)置的仲裁值)的已下線判斷之后稳衬,Sentinel
就會將服務(wù)器判斷為客觀下線霞捡,并對主服務(wù)器執(zhí)行故障轉(zhuǎn)移操作。
選舉領(lǐng)頭Sentinel
??當(dāng)一個主服務(wù)器被判斷為客觀下線時薄疚,監(jiān)視這個現(xiàn)象主服務(wù)器的各個Sentinel
會進(jìn)行協(xié)商碧信,選舉出一個領(lǐng)頭Sentinel
,并由領(lǐng)頭Sentinel
對下線主服務(wù)器執(zhí)行故障轉(zhuǎn)移工作街夭,理所當(dāng)然選舉會基于一定規(guī)則砰碴,此處不介紹。
故障轉(zhuǎn)移
??在選舉出領(lǐng)頭Sentinel
之后板丽,其將對已下線的主服務(wù)器執(zhí)行故障轉(zhuǎn)移操作呈枉,包含3個步驟:
1):在已下線的主服務(wù)器下的所有從服務(wù)器基于一定挑選一個將其轉(zhuǎn)換為主服務(wù)器(根據(jù)從服務(wù)器權(quán)值大小、復(fù)制偏移量大小等規(guī)則挑選);
2):讓已下線主服務(wù)器下的所有從服務(wù)器改為復(fù)制新的主服務(wù)器碴卧;
3):將已下線主服務(wù)器設(shè)置為新的主服務(wù)器的從服務(wù)器弱卡,當(dāng)這個舊主服務(wù)器上線時就會變成新的主服務(wù)器的從服務(wù)器
??具體過程如下圖:
在Redis服務(wù)器中使用哨兵
??哨兵使用前提:至少需要3個Redis服務(wù)器(一主二仆)。哨兵的使用有以下步驟:
- 創(chuàng)建哨兵配置文件
cd /usr/local/etc/master-slave
touch redis-sentinel1.conf
- 配置哨兵配置文件
redis-sentinel.conf1
(一個例子所以簡略配置),加入:
sentinel monitor mymaster 127.0.0.1 6380 1
- 啟動哨兵:
redis-sentinel /usr/local/etc/master-slave /redis-sentinel1.conf
啟動界面如下:
31682:X 12 Apr 2019 15:19:27.937 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
31682:X 12 Apr 2019 15:19:27.937 # Redis version=5.0.4, bits=64, commit=00000000, modified=0, pid=31682, just started
31682:X 12 Apr 2019 15:19:27.937 # Configuration loaded
31682:X 12 Apr 2019 15:19:27.939 * Increased maximum number of open files to 10032 (it was originally set to 256).
_._
_.-``__ ''-._
_.-`` `. `_. ''-._ Redis 5.0.4 (00000000/0) 64 bit
.-`` .-```. ```\/ _.,_ ''-._
( ' , .-` | `, ) Running in sentinel mode
|`-._`-...-` __...-.``-._|'` _.-'| Port: 26379
| `-._ `._ / _.-' | PID: 31682
`-._ `-._ `-./ _.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' | http://redis.io
`-._ `-._`-.__.-'_.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' |
`-._ `-._`-.__.-'_.-' _.-'
`-._ `-.__.-' _.-'
`-._ _.-'
`-.__.-'
31682:X 12 Apr 2019 15:19:27.940 # Sentinel ID is 6712a29afc6e6e776bb6aaa661d087ceb8170e89
31682:X 12 Apr 2019 15:19:27.940 # +monitor master mymaster 127.0.0.1 6380 quorum 1
31682:X 12 Apr 2019 15:19:27.940 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6380
31682:X 12 Apr 2019 15:19:27.941 * +slave slave 127.0.0.1:6382 127.0.0.1 6382 @ mymaster 127.0.0.1 6380
??很顯然住册,哨兵監(jiān)控了主服務(wù)器婶博,并知道了其從服務(wù)器信息,當(dāng)主機宕機后(模擬主機關(guān)閉):
127.0.0.1:6380> shutdown
??過了一段時間后荧飞,哨兵日志有如下內(nèi)容:
31682:X 12 Apr 2019 15:21:14.993 # +sdown master mymaster 127.0.0.1 6380
31682:X 12 Apr 2019 15:21:14.993 # +odown master mymaster 127.0.0.1 6380 #quorum 1/1
31682:X 12 Apr 2019 15:21:14.993 # +new-epoch 1
31682:X 12 Apr 2019 15:21:14.993 # +try-failover master mymaster 127.0.0.1 6380
31682:X 12 Apr 2019 15:21:14.996 # +vote-for-leader 6712a29afc6e6e776bb6aaa661d087ceb8170e89 1
31682:X 12 Apr 2019 15:21:14.996 # +elected-leader master mymaster 127.0.0.1 6380
31682:X 12 Apr 2019 15:21:14.996 # +failover-state-select-slave master mymaster 127.0.0.1 6380
31682:X 12 Apr 2019 15:21:15.064 # +selected-slave slave 127.0.0.1:6382 127.0.0.1 6382 @ mymaster 127.0.0.1 6380
31682:X 12 Apr 2019 15:21:15.064 * +failover-state-send-slaveof-noone slave 127.0.0.1:6382 127.0.0.1 6382 @ mymaster 127.0.0.1 6380
31682:X 12 Apr 2019 15:21:15.130 * +failover-state-wait-promotion slave 127.0.0.1:6382 127.0.0.1 6382 @ mymaster 127.0.0.1 6380
31682:X 12 Apr 2019 15:21:15.566 # +promoted-slave slave 127.0.0.1:6382 127.0.0.1 6382 @ mymaster 127.0.0.1 6380
31682:X 12 Apr 2019 15:21:15.566 # +failover-state-reconf-slaves master mymaster 127.0.0.1 6380
31682:X 12 Apr 2019 15:21:15.638 * +slave-reconf-sent slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6380
31682:X 12 Apr 2019 15:21:16.588 * +slave-reconf-inprog slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6380
31682:X 12 Apr 2019 15:21:16.588 * +slave-reconf-done slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6380
31682:X 12 Apr 2019 15:21:16.692 # +failover-end master mymaster 127.0.0.1 6380
31682:X 12 Apr 2019 15:21:16.692 # +switch-master mymaster 127.0.0.1 6380 127.0.0.1 6382
31682:X 12 Apr 2019 15:21:16.692 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6382
31682:X 12 Apr 2019 15:21:16.692 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6382
31682:X 12 Apr 2019 15:21:46.776 # +sdown slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6382
??可以看到凡人,哨兵將本地6382端口的從機升級為了主機:
127.0.0.1:6382> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=6381,state=online,offset=25036,lag=1
master_replid:9a87c8ae6ea203b87991d4edb3584b3f83e73439
master_replid2:a5183310e466a5a9e7d2c465a1eea051575ab130
master_repl_offset:25036
second_repl_offset:24482
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:25036
??而6381變?yōu)榱似鋸臋C:
127.0.0.1:6381> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6382
master_link_status:up
master_last_io_seconds_ago:0
master_sync_in_progress:0
slave_repl_offset:25316
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:9a87c8ae6ea203b87991d4edb3584b3f83e73439
master_replid2:a5183310e466a5a9e7d2c465a1eea051575ab130
master_repl_offset:25316
second_repl_offset:24482
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:25316
??當(dāng)舊主機重新上線后,變?yōu)?382的從機:
127.0.0.1:6380> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6382
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_repl_offset:43259
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:9a87c8ae6ea203b87991d4edb3584b3f83e73439
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:43259
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:43104
repl_backlog_histlen:156
參考資料
《redis設(shè)計與實現(xiàn)》(第二版)