1. 程式人生 > 其它 >《Redis設計與實現》讀書筆記(二十六) ——Redis哨兵(sentinel)啟動與建立監聽機制

《Redis設計與實現》讀書筆記(二十六) ——Redis哨兵(sentinel)啟動與建立監聽機制

《Redis設計與實現》讀書筆記(二十六) ——Redis哨兵(sentinel)啟動與建立監聽機制

(原創內容,轉載請註明來源,謝謝)

一、概述

哨兵(Sentinel)是redis高可用性的解決方案,由一個或多個哨兵例項組成的哨兵系統,可以監視任意多個主伺服器,以及這些主伺服器屬下的從伺服器。

當被監視的主伺服器下線時,根據某些規則挑選一個從伺服器,作為新的主伺服器。接著,其他從伺服器會向新的主伺服器傳送複製指令,並且完成複製。同時,哨兵會監視下線的原主伺服器,在它重新上線後,將它也置為從伺服器。

二、哨兵啟動與初始化

1、啟動命令

有兩個命令,效果完全相同:

redis-sentinel /path/to/sentinel/config/sentinel.conf
或 redis-server/path/to/sentinel/config/sentinel.conf --sentinel

啟動後,會做五件事:初始化伺服器、將普通redis使用的程式碼替換成sentinel專用的程式碼、初始化sentinel狀態、根據給定的配置檔案去初始化監視的主伺服器列表、建立連向主伺服器的網路連線。

2、初始化伺服器

由於sentinel伺服器是特殊的redis伺服器,因此其一開始的初始化伺服器和redis普通的伺服器初始化大體一致。區別在於sentinel不會載入rdb、aof檔案。

3、使用sentinel專用程式碼

在載入常量、命令表時,會載入sentinel專用的內容。例如sentinel預設的埠是26379,命令表只有七個命令可以執行,包括ping、info、sentinel以及釋出訂閱相關的四個命令(不含publish命令),而且這七個命令的定義和普通的redis伺服器也有所不同。

因此,客戶端無法對sentinel伺服器請求redis普通伺服器的命令,如get、set等。

4、初始化sentinel狀態

sentinel狀態會初始化sentinelState結構體,這裡面儲存了sentinel特有的屬性,普通的屬性仍會初始化在redisServer結構體中。

struct sentinelState{
//當前紀元,用於實現故障轉移
uint64_t current_epoch;
//字典形式儲存這個sentinel監視的所有主伺服器,鍵是伺服器名稱,值是指向sentinelRedisInstance指標
dict *masters;
//是否進入TILT模式
int titl;
//目前正在執行的指令碼數量
int running_scripts;
//進入TILT模式的時間
mstime_t tilt_start_time;
//最後一次執行時間處理器的時間
mstime_t previous_time;
//FIFO佇列,包含所有要執行的指令碼
list *scripts_queue;
}sentinel;

上述即sentinelState結構體中的全部屬性。

5、初始化sentinelState中的master屬性

1)sentinelRedisInstance

master屬性是字典形式儲存這個sentinel監視的所有主伺服器,鍵是伺服器名稱,值是指向sentinelRedisInstance指標。

每個sentinelRedisInstance結構(例項結構)是一個被sentinel監視的主伺服器、從伺服器或其他sentinel。

例項結構屬性非常多,主要的屬性:name表示伺服器名稱,從伺服器和其他sentinel名稱採用“ip:埠”的方式命名,主伺服器由sentinel來自動命名;runid表示執行id;addr記錄例項的地址;down_after_period記錄主觀下線時間;quorum表示接收到多少個投票後,伺服器算是客觀下線。

其中,addr的地址,是一個結構體,包括字元陣列形式的ip以及int形式的埠號。

2)初始化

對sentinel的初始化會引起對masters字典的初始化,而masters字典的初始化,由載入的sentinel的配置檔案決定。

例如如下配置檔案:

則masters字典如下圖所示:

其中的每一個sentinelRedisInstance如下圖所示:

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

初始化的最後一步,即建立連向主伺服器的網路連線。sentinel將成為主伺服器的客戶端,對它傳送命令,並獲取有關的回覆。

