一.概述 Sentinel(哨崗或哨兵)是Redis的高可用解決方案:由一個或多個Sentinel實例(instance)組成的Sentinel系統(system)可以監視任意多個主伺服器,以及這些主伺服器屬下的所有從伺服器,併在被監視的主伺服器進入下線狀態時,自動將下線主伺服器屬下的某個從伺服器升 ...
一.概述
Sentinel(哨崗或哨兵)是Redis的高可用解決方案:由一個或多個Sentinel實例(instance)組成的Sentinel系統(system)可以監視任意多個主伺服器,以及這些主伺服器屬下的所有從伺服器,併在被監視的主伺服器進入下線狀態時,自動將下線主伺服器屬下的某個從伺服器升級為新的主伺服器,然後由新的主伺服器代替已下線的主伺服器繼續處理命令請求。
1.1 下麵是一個Sentinel系統與主從伺服器之間的關係:
(1) 雙環圖案的server1是當前的主伺服器,以及server2、server3、server4三個從伺服器。
(2) 三個從伺服器複製主伺服器的寫入命令數據,實現數據同步。
(3) Sentinel系統則監視所有四個伺服器。
1.2 Sentinel系統監視主伺服器下線
當主伺服器server1進入下線狀態,那麼三個從伺服器複製操作被中止,由Sentinel系統監視到了server1已下線,當server1的下線時長超過用戶設定的下線時長時,Sentinel系統就會對server1執行自動故障遷移。下麵是Sentinel系統監視到了server1主伺服器已下線:
只有具備故障轉移功能,才是一個高可用的解決方案。Sentinel系統自動執行故障轉移步驟:
(1) 首先Sentinel系統會挑選一個從伺服器,並將這個選中的從伺服器升級為新的主伺服器。
(2) 之後 Sentinel系統會讓所有從伺服器都開始複製新的主伺服器,故障轉移操作執行完畢。
(3) Sentinel系統還會繼續監視已下線的server1,併在它重新上線時,將它設置為從伺服器。
二. Sentinel伺服器初始化過程介紹
2.1 初始化一個普通的Redis伺服器
因為Sentinel伺服器本質上只是一個運行在特殊模式下的Redis伺服器,所以啟動Sentinel的第一步,就是初始化一個普通的Redis伺服器。初始化伺服器在介紹“伺服器章節”中講到過。由於工作不同,Sentinel的初始化過程和普通Redis伺服器的初始化過程並不完全相同。比如:Sentinel就不會載入RDB文件或者AOF文件。下麵表格展示Sentinel模式下運行時,伺服器各個主要功能使用情況:
功能 | 使用情況 |
資料庫和鍵值對方面的命令,如set,del,flushdb | 不使用 |
事務命令,如multi和watch | 不使用 |
腳本命令,如eval | 不使用 |
RDB持久化命令,如save和bgsave | 不使用 |
AOF 持久化命令,如bgrewriteaof | 不使用 |
複製命令,如slaveof |
Sentinel內部可以使用,但客戶端不可以使用 |
發佈與訂閱命令,如publish和subscribe | Subscribe,psubscribe,unsubscribe,punsubscribe四個命令在Sentinel內部和客戶端都可以使用,但publish命令只能在Sentinel內部使用 |
文件事件處理器,(負責發送命令請求,處理命令回覆) | Sentinel內部使用,但關聯的文件事件處理器與普通Redis伺服器不同 |
時間事件處理器,(負責執行serverCron函數) | Sentinel內部使用,serverCron函數會調用sentinel.c/sentinelTimer函數,後者包含了Sentinel要執行的所有操作 |
2.2 伺服器內部使用Sentinel專用代碼
啟動Sentinel的第二個步驟就是將一部分普通Redis伺服器使用的代碼替換成Sentinel專用代碼。比如:普通Redis伺服器埠6379,而Sentinel埠26379,即Sentinel.c/redis_sentinel_port常量。還有命令表也不一樣,對於Sentinel的客戶端就只有ping, sentinel,info, Subscribe,psubscribe,unsubscribe,punsubscribe 這7個命令,即Sentinel.c/sentinelcmds作為伺服器的命令表。
2.3 初始化Sentinel狀態
應用了專用代碼之後,步驟三是伺服器會初始化一個Sentinel.c/sentinelstate結構,這個結構保存了伺服器中所有和Sentinel功能有關的狀態,對於伺服器一般狀態還是由redis.h/redisServer結構保存。
2.4 初始化Sentinel狀態的masters屬性
sentinel狀態中的masters字典記錄了所有被sentinel監視的主伺服器的相關信息,其中字典的鍵是被監視的主伺服器名字,而字典的值是被監視主伺服器對應的sentinel.c/sentinelRedisInstance結構。每個sentinelRedisInstance實例結構代表監視一個Redis伺服器實例,這個實例可以是主伺服器,也可以是從伺服器,或者另外一個sentinel伺服器。對於sentinel狀態的初始化將引發對masters字典的初始化,而masters字典的初始化是根據被該入的sentinel配置文件(sentinel.conf)來進行的。
2.5 創建連向主伺服器的網路連接
初始化Sentinel的最後一步是創建連向被監聽主伺服器的網路連接,Sentinel將成為主伺服器的客戶端,它可以向主伺服器發送命令,並從命令回覆中獲取相關信息。對於被Sentinel監視的主伺服器來說,Sentinel會創建兩個連向主伺服器的非同步網路連接:
(1) 一是命令連接,專門用於向主伺服器發送命令,並接收命令回覆。
(2) 二是訂閱連接,專門用於訂閱主伺服器的_sentinel_:hello頻道。
下麵是一個Sentinel監視兩個主伺服器master1和master2,創建命令連接和訂閱連接圖:
三. 獲取伺服器信息
3.1 Sentinel獲取主伺服器信息
Sentinel預設會以每10秒一次的頻率,通過命令連接向主伺服器發送info命令,通過分析info命令的回覆來獲取主伺服器的當前信息,就像在上篇講到的複製功能,在客戶端輸入info replication 命令一樣,Sentinel可以獲取以下兩方面的信息:
(1) 關於主伺服器本身的信息,包括伺服器run_id,role的伺服器角色。
(2) 關於所有從伺服器的信息,每個從伺服器都由一個slave字元串開頭的行記錄,記錄了從伺服器IP和埠。
3.2 Sentinel獲取從伺服器信息
當Sentinel發現主伺服器有新的從伺服器出現時,Sentinel除了會為這個新的從伺服器創建相應的實例結構(sentinelRedisInstance)之外,Sentinel還會創建連接到從伺服器的命令連接和訂閱連接。Sentinel預設會以每10秒一次的頻率通過命令連接從伺服器發送info命令,通過分析info命令的回覆來獲取從伺服器的當前信息。包括:從伺服器運行run_ID、從伺服器角色role、主伺服器的ip及埠、主從伺服器的連接狀態master_link_status、從伺服器的優先順序slave_priority。
3.3 Sentinel向主從伺服器發送信息
在預設情況下, Sentinel會以每2秒一次的頻率,通過命令連接向,所有被監視的主伺服器和從伺服器發送以下格式的命令:
這條命令向伺服器的_sentinel_:hello頻道發送了一條信息,信息的內容由多個參數組成:
(1) 以s_開頭以參數記錄的是sentinel本身的信息。
(2) 而m_開頭的參數記錄的則是主伺服器的信息,如果sentinel正在監視的是主伺服器,那麼這些參數就是主伺服器的信息,如果sentinel正在監視的是從伺服器,那麼這些參數記錄就是從伺服器正在複製的主伺服器的信息。
參數 | 描述 |
S_ip | Sentinel的ip地址 |
S_port | Sentinel的埠號 |
S_runid | Sentinel的運行ID |
S_epoch |
Sentinel 的當前配置紀元 |
m_name | 主伺服器的名字 |
M_ip | 主伺服器的IP地址 |
M_port | 主伺服器的埠號 |
M_epoch | 主伺服器的當前配置紀元 |
以下是一條sentinel通過publish命令向主伺服器發送的信息示例:
這個示例中sentinel的ip地址為172.0.0.1埠號為26379, 運行ID為後面一串,當前紀元為0。主伺服器的名字為mymaster,ip地址為127.0.0.1,埠號為6379, 當前紀元為0。
3.4 sentinel接收來自主伺服器和從伺服器的頻道信息
當sentinel與一個主伺服器或者從伺服器建立起訂閱連接之後,Sentinel就會通過訂閱連接,向伺服器發送以下命令:subscribe_sentinel_:hello 。對於每個與Sentinel連接的伺服器,Sentinel既通過命令連向伺服器的_sentinel_:hello頻道發送信息,又通過訂閱連接從伺服器的_sentinel_:hello頻道接收信息。
當有三個sentinel,分別是sentinel1、sentinel2 、sentinel3。三個sentinel在監視同一個伺服器,那麼當sentinel1向伺服器的_sentinel_:hello頻道發送一條信息時,所有訂閱了_sentinel_:hello頻道的sentinel(包括sentinel1自己在內)都會收到這條信息。
當一個sentinel從_sentinel_:hello頻道收到一條信息時,sentinel會對這條信息進行分析,提取出信息中sentinel 的 ip 、port、runID等8個參數,併進行以下檢查:
(1) 如果信息中記錄的sentinel運行ID和接收信息的sentinel運行ID相同,那麼說明這條信息是sentinel自己發送的,sentinel將丟棄這條信息,不做進一步處理。
(2) 相反地,如果信息中記錄的sentinel運行ID和接收信息的sentinel運行ID不相同,那說明這條信息監視同一個伺服器的其它sentinel發來的,接收信息的sentinel將根據信息中的參數,對相應主伺服器的實例結構進行更新。
3.5 sentinel更新自己的sentinels字典
sentinel為主伺服器創建實例結構中的sentinels字典,保存了sentinel本身,還監視這個主伺服器的其他sentinel的資料。當一個sentinel接收到其他sentinels發來的信息時,接收的sentinel會從信息中分析並提取出兩方面參數:
(1)與sentinel有關的參數,包括sentinel的ip、port、runid、配置紀元。
(2)與主伺服器有關的參數, 包括監視主伺服器的ip、port、runid、配置紀元。
假設分別有三個sentinel: 127.0.0.1:26379、127.0.0.1:26380、127.0.0.1:26381。三個sentinel正在監視主伺服器127.0.0.1:6379, 那麼當127.0.0.1:26379這個sentinel接收到以下消息時:
這個sentinel將執行以下動作:
(1) 第一條信息發送者為自己,信息忽略。
(2) 第二條信息發送者為26381, sentinel會根據信息提取出內容,對sentinels字典中26381對應的實例結構進行更新。
(3) 第三條信息發送者為23680,同樣更新字典中的23680對應的實例結構。
每個sentinel都有自己的一個sentinels字典, 對於26379的sentinel它的sentinels字典信息保存了26380和26381兩個sentinel信息。其它sentinel也一樣。
3.6 sentinel創建連向其他sentinel的命令連接
當sentinel通過頻道信息發現一個新的sentinel時,不僅更新sentinels字典,還會創建一個連向sentinel命令連接,而新的sentinel也會創建連向這個sentinel的命令連接,最終監視同一個主伺服器的多個sentinel將形成相互連接的網路。如下圖所示: