1. 程式人生 > >Redis 哨兵模式的理論(轉載)

Redis 哨兵模式的理論(轉載)

Sentinel是Redis的高可用性解決方案,本文主要介紹Sentinel的初始化過程及其與一般Redis伺服器的區別。並說明Sentinel監視伺服器的方法和原理,說明Sentinel如何判斷一個伺服器是否線上,並介紹故障轉移過程。

I、上帝視角看Sentinel

由一個或多個Sentinel例項組成的Sentinel系統可以監視任意多個主伺服器,以及這些主伺服器屬下的所有從伺服器,並在被監視的主伺服器進入下線狀態時,自動將下線主伺服器屬下的某個從伺服器升級為新的主伺服器,然後由新的主伺服器替代已下線的主伺服器繼續處理命令請求。

Sentinel的主要功能就是監視主伺服器是否下線,並進行相應處理(故障轉移

)。
下面四個示意圖展示了Sentinel系統監視伺服器過程:

1、server2、server3、server4 三個從伺服器正在複製主伺服器server1,而Sentinel系統監視這四個伺服器:

2、此時,server1下線,從伺服器會中止其複製狀態:

3、當Sentinel系統判斷server1的下線時長超過下線時長上限時,會為server1執行故障轉移操作:
· 首先,Sentinel系統會挑選server1屬下的其中一個從伺服器(後面會介紹選擇標準),將其升級為新的主伺服器;
· 然後,Sentinel系統會向server1屬下的所有從伺服器傳送新的複製指令,讓它們成為新的主伺服器(以server2為例)的從伺服器;
· Sentinel系統還會繼續監視已經下線的server1,並在它重新上線時,將其設定為server2的從伺服器;

II、啟動並初始化Sentinel

可以使用如下命令啟動一個Sentinel:

$ redis-sentinel /path/to/your/sentinel.conf
或者
$ redis-server /path/to/your/sentinel.conf --sentinel

這時Sentinel系統會執行以下操作:
· 初始化伺服器;
· 將普通Redis伺服器使用的程式碼替換成Sentinel專用程式碼;
· 初始化Sentinel狀態;
· 根據給定的配置檔案,初始化Sentinel的監視主伺服器列表;
· 建立連向主伺服器的網路連線;

2.1 初始化伺服器

Sentinel本質上只是一個執行在*特殊模式下

8的Redis伺服器,所以啟動Sentinel的第一步就是啟動一個普通的Redis伺服器,具體步驟如Redis之伺服器
但是,Sentinel的工作與普通Redis有有所不同,所以在初始化過程中並不與初始化Redis伺服器完全相同,如Sentinel不需要通過載入RDB檔案或AOF檔案還原資料庫狀態。

Sentinel與Redis普通伺服器在使用功能上的主要不同:

2.2 使用Sentinel專用程式碼

普通Redis伺服器使用redis.c/redisCommandTable作為伺服器命令表;
而Sentinel使用sentinel.c/sentinelcmds作為伺服器的命令表;

PINGSENTINELINFOSUBSCRIBEUNSUBSCRIBEPSUBSCRIBEPUNSUBSCRIBE這七個命令為客戶端可以對Sentinel執行的全部命令。

2.3 初始化Sentinel狀態

伺服器初始化一個sentinel.c/sentinelState結構,這個結構儲存了伺服器中所有和Sentinel功能有關的狀態(而伺服器的一般狀態由redis.h/redisServer結構儲存):

struct sentinelState {
    //當前紀元,用於實現故障轉移
    uint64_t current_epoch;

    //儲存所有被這個sentinel監視的主伺服器
    //字典的鍵時主伺服器的名字
    //字典的值則是一個指向sentinelRedisInstance結構的指標
    dict *masters;

    //是否進入TILT模式
    int tilt;

    //目前正在執行的指令碼數量
    int running_scripts;

    //進入TITL模式的時間
    mstime_t tilt_start_time;

    //最後一次執行時間處理器的時間
    mstime_t previous_time;

    //一個FIFO佇列,包含了所有需要執行的使用者指令碼
    list *scripts_queue;
} sentinel;

2.4 初始化Sentinel狀態的masters屬性

Sentinel狀態中的masters字典記錄了所有被Sentinel監視的主伺服器的相關資訊,其中:
· 字典的鍵為被監視主伺服器的名字;
· 字典的值為主伺服器對應的sentinel.c/sentinelRedisInstance結構,這個結構代表一個被Sentinel監視的Redis伺服器例項,這個例項可以是主伺服器、從伺服器或者另一個Sentinel。

下圖說明了一個masters字典結構:

下圖說明了一個sentinelRedisInstance結構:

