Redis Sentinel

1.概述

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


Sentinel系統(tǒng)

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


主服務器下線

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

當已下線的主服務器重新上線后,變?yōu)槿缦滤荆?/p>

下線的主服務器重新上線

2.啟動并初始化Sentinel

啟動Sentinel可以使用命令:

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

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

  1. 初始化服務器
  2. 將普通Redis服務器使用的代扣替換為Sentinel專用代碼
  3. 初始化Sentinel 狀態(tài)
  4. 根據(jù)給定的配置文件,初始化Sentinel的監(jiān)視主服務器列表
  5. 創(chuàng)建連向主服務器的網(wǎng)絡連接

2.1 初始化服務器

Sentinel本質(zhì)上是一個運行在特殊模式下的Redis服務器,Sentinel的啟動第一步就是啟動一個普通的Redis服務器。但是Sentinel和普通Redis服務器執(zhí)行的工作不一樣,所以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狀態(tài)

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

#保存了所有這個Sentinle監(jiān)視的主服務器
#字典的鍵是主服務器的名字
#字典的值則是一個指向sentinelRedisInstance結(jié)構(gòu)的指針
dict *masters;

2.4 初始化Sentinel狀態(tài)的masters屬性

對Sentinel狀態(tài)的初始化會引起對于masters字典的初始化,即初始化Sentinel監(jiān)控的所有主服務器,它是根據(jù)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會創(chuàng)建為如下結(jié)構(gòu):


master1結(jié)構(gòu)

此時Sentinel狀態(tài)的結(jié)構(gòu)如下所示:


Sentinel狀態(tài)的結(jié)構(gòu)

2.5 創(chuàng)建連向主服務器的連接

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

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

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

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


Snetinel與兩個master建立連接

3.獲取主服務器信息

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

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

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

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

具體結(jié)構(gòu)如下圖所示:


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

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

4.獲取從服務器信息

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

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

更新后從服務器結(jié)構(gòu)如下所示:


從服務器結(jié)構(gòu)

5.向主服務器和從服務器發(fā)送消息

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

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

具體參數(shù):

參數(shù) 含義
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就會通過訂閱連接向服務器發(fā)送以下命令來進行訂閱消息:

SUBSCRIBE __sentinel__:hello

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

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

6.1 更新sentinels字典

  • sentinels字典中鍵為Sentinel的名字,格式:ip:port
  • sentinels字典中值為對應的Sentinel的實例結(jié)構(gòu)

具體如下圖所示:


sentinels結(jié)構(gòu)

6.2 創(chuàng)建連接其他Sentinel的命令連接

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

7.檢測主觀下線狀態(tài)

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

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

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

主觀下線

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

8.檢測客觀下線狀態(tài)

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

8.1 發(fā)送SENTINEL is-master-down-by-addr命令

命令格式:

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

具體參數(shù)含義:

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

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

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

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

具體參數(shù)含義:

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

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

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

客觀下線

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

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

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

9.選舉領頭Sentinel

當一個主服務器被判定為主觀下線時,監(jiān)控這個主服務器的各個Sentinel會進行協(xié)商,選舉一個領頭Sentinel,并由選舉出的這個領頭Sentinel對主服務器進行故障轉(zhuǎn)移。
具體選舉步驟如下:
1)所有在線Sentinel都有被選為領頭Sentinel的資格
2) 每次進行領頭Sentinel選舉之后,無論選舉是否成功,所有Sentinel的配置紀元值都會自增一次,配置紀元實際上就是一個計數(shù)器,并無其他特別之處
3) 在一個配置紀元里面,所有Sentinel都有一次將某個Sentinel設置為局部領頭Sentinel的機會,并且局部領頭Sentinel一旦設置,在這個配置紀元里面就不能在更改
4)每個發(fā)現(xiàn)主服務器客觀下線的Sentinel都會要求其他Sentinel將其設置為局部領頭Sentinel
5)當一個Sentinel向另一個Sentinel(目標)發(fā)送SENTINEL is-master-down-by-addr時,并且命令中runid不為*而是源Sentinel自身的runid時,即表示源Sentinel要求目標Sentinel將其設置為自己的局部領頭Sentinel。設置局部領頭Sentinel的規(guī)則是先到先到,最先向目標Sentinel發(fā)送命令的源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) 如果有半數(shù)以上的Sentinel將源Sentinel設置為局部領頭Sentinel,那么這個Sentinel就成為領頭Sentinel。因為需要半數(shù)以上,因此每次最多會有一個領個Sentinel被選舉出。
8) 如果在給定的時間內(nèi),沒有選出領頭Sentinel,那么各個Sentinel將在一段時間后再次進行選舉,直到選出領頭Sentinel

10.故障轉(zhuǎn)移

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

10.1 選出新的主服務器

領頭Sentinel挑選出一個狀態(tài)良好、數(shù)據(jù)完整的從服務器,然后向這個從服務器發(fā)送SLAVEOF no one命令,將這個從服務器轉(zhuǎn)換為主服務器。
具體挑選從服務器的規(guī)則如下:

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

11.參考資料

《Redis設計與實現(xiàn)》

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

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

友情鏈接更多精彩內(nèi)容