[toc]
Sentinel是Redis的高可用解決方案稿饰,由一個(gè)或多個(gè)Sentinel實(shí)例組成的Sentinel系統(tǒng)可以監(jiān)視多個(gè)主服務(wù)器压汪,以及這些主服務(wù)器屬下的所有從服務(wù)器忧换,并在被監(jiān)視的主服務(wù)器下線時(shí)盔憨,自動(dòng)將下線主服務(wù)器屬下的某個(gè)從服務(wù)器升級(jí)為主服務(wù)器洗搂,代替下線的主服務(wù)器繼續(xù)處理命令請(qǐng)求。若已下線的主服務(wù)器重新上線沛申,則將它設(shè)置為新的主服務(wù)器的從服務(wù)器劣领。
啟動(dòng)并初始化一個(gè)Sentinel
- 啟動(dòng)命令
$ redis-sentinel /path/to/your/sentinel.conf
或 $ redis-server /path/to/your/sentinel.conf --sentinel
-
初始化服務(wù)器
Sentinel本質(zhì)上是一個(gè)運(yùn)行在特殊模式下的Redis服務(wù)器,不過(guò)功能不一樣:
image Sentinel專用代碼
Sentinel模式下的Redis服務(wù)器使用專用的代碼铁材,只支持PING尖淘、SENTINEL、INFO衫贬、SUBSCRIBE德澈、UNSUBSCRIBE歇攻、PSUBSCRIBE固惯、PUNSUBSCRIBE七個(gè)命令:
// 服務(wù)器在 sentinel 模式下可執(zhí)行的命令
struct redisCommand sentinelcmds[] = {
{"ping",pingCommand,1,"",0,NULL,0,0,0,0,0},
{"sentinel",sentinelCommand,-2,"",0,NULL,0,0,0,0,0},
{"subscribe",subscribeCommand,-2,"",0,NULL,0,0,0,0,0},
{"unsubscribe",unsubscribeCommand,-1,"",0,NULL,0,0,0,0,0},
{"psubscribe",psubscribeCommand,-2,"",0,NULL,0,0,0,0,0},
{"punsubscribe",punsubscribeCommand,-1,"",0,NULL,0,0,0,0,0},
{"publish",sentinelPublishCommand,3,"",0,NULL,0,0,0,0,0},
{"info",sentinelInfoCommand,-1,"",0,NULL,0,0,0,0,0},
{"shutdown",shutdownCommand,-1,"",0,NULL,0,0,0,0,0}
};
- Sentinel狀態(tài)
服務(wù)器會(huì)初始化sentinelState結(jié)構(gòu),其中的字典masters記錄了該sentinel監(jiān)視的主服務(wù)器相關(guān)信息缴守,字典的鍵是主服務(wù)器的名字葬毫,值是一個(gè)sentinelRedisInstance結(jié)構(gòu),包含很多屬性屡穗,其中sentinelAddr是一個(gè)保存IP地址和端口號(hào)的結(jié)構(gòu)贴捡。
/* Sentinel 的狀態(tài)結(jié)構(gòu) */
struct sentinelState {
uint64_t current_epoch; // 當(dāng)前紀(jì)元
dict *masters;
// 保存了所有被這個(gè) sentinel 監(jiān)視的主服務(wù)器
// 字典的鍵是主服務(wù)器的名字
// 字典的值則是一個(gè)指向 sentinelRedisInstance 結(jié)構(gòu)的指針
... ...
} sentinel;
// Sentinel 會(huì)為每個(gè)被監(jiān)視的 Redis 實(shí)例創(chuàng)建相應(yīng)的 sentinelRedisInstance 實(shí)例
// (被監(jiān)視的實(shí)例可以是主服務(wù)器、從服務(wù)器村砂、或者其他 Sentinel )
typedef struct sentinelRedisInstance {
int flags; // 標(biāo)識(shí)值烂斋,記錄了實(shí)例的類(lèi)型,以及該實(shí)例的當(dāng)前狀態(tài)
char *name;
// 實(shí)例的名字
// 主服務(wù)器的名字由用戶在配置文件中設(shè)置
// 從服務(wù)器以及 Sentinel 的名字由 Sentinel 自動(dòng)設(shè)置
// 格式為 ip:port 础废,例如 "127.0.0.1:26379"
char *runid; // 實(shí)例的運(yùn)行 ID
sentinelAddr *addr; // 實(shí)例的地址
dict *sentinels; // 其他同樣監(jiān)控這個(gè)主服務(wù)器的所有 sentinel
dict *slaves;
// 如果這個(gè)實(shí)例代表的是一個(gè)主服務(wù)器
// 那么這個(gè)字典保存著主服務(wù)器屬下的從服務(wù)器
// 字典的鍵是從服務(wù)器的名字汛骂,字典的值是從服務(wù)器對(duì)應(yīng)的 sentinelRedisInstance 結(jié)構(gòu)
... ...
} sentinelRedisInstance;
/* 地址對(duì)象,用于保存 IP 地址和端口 */
typedef struct sentinelAddr {
char *ip;
int port;
} sentinelAddr;
假設(shè)一個(gè)sentinel監(jiān)聽(tīng)兩個(gè)主服務(wù)器评腺,結(jié)果是:
- 創(chuàng)建連接主服務(wù)器的網(wǎng)絡(luò)連接
對(duì)于每個(gè)主服務(wù)器帘瞭,Sentinel創(chuàng)建兩個(gè)連接主服務(wù)器的異步網(wǎng)絡(luò)連接:
- 命令連接,用于向主服務(wù)器發(fā)送命令并接收命令回復(fù)蒿讥;
- 訂閱連接蝶念,用于訂閱主服務(wù)器的 sentinel:hello頻道抛腕。
- 獲取主服務(wù)器信息
Sentinel默認(rèn)每10秒向監(jiān)視的主服務(wù)器發(fā)送INFO命令:得到主服務(wù)器以及其下屬?gòu)姆?wù)器的信息:
image
根據(jù)返回的信息可以更新或創(chuàng)建sentinelRedisInstance實(shí)例結(jié)構(gòu)中的信息,并創(chuàng)建連接到從服務(wù)器的命令連接和訂閱連接:
-
獲取從服務(wù)器信息
Sentinel每10s向從服務(wù)器發(fā)送INFO命令媒殉,獲得從服務(wù)器運(yùn)行ID担敌、角色、其主服務(wù)器IP地址和端口适袜、連接狀態(tài)柄错、優(yōu)先級(jí)、復(fù)制偏移量等信息苦酱,更新從服務(wù)器的實(shí)力結(jié)構(gòu):
image 頻道信息
Sentinel每2s會(huì)通過(guò)命令連接向所有監(jiān)視的主服務(wù)器和從服務(wù)器發(fā)送命令:
PUBLISH __sentinel__:hello "<s_ip>,<s_port>,<s_runid>,<s_epoch>,<m_name>,<m_ip>,<m_port>,<m_epoch>"
該命令向服務(wù)器的sentinel:hello頻道發(fā)送一條信息售貌,其他訂閱該頻道的Sentinel會(huì)通過(guò)該頻道接收到消息:
如果Sentinel收到的信息是自己發(fā)送的,則忽略疫萤;否則根據(jù)信息更新實(shí)例結(jié)構(gòu)中的sentinels字典:
這樣就能發(fā)現(xiàn)監(jiān)視同一個(gè)主服務(wù)器的其他Sentinel了颂跨,并與其他Sentinel建立命令連接,形成網(wǎng)絡(luò)扯饶。
下線
Sentienl默認(rèn)每秒向創(chuàng)建了命令連接的實(shí)例(包括主服務(wù)器恒削、從服務(wù)器和其他Sentinel)發(fā)送PING命令,并通過(guò)實(shí)例返回的PING命令回復(fù)來(lái)判斷實(shí)例是否在線尾序。
- 若回復(fù)是+PING钓丰、-LOADING、-MASTERDOWN每币,則有效携丁;
- 否則就是無(wú)效回復(fù)。
若連續(xù)一段時(shí)間(down_after_milliseconds)內(nèi)返回的是無(wú)效回復(fù)兰怠,則將Sentinel狀態(tài)中的對(duì)應(yīng)實(shí)例結(jié)構(gòu)的flags屬性設(shè)置為主觀下線(SRI_S_DOWN)梦鉴。
不同Sentinel對(duì)down_after_milliseconds設(shè)置可能不同,因此不同Setinel對(duì)某個(gè)服務(wù)器是否主觀下線的判斷可能不同揭保。
當(dāng)Sentinel判斷一個(gè)主服務(wù)器為主觀下線后肥橙,會(huì)詢問(wèn)其他Sentinel該主服務(wù)器的狀態(tài),若超過(guò)一定數(shù)量的Sentinel判斷其為主觀下線秸侣,則將該主服務(wù)器判定為客觀下線存筏,并對(duì)其執(zhí)行故障轉(zhuǎn)移操作。
- 詢問(wèn)
發(fā)送以下命令進(jìn)行詢問(wèn):
SENTINEL is-master-down-by-addr <ip> <port> <current_epoch> <runid>
- 目標(biāo)Sentinel接收詢問(wèn)命令
目標(biāo)Sentinel接收到詢問(wèn)命令味榛,則根據(jù)詢問(wèn)命令的IP和端口號(hào)椭坚,檢查對(duì)應(yīng)主服務(wù)器的狀態(tài),然后回復(fù):
- <down_state>
- <leader_runid>
-
<leader_epoch>
image
- 接收詢問(wèn)命令回復(fù)
統(tǒng)計(jì)同意主服務(wù)器已下線的Sentinel數(shù)量励负,若超過(guò)某個(gè)數(shù)值藕溅,則則判定該主服務(wù)器為客觀下線(SRI_O_DOWN)。
不同Sentinel判斷客觀下線的條件(數(shù)量值)可能不同继榆。
選舉領(lǐng)頭Sentinel
當(dāng)一個(gè)主服務(wù)器被判定為客觀下線時(shí)巾表,監(jiān)視這個(gè)主服務(wù)器的Sentienl會(huì)協(xié)商出一個(gè)領(lǐng)頭Sentinel汁掠,并由領(lǐng)頭Sentinel對(duì)下線主服務(wù)器進(jìn)行故障轉(zhuǎn)移操作。
領(lǐng)頭Sentinel選舉規(guī)則和方法:
- 所有在線的Sentinel都有被選為領(lǐng)頭Sentinel的資格集币,換句話說(shuō)考阱,監(jiān)視同一個(gè)主服 務(wù)器的多個(gè)在線Sentinel中的任意一個(gè)都有可能成為領(lǐng)頭Sentinel。
- 每次進(jìn)行領(lǐng)頭Sentinel選舉之后鞠苟,不論選舉是否成功乞榨,所有Sentinel的配置紀(jì)元(configuration epoch)的值都會(huì)自增一次。配置紀(jì)元實(shí)際上就是一個(gè)計(jì)數(shù)器当娱,并沒(méi)有什么特別的吃既。
- 在一個(gè)配置紀(jì)元里面,所有Sentinel都有一次將某個(gè)Sentinel設(shè)置為局部領(lǐng)頭 Sentinel的機(jī)會(huì)跨细,并且局部領(lǐng)頭一旦設(shè)置鹦倚,在這個(gè)配置紀(jì)元里面就不能再更改。 每個(gè)發(fā)現(xiàn)主服務(wù)器進(jìn)人客現(xiàn)下線的Sentinel都會(huì)要求其他Sentinel將自己設(shè)置為局部領(lǐng)頭Sentinel冀惭。
- 當(dāng)一個(gè)Sentinel(源Sentinel)向另一個(gè) Sentinel (目標(biāo)Sentinel)發(fā)送SENTINEL is-master-down-by-addr命令震叙,并且命令中的runid參數(shù)不是*符號(hào)而是源Sentinel的運(yùn)行ID時(shí),這表示源Sentinel要求目標(biāo)Sentinel將前者設(shè)置為后者的局 部 領(lǐng) 頭 Sentinel散休。
- Sentinel設(shè)置局部領(lǐng)頭Sentinel的規(guī)則是先到先得:最先向目標(biāo)Sentinel發(fā)送設(shè)置要求的源Sentinel將成為目標(biāo)Sentinel的局部領(lǐng)頭Sentinel,而之后接收到的所有設(shè)置 要求都會(huì)被目標(biāo)Sentinel拒絕媒楼。
- 目標(biāo)Sentinel在接收到SENTINEL is-master-down-by-addr命令之后,將向 源Sentinel返回一條命令回復(fù)戚丸,回復(fù)中的leader_runid參數(shù)和leader_epoch 參數(shù)分別記錄了目標(biāo)Sentinel的局部領(lǐng)頭Sentinel運(yùn)行ID和配置紀(jì)元划址。
- 源Sentinel在接收到目標(biāo)Sentinel返回的命令回復(fù)之后,會(huì)檢查回復(fù)中l(wèi)eadeir_ epoch參敗的值和自己的配置紀(jì)元是否相同昏滴,如果相同的話猴鲫,那么源Sentinel繼續(xù)取出回復(fù)中的leader_runid參數(shù)对人,如果leader_runid參數(shù)的值和源Sentinel的運(yùn)行ID 一致谣殊,那么表示目標(biāo)Sentinel將源Sentine設(shè)置成了局部領(lǐng)頭Sentinel。
- 如果有某個(gè)Sentinel被半數(shù)以上的Sentinel設(shè)置成了局部領(lǐng)頭Sentinel, 那 么 這 個(gè)Sentinel成為領(lǐng)頭Sentinel牺弄。舉個(gè)例子姻几,在一個(gè)由10個(gè)Sentinel組成的Sentinel系統(tǒng)里面,只要有大于等于10/2+1=6個(gè)Sentinel將某個(gè)Sentinel設(shè)置為局部領(lǐng)頭Sentinel,那么被設(shè)置的某個(gè)Sentinel就會(huì)成為領(lǐng)頭Sentinel势告。
- 因?yàn)轭I(lǐng)頭Sentinel的產(chǎn)生需要半數(shù)以上Sentinel的支持蛇捌,并且每個(gè)Sentinel在每個(gè) 配置紀(jì)元里面只能設(shè)置一次局部領(lǐng)頭Sentinel,所以在一個(gè)配置紀(jì)元里面,只會(huì)出現(xiàn) 一個(gè)領(lǐng)頭 Sentinel咱台。
- 如果在給定時(shí)限內(nèi)络拌,沒(méi)有一個(gè)Sentinel被選舉為領(lǐng)頭Sentinel, 那么各個(gè)Sentinel將在一段時(shí)間之后再次進(jìn)行選舉,直到選出領(lǐng)頭Sentinel為止回溺。
故障轉(zhuǎn)移
領(lǐng)頭Sentinel將對(duì)已下線的主服務(wù)器執(zhí)行故障轉(zhuǎn)移操作春贸,執(zhí)行以下三個(gè)步驟:
- 在已下線主服務(wù)器屬下的所有從服務(wù)器里邊混萝,挑選一個(gè)從服務(wù)器,并將其轉(zhuǎn)換為主服務(wù)器萍恕;
- 讓已下線主服務(wù)器屬下的所有從服務(wù)器改為復(fù)制新的主服務(wù)器逸嘀;
- 將已下線主服務(wù)器設(shè)置為新的主服務(wù)器的從服務(wù)器,當(dāng)這個(gè)舊的主服務(wù)器重新上線允粤,就會(huì)成為新的主服務(wù)器的從服務(wù)器崭倘。
挑選新的主服務(wù)器規(guī)則:
領(lǐng)頭Sentinel會(huì)將己下線主服務(wù)器的所有從服務(wù)器保存到一個(gè)列表里面,然后按以下規(guī)則类垫,一項(xiàng)一項(xiàng)地對(duì)列表進(jìn)行過(guò)濾:
- 刪除列表中所有處于下線或者斷線狀態(tài)的從服務(wù)器司光,這可以保證列表中剩余的從服務(wù)器都是正常在線的;
- 刪除列表中所有最近五秒內(nèi)沒(méi)有回復(fù)過(guò)領(lǐng)頭Sentinel的INFO命令的從服務(wù)器悉患,這可以保證列表中剩余的從服務(wù)器都是最近成功進(jìn)行過(guò)通信的飘庄。
- 刪除所有與已下線主服務(wù)器連接斷開(kāi)建過(guò)down-after-milliseconds10毫秒的從服務(wù)器 : down- after-milliseconds選項(xiàng)指定了判斷主服務(wù)器下線所需的時(shí)間,而刪除斷開(kāi)時(shí)長(zhǎng)超過(guò)down-after-milliseconds10毫秒的從服務(wù)器购撼, 則可以保證列表中剩余的從服務(wù)器保存的數(shù)據(jù)都是比較新的跪削。
之后,領(lǐng)頭Sentinel將根據(jù)從服務(wù)器的優(yōu)先級(jí)迂求,對(duì)列表中剩余的從服務(wù)器進(jìn)行排序碾盐,并選出其中優(yōu)先級(jí)最高的從服務(wù)器。
如 果 有 多 個(gè) 具 有 相 同 最 高 優(yōu) 先級(jí) 的 從服務(wù)器揩局,那么領(lǐng)頭Sentinel將按從服務(wù)器的復(fù)制偏移量毫玖,對(duì)具有相同最高優(yōu)先級(jí)的所有從服務(wù)器進(jìn)行排序,并選出其中偏移量最大的從服務(wù)器 (復(fù)制偏移量最大的從服務(wù)器就是保存著最新數(shù)據(jù)的從服務(wù)器)凌盯。
最后付枫,如果有多個(gè)優(yōu)先級(jí)最高箱蝠、復(fù)制偏移量最大的從服務(wù)器蟆盹,那么領(lǐng)頭Sentinel將按照運(yùn)行ID對(duì)這些從服務(wù)器進(jìn)行排序,并選出其中運(yùn)行ID最小的從服務(wù)器匿刮。