· addr屬性是一個指向sentinel.c/sentinelAddr結構的指標,這個結構儲存著例項的IP地址和埠號。

2.5 建立連向主伺服器的網路連線

初始化Sentinel的最後一步是建立連向監視主伺服器的網路連線,Sentinel將成為主伺服器的客戶端,它可以向主伺服器傳送命令,並從命令回覆中獲取相關資訊。

對於每個Sentinel監視的主伺服器來說,Sentinel會建立兩個連向主伺服器的非同步網路連線
· 一個是命令連線
· 一個是訂閱連線,這個連線專門用於訂閱主伺服器的__sentinel__:hello頻道。這是因為Redis的釋出與訂閱功能中,被髮送的資訊不會儲存在Redis伺服器裡面,如果在資訊傳送時,想要接收資訊的客戶端不線上,則這個客戶端就會丟失這條資訊。因此,為了不丟失__sentinel__:hello 頻道的任何資訊,Sentinel專門用一個訂閱連線來接收該頻道的資訊。

下圖展示了一個Sentinel向它監視的兩個master建立網路連線的例項:

III、獲取主伺服器資訊

Sentinel預設會以每十秒一次的頻率,通過命令連線向被監視的主伺服器傳送INFO命令,並通過分析INFO命令的回覆獲取主伺服器的當前資訊。

通過分析主伺服器返回的回覆,Sentinel可以獲取兩方面的資訊:
· 一方面是關於主伺服器本身的資訊,包括run_id域記錄的伺服器執行ID,以及role域記錄的伺服器角色。
· 另一方面會得到主伺服器屬下的所有從伺服器的資訊(主要包括從伺服器的IP::port)。

主伺服器返回的從伺服器資訊,會被用於更新主伺服器例項結構的slaves字典,這個字典記錄了主伺服器屬下從伺服器的名單:

字典結構如下圖所示:

IV、獲取從伺服器資訊

Sentinel除了會為從伺服器建立相應的例項結構外,還會建立連線到從伺服器的命令連線和訂閱連線

接下來,Sentinel會以每十秒一次的頻率通過命令連線向從伺服器傳送INFO命令,並獲得相應的回覆,這些回覆包括:
· 從伺服器執行ID run_id;
· 從伺服器的角色 role;
· 主伺服器的IP地址master_host,以及主伺服器的埠號 master_port;
· 從伺服器的優先順序 slave_priority;
· 從伺服器的複製偏移量 slave_repl_offset;

根據以上資訊,Sentinel會對從伺服器的例項結構進行更新:

V、向主伺服器和從伺服器傳送資訊

預設情況下,Sentinel會以每兩秒一次的頻率,通過命令連線向所有被監視的主伺服器和從伺服器傳送命令:
PUBLISH __sentinel__:hello "<s_ip>,<s_port>,<s_runid>,<s_epoch>,<m_name>,<m_ip>,<m_port>,<m_epoch>"

這條命令向伺服器的__sentinel__:hello頻道傳送了一條資訊,資訊內容含義如下圖:

VI、接收來自主伺服器和從伺服器的頻道資訊

當Sentinel與一個主伺服器或從伺服器建立起訂閱連線之後,Sentinel會通過訂閱連線,向伺服器傳送以下命令:
SUBSCRIBE __sentinel__:hello
也就是Sentinel對所有伺服器的__sentinel__:hello頻道進行了訂閱,這個訂閱會一直持續到連線斷開。

也就是說,對於每個與Sentinel連線的伺服器,**Sentinel既通過命令連線向伺服器的__sentinel__:hello頻道傳送資訊,又通過訂閱連線從伺服器的sentinel__:hello頻道接受資訊:

可見,命令連線用於向伺服器傳送命令請求,而訂閱連線用於接收指定頻道的資訊。

舉個例子:
假設現在有sentinel1、sentinel2、sentinel3三個Sentinel在監視同一個伺服器,那麼當sentinel1向伺服器的__sentinel__:hello頻道傳送一條資訊時(使用命令連線),所有訂閱了__sentinel__:hello頻道的Sentinel都會收到這條資訊:

之後Sentinel會對收到的訂閱資訊進行分析,提取出V中說明的八個引數,並進行檢查:
· 如果資訊中的Sentinel執行ID與自己的執行ID相同,說明這條資訊是Sentinel自己傳送的,Sentinel將丟棄這條資訊,不做進一步處理。
· 如果不同,說明這條資訊是監視同一個伺服器的其他Sentinel發來的(這裡要理解訂閱資訊是由誰傳送的,訂閱資訊不是由伺服器自己產生的,而是有客戶端傳送到伺服器的,這裡的客戶端即是各個Sentinel),接受資訊的Sentinel將根據資訊中的各個引數,對相應主伺服器的例項結構進行更新。

6.1 更新sentinels字典

Sentinel為主伺服器建立的例項結構中的sentinels字典儲存了除Sentinel本身之外,同樣監視這個主伺服器的其他Sentinel的資料:

而這些資料是由於所有監視某個主伺服器的所有Sentinel都訂閱了主伺服器的__sentinel__:hello頻道,當Sentinel自己通過命令連線向主伺服器傳送如V中描述的PUBLISH命令時,會通過主伺服器的訂閱連線廣播給其他Sentinel。於是監視同一個主伺服器的多個Sentinel可以自動發現對方

6.2 建立連向其他Sentinel的命令連線

當Sentinel通過頻道資訊發現一個新的Sentinel時,不僅會為其在sentinels字典重建立例項,還會建立一個連向新Sentinel的命令連線,從而形成一個如下圖的結構:

Sentinel之間的命令連線用來進行資訊交換,實現如主觀下線檢測客觀下線檢測等功能。

VII、檢測主觀下線狀態

預設情況下,Sentinel會以每秒一次的頻率向與它建立命令連線的例項(包括主伺服器,從伺服器,其他Sentinel)傳送PING命令,並通過例項返回的PING命令回覆來判斷例項是否線上。

例項對PING命令的回覆可以分為一下兩種情況:
· 有效回覆: +PONG-LOADING-MASTERDOWN
· 無效回覆: 上面三種回覆之外的其他回覆,或在指定時限內沒有任何回覆。

如果一個例項在配置的down-after-milliseconds時間內,連續向Sentinel返回無效回覆,那麼Sentinel會修改這個例項對應的例項結構,在結構的flags屬性中開啟SRI_S_DOWN標識,以此來表示這個例項已進入主觀下線狀態

這裡對於每個Sentinel給相同伺服器的down-after-milliseconds配置可能是不同的,所以當一個Sentinel將主伺服器判斷為主觀下線時,其他Sentinel可能仍然會認為這個主伺服器處於線上狀態

VIII、 檢查客觀下線狀態

當Sentinel將一個主伺服器判斷為主觀下線後,為了確認這個主伺服器是否真的下線了,會向同樣監視這一主伺服器的其他Sentinel進行詢問,看它們是否也認為主伺服器已經進入了下線狀態(可能是主觀下線或者客觀下線)。當Sentinel從其他Sentinel得到足夠多數量的下線判斷後,Sentinel會將主伺服器判定為客觀下線,並對主伺服器執行故障轉移操作

Sentinel使用SENTINEL is-master-down-by-addr命令來與其他Sentinel互動,判斷主伺服器是否客觀下線,如果達到配置的客觀下線所需的數量時,Sentinel開啟主伺服器結構中的flags屬性的SRI_O_DOWN標識,標識主伺服器進入客觀下線狀態。

IX、選舉領頭Sentinel

當一個主伺服器被判斷為客觀下線後,檢測這個下線主伺服器的各個Sentinel會進行協商,選舉出一個領頭Sentinel,並由領頭Sentinel對下線伺服器執行故障轉移操作。

選取領頭Sentinel的規則和方法為:

X、故障轉移

領頭Sentinel對已下線的主伺服器進行故障轉移操作的步驟如下:
1、在已下線的主伺服器屬下的所有從伺服器裡面,挑選出一個從伺服器,並將其轉換為主伺服器;
2、讓已下線主伺服器屬下的所有從伺服器改為賦值新的主伺服器;
3、將已下線主伺服器設定為新的主伺服器的從伺服器。

10.1 選出新的主伺服器

1、新的主伺服器的挑選過程如下:

2、例項:
假設根據以上規則挑選出server2為新的主伺服器,則Sentinel向server2傳送SLAVEOF no one命令;

在傳送完SLAVEOF no one命令之後,領頭Sentinel以每秒一次的頻率(平時是每十秒一次)向server2傳送INFO命令,觀察其role資訊,當server2的role由slave變為master時,則server2順利升級為主伺服器。

10.2 修改從伺服器的複製目標

領頭Sentinel向server3和server4傳送SLAVEOF命令,令其複製新的主伺服器server2的內容:

形成如下結構:

10.3 將舊的主伺服器變為從伺服器

當server1重新上線時,Sentinel向其傳送SLAVEOF命令,讓其成為server2的從伺服器:

image.png

【參考】
[1] 《Redis設計與實現》



作者:wenmingxing
連結:https://www.jianshu.com/p/bd777dc24dc0
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯絡作者獲得授權並註明出處。