Redis Sentinel

1.概述

Sentinel(哨崗、哨兵)是Redis高可用性的解決方案:由一個或多個Sentinel實例組成的Sentinel系統可以監視任意多個主服務器,以及這個主服務器下的從服務器,并在被監視主服務器下線時,自動將下線主服務器下的某個從服務器升級為新的主服務器,然后由新的主服務器代替已下線的主服務器繼續處理命令請求。


Sentinel系統

當主服務器下線時,Sentinel會采取如下操作:


主服務器下線

Sentinel會選出一個從服務器作為新的主服務器,假設此時選出的從服務器為:從服務器2,此時系統會如下圖所示:
新主服務器上線

當已下線的主服務器重新上線后,變為如下所示:


下線的主服務器重新上線

2.啟動并初始化Sentinel

啟動Sentinel可以使用命令:

redis-sentinel /path/to/your/sentinel.conf
#或者命令
redis-server /path/to/your/sentinel.conf --sentinel

這兩個命令的效果完全一樣。
一個Sentinel實例啟動時,它需要執行以下步驟:

  1. 初始化服務器
  2. 將普通Redis服務器使用的代扣替換為Sentinel專用代碼
  3. 初始化Sentinel 狀態
  4. 根據給定的配置文件,初始化Sentinel的監視主服務器列表
  5. 創建連向主服務器的網絡連接

2.1 初始化服務器

Sentinel本質上是一個運行在特殊模式下的Redis服務器,Sentinel的啟動第一步就是啟動一個普通的Redis服務器。但是Sentinel和普通Redis服務器執行的工作不一樣,所以Sentinel的初始化過程和普通Redis服務器并不完全相同。
Sentinel在初始化時,不需要載入RDB文件或者AOF文件。
并且Sentinel向外提供的命令和普通Redis服務器也不是完全一樣的,像SET這一類命令Sentinel是沒有的。

2.2 使用Sentinel專用代碼

  • 使用與普通Redis服務器不同的默認端口號
  • 載入Sentinel需要使用的命令列表

Sentinle支持:PING、SENTINEL、INFO、SUBSCRIBE、UNSUBSCRIBE、PSUBSCRIBE和PUNSUBSCRIBE這七個命令

2.3 初始化Sentinel狀態

在應用了Sentinel的專用代碼之后,接下來服務器會初始化一個snetinel.c/sentinelState的結構,這個結構中保存了服務器中所有和Sentinel功能相關的狀態。
比較重要的一個屬性:

#保存了所有這個Sentinle監視的主服務器
#字典的鍵是主服務器的名字
#字典的值則是一個指向sentinelRedisInstance結構的指針
dict *masters;

2.4 初始化Sentinel狀態的masters屬性

對Sentinel狀態的初始化會引起對于masters字典的初始化,即初始化Sentinel監控的所有主服務器,它是根據Sentinel的配置文件來進行初始化的。
配置文件示例如下:

# master1 configure
sentinel monitor master1 127.0.0.1 6379 2
sentinel down-after-milliseconds master1 30000
sentinel parallel-syncs master1 1
sentinel failover-timeout master1 900000

# master2 configure
sentinel monitor master2 127.0.0.1 6379 2
sentinel down-after-milliseconds master2 30000
sentinel parallel-syncs master2 1
sentinel failover-timeout master2 900000

此時Sentinel將主服務器master1會創建為如下結構:


master1結構

此時Sentinel狀態的結構如下所示:


Sentinel狀態的結構

2.5 創建連向主服務器的連接

初始化Sentinel的最后一步是創建連向主服務器的網絡連接。Sentinel將成為主服務器的客戶端,它可以向主服務器發送命令,并從命令回復中獲取相關信息。
Sentinel對每個被監視的主服務器會創建兩個異步網絡連接:

  • 命令連接,這個連接專門用于向主服務器發送命令,并接收命令回復
  • 訂閱連接,這個連接專門用于訂閱主服務器的sentinel:hello 頻道

為什么使用兩個連接?
Redis在使用訂閱功能時,如果在發送消息時,想要接收信息的客戶端不在線或者斷線,那么這個客戶端就會丟失這條消息,因此為了不丟失sentinel:hello 頻道的任何信息,sentinel必須開通一條專門的連接來接收該頻道的消息。但是由于Snetinel還必須向主服務器發送命令來獲取主服務器的相關信息,因此必須再開通一條命令連接。
因為Sentinel需要與多個實例創建多個網絡連接,所有Sentinel使用的異步連接

