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