天天看點

Redis 的 Sentinel 系統

介紹 Redis 的 Sentinel 系統

技術是為了解決問題而生的,Redis 的 Sentinel 系統實作了 Redis 主從伺服器的自動切換。

Sentinel 是 Redis 的高可用性解決方案:由一個或多個 Sentinel 執行個體組成的 Sentinel 系統可以監視任意多個主伺服器,以及這些主伺服器屬下的所有從伺服器,并在被監視的主伺服器進入下線狀态時,自動将下線主伺服器屬下的某個從伺服器更新為新的主伺服器,然後由新的主伺服器代替已下線的主伺服器繼續處理指令請求。

Sentinel 系統監視伺服器的原理

Sentinel 和一般 Redis 伺服器的差別:Sentinel 本質上隻是一個運作在特殊模式下的 Redis 伺服器。

Sentinel 會讀入使用者指定的配置檔案,為每個要被監視的主伺服器建立相應的執行個體結構,并建立連向主伺服器的指令連接配接和訂閱連接配接:

  • 指令連接配接用于,向主伺服器發送指令請求;
  • 訂閱連接配接用于,接收指定頻道的消息。

Sentinel 通過向主伺服器發送 info 指令來獲得主伺服器屬下所有從伺服器的位址資訊,并為這些從伺服器建立相應的執行個體結構,以及連向這些從伺服器的指令連接配接和訂閱連接配接。

在一般情況下,Sentinel 以每十秒一次的頻率向被監視的主伺服器和從伺服器發送 info 指令。當主伺服器處于下線狀态,或者 Sentinel 正在對主伺服器進行故障轉移操作時,Sentinel 向從伺服器發送 info 指令的頻率會改為每秒一次。

對于監視同一個主伺服器和從伺服器的多個 Sentinel 來說,它們會以每兩秒一次的頻率,通過向被監視伺服器的 __ sentinel __:hello 頻道發送消息來向其他 Sentinel 宣告自己的存在。

每個 Sentinel 也會從 __ sentinel __:hello 頻道中接收其他 Sentinel 發來的資訊,并根據這些資訊為其他 Sentinel 建立相應的執行個體結構,以及指令連接配接。

Sentinel 隻會與主伺服器和從伺服器建立指令連接配接和訂閱連接配接, Sentinel 與 Sentinel 之間則隻建立指令連接配接。

Sentinel 系統對主伺服器執行故障轉移的整個過程

在預設情況下,Sentinel 會以每秒一次的頻率向所有與它建立了指令連接配接的執行個體(包括主伺服器、從伺服器、其他 Sentinel 在内)發送 ping 指令,并根據執行個體對 ping 指令的回複來判斷執行個體是否線上,當一個執行個體在指定的時長中連續向 Sentinel 發送無效回複時,Sentinel 會将這個執行個體判斷為主觀下線。

當 Sentinel 将一個主伺服器判斷為主觀下線時,它會向同樣監視這個主伺服器的其他 Sentinel 進行詢問,看它們是否同意這個主伺服器已經進入主觀下線狀态。當 Sentinel 收集到足夠多的主觀下線投票之後,它會将主伺服器判斷為客觀下線。

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

在選舉産生出領頭 Sentinel 之後,領頭 Sentinel 将對已下線的主伺服器執行故障轉移操作,該操作包含以下三個步驟:

  1. 選出新的主伺服器:在已下線主伺服器屬下的所有從伺服器裡面,挑選出一個從伺服器,并将其轉換為主伺服器。
  2. 修改從伺服器的複制目标:讓已下線主伺服器屬下的所有從伺服器改為複制新的主伺服器。
  3. 将舊的主伺服器變為從伺服器:将已下線主伺服器設定為新的主伺服器的從伺服器,當這個舊的主伺服器重新上線時,它就會成為新的主伺服器的從伺服器。

1、檢測伺服器的下線狀态

在預設情況下,Sentinel 會以每秒一次的頻率向所有與它建立了指令連接配接的執行個體(包括主伺服器、從伺服器、其他 Sentinel 在内)發送 ping 指令,并根據執行個體對 ping 指令的回複來判斷執行個體是否線上,當一個執行個體在指定的時長中連續向 Sentinel 發送無效回複時,Sentinel 會将這個執行個體判斷為主觀下線。