如下圖展示了一個Snetinel與兩個master建立連接的情況:


Snetinel與兩個master建立連接

3.獲取主服務器信息

Sentinel會以每10秒一次的頻率,通過命令連接向被監視的主服務器發送INFO命令,并通過分析INFO的回復來獲取主服務器當前的狀態。
通過分析INFO的回復,Sentinel可以獲取以下兩個方面的信息:

  • 主服務器本身的信息,包括run_id域記錄的服務器運行ID,以及role域記錄的服務器角色
  • 主服務器下從服務器的信息,每個從服務器都由一個slave字符串開頭的行記錄,每行的ip記錄了從服務器的IP地址,port記錄了從服務器的端口號,根據ipport的信息Sentinel無需用戶來配置從服務器信息,即可自動發現從服務器。

根據run_id和role記錄的信息,Sentinel對主服務器的實例進行更新。
從服務器的信息會更新至主服務器實例結構中的slaves字典中,這個字典記錄了主服務器下從服務器的名單:

  • 字典的鍵是由Sentinel自動設置的從服務器的名字,格式為:ip:port
  • 字典的值則是對應從服務器的實例結構。

具體結構如下圖所示:


Sentinel主服務器實例中帶有從服務器信息

注意:從服務器的flags值為SRI_SLAVE

4.獲取從服務器信息

當Sentinel發現主服務器有新的從服務器出現時,Sentinel除了會為這個新的從服務器創建相應的實體結構之外,Sentinel還會創建連接到從服務器的命令連接和訂閱連接。
在創建命令連接后,會以每10秒一次的頻率發送INFO命令,并解析返回的信息。提取出如下信息對從服務器實例進行更新:

  • 從服務器運行run_id
  • 從服務器角色role
  • 主服務器的ip地址和端口號
  • 主服務器的連接狀態:master_link_status
  • 從服務器器優先級:slave_priority
  • 從服務器的復制偏移量:slave_repl_offset

更新后從服務器結構如下所示:


從服務器結構

5.向主服務器和從服務器發送消息

默認情況下Sentinel會以兩秒每次的頻率,通過命令連接向所有被監視的主服務器和從服務器發送如下格式的命令:

#s開頭的參數是Sentinel的信息
#m開頭的信息是主服務器的信息:如果Sentinel正在監視的為主服務器那么就是主服務器自身的信息;
#如果Sentinel監視的是從服務器那么就是從服務器復制的主服務器的信息
PUBLISH __sentinel__:hello "<s_ip>,<s_port>,<s_runid>,<s_epoch>,<m_name>,<m_ip>,<m_port>,<m_epoch>"

具體參數:

參數 含義
s_ip Sentinel的ip
s_port Sentinel的port
s_runid Sentinel的runid
s_epoch Sentinel當前的配置紀元(在選舉領頭Sentinel時使用)
m_name 主服務器的名字
m_ip 主服務器的ip
m_port 主服務器的port
m_epoch 主服務器的配置紀元

6.接收來自主服務器和從服務器的頻道信息

Sentinel當與一個主服務器或從服務器建立器訂閱連接之后,Sentinel就會通過訂閱連接向服務器發送以下命令來進行訂閱消息:

SUBSCRIBE __sentinel__:hello

Senetinel會一直對sentinel:hello繼續訂閱直到Sentinel與服務器斷開連接為止。也就是說Sentinel既會通過命令連接向服務器發送sentinel:hello消息又會通過訂閱連接從服務器接收消息。
對于監視同一個服務器的多個Sentinel來說,一個Sentinel發送的消息會被其他Sentinel接收到,這些信息會被用于更新其他Sentinel對于發送消息的Sentnel的認知,也會被用于更新其他Sentinel對于被監視服務的認知。
當一個Sentinel從sentinel:hello收到一條信息時,Sentinel會對這條信息進行分析,提取出信息中的<s_ip>,<s_port>,<s_runid>等上面提到的8個參數:

  • 如果記錄的<s_ip>與當前Sentinel一致,那么說明是自身發送的消息, 那么會丟棄這條消息
  • 如果不一致,那么說明還有另外一個Sentinel在監視同一個服務器,接收消息的Sentinel會對器監視的主服務器實例結構中的sentinels字典進行更新

6.1 更新sentinels字典

  • sentinels字典中鍵為Sentinel的名字,格式:ip:port
  • sentinels字典中值為對應的Sentinel的實例結構

具體如下圖所示:


sentinels結構

6.2 創建連接其他Sentinel的命令連接