sentinel會和每一個主伺服器都建立兩個連線,一個是命令連線,專門用於向主伺服器傳送命令與接收主伺服器的回覆;另一個是訂閱連線,專門用於訂閱主伺服器的__sentinel__:hello頻道。

由於釋出訂閱時候,資訊都不會儲存在redis伺服器,為了保證儲存hello頻道的每一條資訊,必須專門有一個訂閱的連線。另外,除了訂閱頻道外,sentinel必須要能給主伺服器傳送命令,以此來與主伺服器通訊,因此命令連線也是必不可少的。

三、獲取主伺服器資訊

sentinel預設會每10秒一次,給主伺服器傳送info命令,並且通過分析info命令,來判斷當前主伺服器的資訊。

通過分析info命令,sentinel會得到兩方面的資訊:一是主伺服器的執行ID以及其role域記錄的伺服器角色,二是關於主伺服器屬下所有從伺服器的資訊,由slave字串開頭,每行IP記錄一個IP地址、埠號、offset(用於與主伺服器aof)、lag(延遲時間)等。

因此,sentinel無序建立和從伺服器的連線,也可以知道從伺服器的情況。sentinel只需要將info獲得的返回結果,分析並更新到sentinel的sentinelState結構體的相應屬性即可。

另外,sentinel在更新結構體時,還會分析每一個從伺服器是否存在,如果是現有的則更新結構體,如果不是現有的則新增一個結構體。

從上述可知,主從伺服器sentinelRedisInstance的主要區別,一是flags屬性主是SRI_MASTER而從是SRI_SLAVE;二是名稱主伺服器是sentinel起的,從伺服器是IP:埠號;三是主伺服器有個slaves屬性,指向一個字典,該字典鍵是從伺服器的IP:埠號,值是表示從伺服器的sentinelRedisInstance。

四、獲取從伺服器資訊

當sentinel發現有新的從伺服器,除了會為其建立例項結構,還會與其建立連線,連線也是包括訂閱連線和命令連線。且預設下,每十秒也會給從伺服器傳送info命令。

根據info命令,可以提取出以下主要資訊:執行ID、角色、主伺服器ip與埠、主從連線狀態、從伺服器優先順序、從伺服器偏移量。

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

預設情況下,sentinel會每兩秒一次,向監聽的主從伺服器傳送以下格式的命令:

publish __sentinel__:hello “<s_ip>,<s_port>,<s_runid>,<s_epoch>,<m_name>,<m_ip>,<m_port>,<m_epoch>”

其中,s開頭的是監聽的伺服器相關的資訊,m開頭的是sentinel的資訊。

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

1、接收資訊

上面已經提到,sentinel會和每個監聽的伺服器建立釋出訂閱連線,監聽__sentinel__:hello頻道,會一直監聽到連線斷開為止。

採用釋出訂閱的方式,是因為如果不止一個sentinel監聽該主從結構的各伺服器,則當其中某一個sentinel傳送上述第五步的publish的命令,伺服器回覆在頻道資訊的,可以被所有監聽的sentinel獲取到。

sentinel接收到資訊將與sentinel的執行id進行比對,如果一致則表示資訊是自身傳送的,sentinel將丟棄不處理資訊;如果不一致表示是其他sentinel傳送的命令,則會進行比對並更新相應內容。

2、更新sentinels字典

sentinel為主伺服器建立的sentinels字典除了儲存自身資訊,還會儲存其他sentinel的資訊,字典的鍵是sentinel的ip:port,值是對應的sentinel例項。

獲取的結果中,如果已經存在名稱,則是原來就有是sentinel,否則是新的sentinel則要新開一個空間進行儲存。

整體的儲存方式和儲存主從伺服器的方式基本一致。

3、建立連向其他sentinel的連線

當sentinel發現其他sentinel,不僅會記錄其結構資訊,還會建立一個命令連線,而新的sentinel也會向該sentinel建立命令連線。最終監視主伺服器的多個sentinel將形成網路。

另外,sentinel不會和其他sentinel建立訂閱連線,因為不需要訂閱相關資訊。sentinel之間傳送命令和訂閱資訊,只需要互相用命令連線傳送即可。

——written by linhxx 2017.09.13