一文詳解redis高可用Sentinel?
寫在前面
redis 在我們?nèi)粘5臉I(yè)務(wù)開發(fā)中是十分常見的,而redis的可用性就必須要有很高的要求,那么 redis集群的高可用由有一個或者多個 Sentinel(哨兵)
實(shí)例組成的 哨兵系統(tǒng)來保證的。
哨兵
由一個或者多個 Sentinel 實(shí)例組成的 Sentinel 系統(tǒng)可以監(jiān)控任意多個主服務(wù)器,以及這些主服務(wù)器屬下的所有從服務(wù)器,并在被監(jiān)視的主服務(wù)器進(jìn)入下線狀態(tài)時(shí),自動將下線主服務(wù)器屬下的某個從服務(wù)器升級為新主服務(wù)器,然后有新的主服務(wù)器代替已下線的主服務(wù)器繼續(xù)處理命令請求。
簡介
Sentinel 本質(zhì)上只是一個運(yùn)行在特殊模式下的Redis服務(wù)器,但是 Sentinel 和 Redis的初始化和工作內(nèi)容是不同的。Sentinel 不需要使用數(shù)據(jù)庫,所以初始化的時(shí)候是不需要載入RDB文件或者AOF文件的。而Sentinel的工作內(nèi)容如下:
功能 | 使用情況 |
---|---|
數(shù)據(jù)庫鍵值對命令 SET、DEL、FLUSHDB | 不使用 |
事務(wù)命令 MULTI,WATCH | 不使用 |
腳本命令 EVAL | 不使用 |
RDB/AOF持久化命令 SAVE、BGSAVE、BGREWRITEAOF | 不使用 |
復(fù)制命令,SLAVEOF | Sentinel 內(nèi)部使用,但是客戶端不用 |
發(fā)布與訂閱命令,比如 PUBLISH 和 SUBSCRIBE | SUBSCRIBE、PSUBSCRIBE、UNSUBSCRIBE、PUBSUBSCRIBE這四個命令在Sentinel內(nèi)部和客戶端都可以使用,但PUBLISH命令只能在Sentinel內(nèi)部使用 |
文件事件處理器(負(fù)責(zé)發(fā)送命令和請求、處理命令回復(fù)) | Sentinel 內(nèi)部使用,但關(guān)聯(lián)的文件事件處理器和普通Redis處理器不同 |
事件處理器(負(fù)責(zé)執(zhí)行serverCron 函數(shù)) | Sentinel 內(nèi)部使用,時(shí)間事件的處理器仍然是ServerCron函數(shù),ServerCron函數(shù)會調(diào)用sentinel.c/sentineTimer函數(shù),后者包含了Sentinel要執(zhí)行的所有操作 |
在為什么啟動Sentinel的時(shí)候,會有這些限制呢?Sentinel 不都是 Redis 嗎?
因?yàn)樵赟entinel初始化的時(shí)候,加載的是
src/sentinel.c
文件的函數(shù),而Redis加載的是src/redis.c
文件的函數(shù),而這兩個文件的初始化函數(shù),限定了只能使用哪些命令
數(shù)據(jù)結(jié)構(gòu)
Sentinel 的結(jié)構(gòu)體如下
typedef struct sentinelRedisInstance { int flags; // 標(biāo)識,記錄實(shí)例類型 char *name; // 該實(shí)例名字 char *runid; // 實(shí)例的運(yùn)行id uint64_t config_epoch; // 配置紀(jì)元,用于實(shí)現(xiàn)故障轉(zhuǎn)移 sentinelAddr *addr; // 實(shí)例地址 mstime_t last_pub_time; // 上次我們通過 Pub/Sub 發(fā)送了 hello 的時(shí)間。 mstime_t last_hello_time; // 僅在設(shè)置 SRI_SENTINEL 時(shí)使用。上次我們發(fā)送hello的響應(yīng)時(shí)間 mstime_t last_master_down_reply_time; // SENTINEL is-master-down command.命令的最新響應(yīng)時(shí)間 // ... mstime_t down_after_period; // 實(shí)例無響應(yīng)多少毫秒之后才會被判斷為主觀下線 // ... // Master 配置 unsigned int quorum;// 判斷這個實(shí)例為客觀下線鎖需要支持的投票數(shù)量 // ... // Slave 配置 int slave_priority; /* Slave 優(yōu)先級 */ 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 復(fù)制的偏移量. */ // ... } sentinelRedisInstance;
創(chuàng)建鏈接
初始化Sentinel的最后一步是創(chuàng)建連向被監(jiān)控主服務(wù)器的網(wǎng)絡(luò)連接,Sentinel 將成為主服務(wù)器的客戶端,可以向主服務(wù)器發(fā)送命令,并且從命令回復(fù)中獲取相關(guān)的信息。
對于每個被Sentinel 監(jiān)視的主服務(wù)器來說,Sentinel會創(chuàng)建兩個連接主服務(wù)器的異步網(wǎng)絡(luò)連接:
- 命令連接,這個連接專門用于向主服務(wù)器發(fā)送命令,并接受命令回復(fù)。
- 訂閱連接,這個連接專門用于訂閱主服務(wù)器的
__sentinel__:hello
頻道。
為什么會有兩個連接?
在Redis目前的發(fā)布與訂閱功能中,被i發(fā)送的信息都不會保存在Redis服務(wù)器里面,如果在信息發(fā)送時(shí),想要接受信息的客戶端不在線或者斷線,那么這個客戶端就會丟失這條信息。 因此為了不丟失__sentinel__:hello 頻道的任何信息,Sentinel必須專門用一個連接來接受該頻道的信息。除了訂閱頻道之外,Sentinel還必須向主服務(wù)器發(fā)送命令,以此來與主服務(wù)器進(jìn)行通信,所以Sentinel還必須向主服務(wù)器創(chuàng)建命令連接。
獲取信息 INFO
Sentinel 默認(rèn)會以每十秒一次的頻率,通過命令連接向被監(jiān)視的主服務(wù)器發(fā)送 INFO命令
,并通過分析 INFO 命令的回復(fù)
來獲取主服務(wù)器的當(dāng)前信息。
一般INFO命令的回復(fù)有以下信息:
- 從服務(wù)器的運(yùn)行ID、角色role、優(yōu)先級slave_priority、復(fù)制偏移量
- 主服務(wù)器的IP地址 master_host 以及主服務(wù)器的端口號 master_port
- 主從服務(wù)器的連接狀態(tài)master_link_status
獲取到這些信息之后,就會更新存儲到Sentinel的結(jié)構(gòu)體中。
但是當(dāng)主服務(wù)器處于下線狀態(tài),或者Sentinel正在對主服務(wù)器和從服務(wù)器進(jìn)行故障轉(zhuǎn)移操作時(shí),Sentinel 向從服務(wù)器發(fā)送INFO命令的頻率將會變成每秒一次。
發(fā)送命令
對于監(jiān)視同一個主服務(wù)器和從服務(wù)器的多個Sentinel來說,他們會以每兩秒一次的頻率,通過被監(jiān)視服務(wù)器的 __sentinel:hello__
頻道發(fā)送消息來響應(yīng)其他sentinel宣告自己的存在。
PUBLISH __sentinel__:hello "<s_ip>,<s_port>,<s_runid>,<s_epoch>,<m_name>,<m_ip>,<m_port>,<m_epoch>"
這條命令向服務(wù)器的__sentinel__:hello
頻道發(fā)送一條信息,這些信息的組成如下:
- s_ 開頭的是sentinel本身的信息。
- m_開頭的是主服務(wù)器的信息。如果此sentinel監(jiān)視的是主服務(wù)器,那么這個參數(shù)就是主服務(wù)器的參數(shù),如果監(jiān)視的是從服務(wù)器,那么就是這個從服務(wù)器正在所復(fù)制的主服務(wù)器。
接收命令
當(dāng)sentinel與一個主服務(wù)器或者從服務(wù)器建立起訂閱連接之后,sentinel就會就會通過訂閱連接,向服務(wù)器發(fā)送以下命令:
SUBSCRIBE __sentinel__:hello
也就是說對于每一個sentinel連接的服務(wù)器,sentinel既通過命令連接到服務(wù)器的__sentinel__:hello
頻道發(fā)送信息,又通過訂閱連接服務(wù)器的__sentinel__:hello
頻道接受消息。
那么對于監(jiān)視同一個服務(wù)器的多個sentinel來說,一個sentinel發(fā)送的信息就會被其他sentinel接受到,因?yàn)槭潜O(jiān)聽訂閱了同一個服務(wù)器的__sentinel__:hello
頻道,所sentinel就會感知到其他sentinel的存在。并sentinel將會更新其他的sentinel信息到自己的sentinel字典中。
sentinel 之間的通信
從上面我們知道每個Sentinel也會從__sentinel:hello__
頻道中接收其他Sentinel發(fā)送來的信息,并根據(jù)這些信息為其他Sentinel創(chuàng)建實(shí)例結(jié)構(gòu)和命令連接。
但是Sentinel 只會與主服務(wù)器和從服務(wù)器創(chuàng)建命令連接和訂閱連接,Sentinel 和 Sentinel 之間則只創(chuàng)建命令連接。
為什么sentinel與sentinel之間不需要創(chuàng)建訂閱連接呢?
首先我們要確定訂閱連接是用來干嘛的,訂閱連接是用來發(fā)現(xiàn)其他節(jié)點(diǎn)的,而sentinel已經(jīng)通過主服務(wù)器或者從服務(wù)器的頻道信息來發(fā)現(xiàn)未知的sentinel,也就是說sentinel訂閱了主/從服務(wù)器已經(jīng)知道了其他的sentinel,就不需要再進(jìn)行訂閱連接其他的sentinel了,而相互已知的sentinel只需要使用命令連接來進(jìn)行通信就夠了。
主/客觀下線
Sentinel 會以每秒一次的頻率向?qū)嵗?strong>包括主服務(wù)器,從服務(wù)器,其他Sentinel發(fā)送 PING
命令,并根據(jù)實(shí)例對PING命令
的回復(fù)判斷實(shí)例是否在線,當(dāng)一個實(shí)例在指定的時(shí)長中連續(xù)向Sentinel發(fā)送無效回復(fù)時(shí),Sentinel就會判斷為主觀下線。
Sentinel1 將向 Sentinel2、server、slave1、slave2發(fā)送ping命令。sentinel2也會進(jìn)行同樣的操作。那么一般會得到以下兩種情況的回復(fù)
- 有效回復(fù):返回 PONG、LOADING、MASTERDOWN 三個中的一個
- 無效回復(fù):非有效回復(fù)的內(nèi)容,或者是指定時(shí)間內(nèi)沒有返回任何的回復(fù),而這個指定時(shí)間的字段為
down-after-milliseconds
。
當(dāng)Sentinel講一個主服務(wù)器判斷為主觀下線
,他會向同樣監(jiān)視這個主服務(wù)器的其他 Sentinel 進(jìn)行詢問,如果有足夠多的結(jié)點(diǎn)判定這個主服務(wù)器為主觀下線
,那么就狀態(tài)改成客觀下線
,某個節(jié)點(diǎn)的狀態(tài)改成客觀下線之后,監(jiān)視這個節(jié)點(diǎn)的各個sentinel就會協(xié)商選取一個leader sentinel節(jié)點(diǎn)
,并且由領(lǐng)頭的 leader sentinel 節(jié)點(diǎn)發(fā)起一次針對主服務(wù)器的故障轉(zhuǎn)移。
這個選舉的過程在這里就不過多介紹了。有點(diǎn)類似raft。后面有空再說明。
故障轉(zhuǎn)移
在選舉出leader sentinel節(jié)點(diǎn)之后的故障轉(zhuǎn)移會做以下幾件事情:
- 在已下線主服務(wù)器屬下的所有從服務(wù)器里面,挑選出一個從服務(wù)器,并將其轉(zhuǎn)化成
主服務(wù)器
。 - 讓已下線的主服務(wù)器屬下的所有從服務(wù)器改成復(fù)制新的主服務(wù)器。
- 將已下線主服務(wù)器設(shè)置為
新的從服務(wù)器
新的主服務(wù)器是如何挑選出來的呢?首先leader sentinel節(jié)點(diǎn)會將已下線主服務(wù)器的所有從服務(wù)器保存到一個列表,根據(jù)一些規(guī)則進(jìn)行過濾:
- 刪除已下線或者狀態(tài)不正常的從服務(wù)器,保證列表中剩余的從服務(wù)器是
正常
的。 - 刪除所有最近5秒內(nèi)沒有回復(fù)leader sentinel節(jié)點(diǎn)的INFO命令的從服務(wù)器,保證列表中的從服務(wù)器都是
最新通信成功的
。 - 刪除與已下線的主服務(wù)器連接斷開超過 down-after-milliseconds * 10 毫秒的從服務(wù)器,保證剩余的服務(wù)器保存的
數(shù)據(jù)都是比較新
的
down-after-milliseconds:實(shí)例失去聯(lián)系的時(shí)間,而刪除斷開這個時(shí)間的10倍,是為了能保證剩余的從服務(wù)器沒有過早的與主服務(wù)器斷開連接。
- 會根據(jù)從服務(wù)器的優(yōu)先級進(jìn)行排序,選擇最高優(yōu)先級的從服務(wù)器,如果相同優(yōu)先級,則選擇偏移量最大服務(wù)器。因?yàn)槠屏看笠馕吨鴶?shù)據(jù)最新。
易主
選擇完主服務(wù)器之后,就開始改變從服務(wù)器的復(fù)制對象了。這一動作可以通過向服務(wù)器發(fā)送SLAVEOF
的命令來實(shí)現(xiàn)。
本文我們詳細(xì)介紹了redis集群中sentinel的數(shù)據(jù)結(jié)構(gòu),sentinel與主從服務(wù)器的連接,信息傳遞,以及主從服務(wù)器發(fā)生故障時(shí)的處理方式。
那么問題來了?如果sentinel 集群中某一個sentinel節(jié)點(diǎn)掛了會發(fā)送什么事情呢?
到此這篇關(guān)于redis 高可用 Sentinel 詳解的文章就介紹到這了,更多相關(guān)redis 高可用 Sentinel內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
從零搭建SpringBoot2.X整合Redis框架的詳細(xì)教程
這篇文章主要介紹了從零搭建SpringBoot2.X整合Redis框架的詳細(xì)教程,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12SpringBoot整合Redis實(shí)現(xiàn)序列化存儲Java對象的操作方法
這篇文章主要介紹了SpringBoot整合Redis實(shí)現(xiàn)序列化存儲Java對象,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-03-03