Sentinel與Sentinel之間只會創建命令連接,不會創建訂閱連接,因為Sentinel需要通過接收主服務器和從服務器的訂閱信息來發現未知的Sentinel,對于相互已知的Sentinel不需要再建立訂閱連接來進行通信。

7.檢測主觀下線狀態

默認情況下Sentinel會向其監控的服務器(主服務器、從服務器、Sentinel)以每秒一次的頻率發送PING命令,并通過PING命令的回復來判斷具體實例的在線狀態。實例回復可以分為以下兩種情況:

  • 有效回復:+PONG、-LOADING、-MASTERDOWN三種回復中的其中一種
  • 無效回復:+PONG、-LOADING、-MASTERDOWN三種回復之外的回復,或者在指定時間內沒有回復

Sentinel的配置文件中的down-after-milliseconds指定了Sentinel判斷實例進入主觀下線的時間長度:如果一個實例在down-after-milliseconds毫秒內連續向Sentinel返回無效回復,那么Sentinel會修改這個實例對應的實例結構,在flags屬性中打開SRI_S_DOWN標識,以此來表示實例進入主觀下線狀態。
具體如下:

主觀下線

Sentinel的配置文件中的down-after-milliseconds不但用來判斷主服務器的主觀下線時間,還用于此主服務器下所有的從服務器的主觀下線狀態的判斷。
每個Senttinel配置文件中down-after-milliseconds的配置不一定會一樣,因此對于同一個服務器而言,一個Sentinel認為其下線但是另外一個Sentinel可能沒有認為其主觀下線。

8.檢測客觀下線狀態

當Sentinel將一個主服務器檢測為主觀下線后,為確認這個主服務器是否真的下線,它會向同時在監控這臺主服務器的其他Sentinel進行詢問,看他們是否也認為服務器進入下線狀態(可以是主觀下線或客觀下線),如果Sentinel從其他Sentinel哪里接收到足夠的數量的已下線判斷后,Sentinel就會將主服務器判定為客觀下線,對其執行故障轉移
檢測客觀下線主要分以下三步:

8.1 發送SENTINEL is-master-down-by-addr命令

命令格式:

SENTINEL is-master-down-by-addr <ip> <port> <current_epoch> <runid>

具體參數含義:

參數 含義
ip 被Sentinel判斷為主觀下線的主服務器ip地址
port 被Sentinel判斷為主觀下線的主服務器port地址
current_epoch Sentinel當前配置紀元,用于選舉領頭Sentinel
runid 可以為或Sentinel的運行ID;當為時,代表命令僅用于檢測主服務器客觀下線狀態;當為Sentinel的運行ID時,則用于選舉領頭Sentinel

82. 接收SENTINEL is-master-down-by-addr命令

當一個Sentinel接收到另一個Sentinel發送的SENTINEL is-master-down-by-addr時,會檢測當前監控的主服務的主觀下線狀態,并做出如下格式的回復:

  1. <down_state>
  2. <leader_runid>
  3. <leader_epoch>

具體參數含義:

參數 含義
down_state 目標Sentinel主服務器主觀下線狀態:1代表主服務已下線,0代表主服務器未下線
leader_runid 可以為或目標Sentinel的局部領頭Sentinel的運行ID;當為時,代表命令僅用于檢測主服務器客觀下線狀態;當為目標Sentinel的局部領頭Sentinel的運行ID時,則用于選舉領頭Sentinel
leader_epoch 目標Sentinel局部領頭Sentinel的配置紀元,僅在leader_runid部位時有效,如果leader_runid為,則leader_epoch總是為0

8.3 接收SENTINEL is-master-down-by-addr命令回復

根據其他Sentinel返回的回復,Sentinel將統計其他Sentinel同意主服務器已下線的數量,這一數量達到配置指定的判斷客觀下線所需的數量時,Sentinel會將主服務器實例結構中的flags屬性的SRI_O_DOWN打開,表示主服務器已進入客觀下線狀態。具體如下圖所示:

客觀下線

配置文件中對于客觀下線的配置:

#此表示表示總共需要兩個Sentinel認為主服務器已進入主觀下線狀態,那么就可以判斷主服務為客觀下線
sentinel monitor master 127.0.0.1 6379 2

另:不同的Sentinel配置文件不同,因此對于同一主服務器認為其客觀下線的判斷也不一樣。

9.選舉領頭Sentinel

當一個主服務器被判定為主觀下線時,監控這個主服務器的各個Sentinel會進行協商,選舉一個領頭Sentinel,并由選舉出的這個領頭Sentinel對主服務器進行故障轉移。
具體選舉步驟如下:
1)所有在線Sentinel都有被選為領頭Sentinel的資格
2) 每次進行領頭Sentinel選舉之后,無論選舉是否成功,所有Sentinel的配置紀元值都會自增一次,配置紀元實際上就是一個計數器,并無其他特別之處
3) 在一個配置紀元里面,所有Sentinel都有一次將某個Sentinel設置為局部領頭Sentinel的機會,并且局部領頭Sentinel一旦設置,在這個配置紀元里面就不能在更改
4)每個發現主服務器客觀下線的Sentinel都會要求其他Sentinel將其設置為局部領頭Sentinel
5)當一個Sentinel向另一個Sentinel(目標)發送SENTINEL is-master-down-by-addr時,并且命令中runid不為*而是源Sentinel自身的runid時,即表示源Sentinel要求目標Sentinel將其設置為自己的局部領頭Sentinel。設置局部領頭Sentinel的規則是先到先到,最先向目標Sentinel發送命令的源Sentinel被成功設置為目標Sentinel的局部領頭Sentinel。
6) 目標Sentinel在接收SENTINEL is-master-down-by-addr命令后,會向源Sentinel返回一條命令回復,回復中的<leader_runid>和<leader_epoch>分別記錄了被成功設置為自身局部領頭Sentinel的runid和epoch,源Sentinel在收到目標Sentinel返回的命令回復后,會對目標Sentinel回復的<leader_runid>和<leader_epoch>進行檢查,如果都與自身信息一致則表示目標Sentinel將自身設置為局部領頭Sentinel
7) 如果有半數以上的Sentinel將源Sentinel設置為局部領頭Sentinel,那么這個Sentinel就成為領頭Sentinel。因為需要半數以上,因此每次最多會有一個領個Sentinel被選舉出。
8) 如果在給定的時間內,沒有選出領頭Sentinel,那么各個Sentinel將在一段時間后再次進行選舉,直到選出領頭Sentinel

10.故障轉移

在選出領頭Sentinel之后,該Sentinel會對被判定為客觀下線的主服務器執行故障轉移:
1) 在已下線的主服務器屬下的從服務器里面挑選一個從服務器,并將其轉換為從服務器。
2) 讓已下線的主服務器下的剩余的從服務器改為復制新的主服務器
3) 將已下線的主服務器設置為新的主服務器的從服務器,當這個舊的主服務器重新上線時,它會成為新的主服務器的從服務器

10.1 選出新的主服務器

領頭Sentinel挑選出一個狀態良好、數據完整的從服務器,然后向這個從服務器發送SLAVEOF no one命令,將這個從服務器轉換為主服務器。
具體挑選從服務器的規則如下:

  • 刪除列表中所有處于下線或斷線狀態的從服務器
  • 刪除最近5秒內沒有回復過領頭Sentinel的INFO命令的從服務器
  • 刪除所有與已下線主服務器連接斷開查過 down-after-milliseconds*10時間的從服務器
  • 根據從服務器的優先級進行排序
  • 如果有多個同優先級的從服務器,那么按照復制偏移量進行排序
  • 如果復制偏移量相同的從服務器出現多臺,那么將從服務器的運行id進行排序,并選出運行id最小的從服務器

11.參考資料

《Redis設計與實現》

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,237評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,957評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,248評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,356評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,081評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,485評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,534評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,720評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,263評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,025評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,204評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,787評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,461評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,874評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,105評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,945評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,205評論 2 375

推薦閱讀更多精彩內容

  • [toc]Sentinel是Redis的高可用解決方案,由一個或多個Sentinel實例組成的Sentinel系統...
    涵仔睡覺閱讀 269評論 0 0
  • Redis Sentinel 是一個分布式系統, 你可以在一個架構中運行多個 Sentinel 進程(progre...
    你是妖怪吧閱讀 904評論 0 0
  • Sentinel是Redis的高可用性解決方案,本文主要介紹Sentinel的初始化過程及其與一般Redis服務器...
    wenmingxing閱讀 3,107評論 1 5
  • 你是 秋水長天 你是山中的一泓清泉 奏著悅耳的琴聲 融化了皚皚白雪 你是海...
    秋水長天_42b2閱讀 236評論 0 1
  • 你問我怎么寫詩寫詩嘛就要像那蒲公英種子飛到哪便落到哪 20160529
    嚼冰閱讀 173評論 0 0