當 Sentinel 将一個主伺服器判斷為主觀下線時,它會向同樣監視這個主伺服器的其他 Sentinel 進行詢問,看它們是否同意這個主伺服器已經進入主觀下線狀态。

當 Sentinel 收集到足夠多的主觀下線投票之後,它會将主伺服器判斷為客觀下線,并發起一次針對主伺服器的故障轉移操作。

執行個體對 ping 指令的回複可以分為以下兩種情況:

  • 有效回複:執行個體傳回 +pong、-loading、-masterdown 三種回複的其中一種。
  • 無效回複:執行個體傳回除 +pong、-loading、-masterdown 三種回複之外的其他回複,或者在指定時限内沒有傳回任何回複。

1、檢測主觀下線狀态

down-after-milliseconds 選項(主觀下線時長 選項)

Sentinel 配置檔案中的 down-after-milliseconds 選項指定了 Sentinel 判斷執行個體進入主觀下線所需的時間長度。

如果一個執行個體在 down-after-milliseconds 毫秒内,連續向 Sentinel 傳回無效回複,那麼 Sentinel 就會将該執行個體标記為主觀下線。修改這個執行個體所對應的執行個體結構,在結構的 flags 屬性中打開 SRI_S_DOWN 辨別,以此來表示這個執行個體已經進入主觀下線狀态。

down-after-milliseconds 選項的作用範圍

使用者設定的 down-after-milliseconds 選項的值,不僅會被 Sentinel 用來判斷主伺服器的主觀下線狀态,還會被用于判斷主伺服器屬下的所有從伺服器,以及所有同樣監視這個主伺服器的其他 Sentinel 的主觀下線狀态。

2、檢測客觀下線狀态

當 Sentinel 将一個主伺服器判斷為主觀下線之後,為了确認這個主伺服器是否真的下線了,它會向同樣監視這個主伺服器的其他 Sentinel 進行詢問,看它們是否也認為主伺服器已經進入了下線狀态(可以是主觀下線或者客觀下線)。

當 Sentinel 從其他 Sentinel 那裡接收到足夠數量的已下線判斷之後,Sentinel 就會将主伺服器判定為客觀下線,并對主伺服器執行故障轉移操作。

客觀下線狀态的判斷條件:當認為主伺服器已經進入下線狀态的 Sentinel 的數量,超過 Sentinel 配置中設定的 quorum 參數的值,那麼該 Sentinel 就會認為主伺服器已經進入客觀下線狀态。

檢測客觀下線的具體流程:

源 Sentinel 發送指令:Sentinel 使用

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

指令詢問其他 Sentinel 是否同意主伺服器已下線。

目标 Sentinel 接收指令:當一個 Sentinel(目标 Sentinel)接收到另一個 Sentinel(源 Sentinel) 發來的該指令時,目标 Sentinel 會分析并取出指令請求中包含的各個參數,并根據其中的主伺服器 IP 和端口号,檢查主伺服器是否已下線,然後向源 Sentinel 傳回一條包含三個參數的 MultiBulk 回複作為該指令的回複。

  • down_state:傳回目标 Sentinel 對主伺服器的檢查結果,1 代表主伺服器已下線,0 代表主伺服器未下線
  • leader_runid:可以是 * 符号或者目标 Sentinel 的局部領頭 Sentinel 的運作 ID。*符号代表指令僅僅用于檢測主伺服器的下線狀态,而局部領頭 Sentinel 的運作 ID 則用于選舉領頭 Sentinel
  • leader_epoch:目标 Sentinel 的局部領頭 Sentinel 的配置紀元,用于選舉領頭 Sentinel。僅在 leader_ runid 的值不為 * 時有效,如果 leader_ runid 的值為 *,那麼 leader_ epoch 總為 0

源 Sentinel 接收指令的回複:根據其他 Sentinel 對該指令的回複,Sentinel 将統計其他 Sentinel 同意主伺服器已下線的數量,當這一數量達到配置指定的判斷客觀下線所需的數量時,Sentinel 就會将該執行個體标記為客觀下線。修改這個執行個體所對應的執行個體結構,在結構的 flags 屬性中打開 SRI_O_DOWN 辨別,以此來表示這個執行個體已經進入客觀下線狀态。

Redis 檢測客觀下線狀态 / 選舉領頭 Sentinel 的指令:

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

  • 參數 ip:被 Sentinel 判斷為主觀下線的主伺服器的 IP 位址
  • 參數 port:被 Sentinel 判斷為主觀下線的主伺服器的端口号
  • 參數 current_epoch:Sentinel 目前的配置紀元,用于選舉領頭 Sentinel
  • 參數 runid:該參數的值可以是 * 符号或者 Sentinel 的運作 ID。* 符号代表指令僅僅用于檢測主伺服器的客觀下線狀态,而 Sentinel 的運作 ID 則用于選舉領頭 Sentinel

2、選舉領頭 Sentinel

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

Sentinel 系統選舉領頭 Sentinel 的方法是對 Raft 算法的領頭選舉方法的實作。

選舉領頭 Sentinel 的規則

Redis 選舉領頭 Sentinel 的規則:過半數原則

每個發現主伺服器進入客觀下線的 Sentinel 都會要求其他 Sentinel 将自己設定為局部領頭 Sentinel。在一個配置紀元裡面,所有 Sentinel 都隻有一次将某個 Sentinel 設定為局部領頭 Sentinel 的機會,并且局部領頭一旦設定,在這個配置紀元裡面就不能再更改。

配置紀元實際上就是一個計數器,并沒有什麼特别的。

每次進行領頭 Sentinel 選舉之後,不論選舉是否成功,所有 Sentinel 的配置紀元(configuration epoch)的值都會自增一次。

Sentinel 設定局部領頭 Sentinel 的規則是先到先得:最先向目标 Sentinel 發送設定要求的源 Sentinel 将成為目标 Sentinel 的局部領頭 Sentinel,而之後接收到的所有設定要求都會被目标 Sentinel 拒絕。

  • 如果有某個 Sentinel 被半數以上的 Sentinel 設定成了局部領頭 Sentinel,那麼這個 Sentinel 成為領頭 Sentinel。
  • 如果在給定時限内,沒有一個 Sentinel 被選舉為領頭 Sentinel,那麼各個 Sentinel 将在一段時間之後再次進行選舉,直到選出領頭 Sentinel 為止。

因為領頭 Sentinel 的産生需要半數以上 Sentinel 的支援,并且每個 Sentinel 在每個配置紀元裡面隻能設定一次局部領頭 Sentinel,是以在一個配置紀元裡面,隻會出現一個領頭 Sentinel。

選舉領頭 Sentinel 的指令

Redis 檢測客觀下線狀态 / 選舉領頭 Sentinel 的指令:

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

  • 參數 ip:被 Sentinel 判斷為主觀下線的主伺服器的 IP 位址
  • 參數 port:被 Sentinel 判斷為主觀下線的主伺服器的端口号
  • 參數 current_epoch:Sentinel 目前的配置紀元,用于選舉領頭 Sentinel
  • 參數 runid:該參數的值可以是 * 符号或者 Sentinel 的運作 ID。* 符号代表指令僅僅用于檢測主伺服器的客觀下線狀态,而 Sentinel 的運作 ID 則用于選舉領頭 Sentinel

源 Sentinel 發送指令:當一個 Sentinel(源 Sentinel)向另一個 Sentinel(目标 Sentinel)發送該指令,并且指令中的 runid 參數不是 * 符号而是源 Sentinel 的運作 ID 時,這表示源 Sentinel 要求目标 Sentinel 将前者設定為後者的局部領頭 Sentinel。

目标 Sentinel 接收指令:目标 Sentinel 在接收到

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

之後,将向源 Sentinel 傳回一條指令回複,回複中的 leader_runid 參數和 leader_epoch 參數分别記錄了目标 Sentinel 的局部領頭 Sentinel 的運作 ID 和 配置紀元。

**源 Sentinel 接收指令的回複:**源 Sentinel 在接收到目标 Sentinel 傳回的指令回複之後,會檢查回複中 leader_epoch 參數的值和自己的配置紀元是否相同,如果相同的話,那麼源 Sentinel 繼續取出回複中的 leader_runid 參數,如果 leader_runid 參數的值和源 Sentinel 的運作 ID 一緻,那麼表示目标 Sentinel 将源 Sentinel 設定成了局部領頭 Sentinel。

3、故障轉移

在選舉産生出領頭 Sentinel 之後,領頭 Sentinel 将對已下線的主伺服器執行故障轉移操作,該操作包含以下三個步驟:

  1. 選出新的主伺服器:在已下線主伺服器屬下的所有從伺服器裡面,挑選出一個從伺服器,并将其轉換為主伺服器。
  2. 修改從伺服器的複制目标:讓已下線主伺服器屬下的所有從伺服器改為複制新的主伺服器。
  3. 将舊的主伺服器變為從伺服器:将已下線主伺服器設定為新的主伺服器的從伺服器,當這個舊的主伺服器重新上線時,它就會成為新的主伺服器的從伺服器。

1、選出新的主伺服器

故障轉移操作第一步要做的就是在已下線主伺服器屬下的所有從伺服器中,挑選出一個狀态良好、資料完整的從伺服器,然後向這個從伺服器發送 slaveof no one 指令,将這個從伺服器轉換為主伺服器。

新的主伺服器是怎樣挑選出來的

領頭 Sentinel 會将已下線主伺服器的所有從伺服器儲存到一個清單裡面,然後按照以下規則,一項一項地對清單進行過濾:

  1. 根據 線上狀态 過濾:删除清單中所有處于下線或者斷線狀态的從伺服器,這可以保證清單中剩餘的從伺服器都是正常線上的。
  2. 根據 通信情況 過濾:删除清單中所有最近五秒内沒有回複過領頭 Sentinel 的 info 指令的從伺服器,這可以保證清單中剩餘的從伺服器都是最近成功進行過通信的。
  3. 根據 斷連時間 過濾:删除所有與已下線主伺服器連接配接斷開超過 down-after- milliseconds * 10 毫秒的從伺服器:down-after-milliseconds 選項指定了判斷主伺服器下線所需的時間,而删除斷開時長超過 down-after- milliseconds * 10 毫秒的從伺服器,則可以保證清單中剩餘的從伺服器都沒有過早地與主伺服器斷開連接配接,換句話說,清單中剩餘的從伺服器儲存的資料都是比較新的。
  4. 根據 從庫的優先級 篩選:之後,領頭 Sentinel 将根據從伺服器的優先級,對清單中剩餘的從伺服器進行排序,并選出其中優先級最高的從伺服器。
  5. 根據 複制偏移量 篩選:如果有多個具有相同最高優先級的從伺服器,那麼領頭 Sentinel 将按照從伺服器的複制偏移量,對具有相同最高優先級的所有從伺服器進行排序,并選出其中偏移量最大的從伺服器(複制偏移量最大的從伺服器就是儲存着最新資料的從伺服器)。
  6. 根據 運作 ID 篩選:最後,如果有多個優先級最高、複制偏移量最大的從伺服器,那麼領頭 Sentinel 将按照運作 ID 對這些從伺服器進行排序,并選出其中運作 ID 最小的從伺服器。

2、修改從伺服器的複制目标

當新的主伺服器出現之後,領頭 Sentinel 下一步要做的就是,讓已下線主伺服器屬下的所有從伺服器去複制新的主伺服器,這一動作可以通過向從伺服器發送 slaveof 指令來實作。

3、将舊的主伺服器變為從伺服器

故障轉移操作最後要做的是,将已下線的主伺服器設定為新的主伺服器的從伺服器。

因為舊的主伺服器已經下線,是以這種設定是儲存在伺服器對應的執行個體結構裡面的,當伺服器重新上線時,Sentinel 就會向它發送 slaveof 指令,讓它成為新主伺服器的從伺服器。

總結 Sentinel 系統中的周期指令

在 Sentinel 系統中,有很多地方都會以一定的頻率向指定的伺服器發送指令,下面對所有周期指令進行總結。

參考資料

繼續閱讀