sentinel(哨兵)是redis的高可用性(high availability)解決方案:由一個或多個sentinel執行個體組成的Sentinel系統,監視任意多個主伺服器及這些主伺服器下的從伺服器,并在被監視的主伺服器進入下線狀态時,自動将下線主伺服器屬下的某個從伺服器更新為新的主伺服器,代替已下線的主伺服器繼續處理指令請求。
下圖是Sentinel系統監視伺服器的例子:
![]()
redis哨兵(sentinel)系統1. 啟動并初始化Sentinel2. 擷取主伺服器資訊3. 擷取從伺服器資訊4. 向主伺服器和從伺服器發送消息5. 接收來自主伺服器和從伺服器的頻道資訊6. 檢測主觀下線狀态7. 檢測客觀下線狀态8. 選舉領頭sentinel9. 故障轉移
1. 啟動并初始化Sentinel
參考:http://redisbook.com/preview/sentinel/init_sentinel.html
啟動一個Sentinel的指令:
redis> redis-sentinel /path/to/your/sentinel.conf
或者
這兩個效果完全相同。當啟動一個sentinel時,需要執行以下步驟:
1. 初始化伺服器
2. 将普通redis伺服器使用的代碼替換成sentinel專用代碼
3. 初始化sentinel狀态
4. 根據配置檔案,初始化sentinel的監視主伺服器清單
5. 建立連向主伺服器的網絡連接配接
1.1 初始化伺服器
Sentinel 本質上是一個運作在特殊模式下的redis伺服器,是以啟動Sentinel的第一步(類似)初始化一個普通的redis伺服器。
因為 Sentinel 執行的工作和普通 Redis 伺服器執行的工作不同, 是以 Sentinel 的初始化過程和普通 Redis 伺服器的初始化過程并不完全相同。
普通伺服器在初始化時會通過載入 RDB 檔案或者 AOF 檔案來還原資料庫狀态, 但是因為 Sentinel 并不使用資料庫, 是以初始化 Sentinel 時就不會載入 RDB 檔案或者 AOF 檔案。
Sentinel 模式下 Redis 伺服器主要功能的使用情況
功能 | 使用情況 |
---|---|
資料庫和鍵值對方面的指令, 比如 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 函數, serverCron 函數會調用 sentinel.c/sentinelTimer 函數, 後者包含了 Sentinel 要執行的所有操作。 |
1.2 使用 Sentinel 專用代碼
将一部分普通 Redis 伺服器使用的代碼替換成 Sentinel 專用代碼
(1)端口
普通 Redis 伺服器使用 redis.h/REDIS_SERVERPORT 常量的值作為伺服器端口:
而 Sentinel 則使用 sentinel.c/REDIS_SENTINEL_PORT 常量的值作為伺服器端口:
(2)指令表
普通 Redis 伺服器使用 redis.c/redisCommandTable 作為伺服器的指令表:
struct redisCommand redisCommandTable[] = {
{"get",getCommand,,"r",,NULL,,,,,},
{"set",setCommand,-,"wm",,noPreloadGetKeys,,,,,},
{"setnx",setnxCommand,,"wm",,noPreloadGetKeys,,,,,},
// ...
{"script",scriptCommand,-,"ras",,NULL,,,,,},
{"time",timeCommand,,"rR",,NULL,,,,,},
{"bitop",bitopCommand,-,"wm",,NULL,,-,,,},
{"bitcount",bitcountCommand,-,"r",,NULL,,,,,}
}
而 Sentinel 則使用 sentinel.c/sentinelcmds 作為伺服器的指令表, 并且其中的 INFO 指令會使用 Sentinel 模式下的專用實作 sentinel.c/sentinelInfoCommand 函數, 而不是普通 Redis 伺服器使用的實作 redis.c/infoCommand 函數:
struct redisCommand sentinelcmds[] = {
{"ping",pingCommand,,"",,NULL,,,,,},
{"sentinel",sentinelCommand,-,"",,NULL,,,,,},
{"subscribe",subscribeCommand,-,"",,NULL,,,,,},
{"unsubscribe",unsubscribeCommand,-,"",,NULL,,,,,},
{"psubscribe",psubscribeCommand,-,"",,NULL,,,,,},
{"punsubscribe",punsubscribeCommand,-,"",,NULL,,,,,},
{"info",sentinelInfoCommand,-,"",,NULL,,,,,}
};
1.3 初始化 Sentinel 狀态
伺服器會初始化一個 sentinel.c/sentinelState 結構(“Sentinel 狀态”), 儲存了伺服器中所有和 Sentinel 功能有關的狀态 (伺服器的一般狀态仍然由 redis.h/redisServer 結構儲存):
struct sentinelState {
// 目前紀元,用于實作故障轉移
uint64_t current_epoch;
// 儲存了所有被這個 sentinel 監視的主伺服器的相關資訊
// 字典的鍵是主伺服器的名字
// 字典的值則是一個指向 sentinelRedisInstance 結構的指針
dict *masters;
// 是否進入了 TILT 模式?
int tilt;
// 目前正在執行的腳本的數量
int running_scripts;
// 進入 TILT 模式的時間
mstime_t tilt_start_time;
// 最後一次執行時間處理器的時間
mstime_t previous_time;
// 一個 FIFO 隊列,包含了所有需要執行的使用者腳本
list *scripts_queue;
} sentinel;
1.4 初始化 Sentinel 狀态的 masters 屬性
Sentinel 狀态中的 masters 字典記錄了所有被 Sentinel 監視的主伺服器的相關資訊,是根據被載入的 Sentinel 配置檔案對其進行初始化。
1. 字典的鍵是被監視主伺服器的名字。
2. 字典的值則是被監視主伺服器對應的 sentinel.c/sentinelRedisInstance 結構。
typedef struct sentinelRedisInstance {
// 辨別值,記錄了執行個體的類型,以及該執行個體的目前狀态
int flags;
// 執行個體的名字
// 主伺服器的名字由使用者在配置檔案中設定
// 從伺服器以及 Sentinel 的名字由 Sentinel 自動設定
// 格式為 ip:port ,例如 "127.0.0.1:26379"
char *name;
// 執行個體的運作 ID
char *runid;
// 配置紀元,用于實作故障轉移
uint64_t config_epoch;
// 執行個體的位址:ip位址和port
sentinelAddr *addr;
// SENTINEL down-after-milliseconds 選項設定的值
// 執行個體無響應多少毫秒之後才會被判斷為主觀下線(subjectively down)
mstime_t down_after_period;
// SENTINEL monitor <master-name> <IP> <port> <quorum> 選項中的 quorum 參數
// 判斷這個執行個體為客觀下線(objectively down)所需的支援投票數量
int quorum;
// SENTINEL parallel-syncs <master-name> <number> 選項的值
// 在執行故障轉移操作時,可以同時對新的主伺服器進行同步的從伺服器數量
int parallel_syncs;
// SENTINEL failover-timeout <master-name> <ms> 選項的值
// 重新整理故障遷移狀态的最大時限
mstime_t failover_timeout;
// ...
} sentinelRedisInstance;
1.5 建立連向主伺服器的網絡連接配接
對于每個被 Sentinel 監視的主伺服器來說, Sentinel 會建立兩個連向主伺服器的異步網絡連接配接:
- 一個指令連接配接, Sentinel 将成為主伺服器的用戶端, 這個連接配接專門用于向主伺服器發送指令, 并接收指令回複。
- 一個訂閱連接配接, 這個連接配接專門用于訂閱主伺服器的 __sentinel__:hello 頻道。
2. 擷取主伺服器資訊
Sentinel預設會每十秒一次的頻率,通過指令連接配接向被監視的主伺服器發送 INFO指令
。
通過分析INFO指令的回複來擷取主伺服器的目前資訊:
1. 主伺服器本身的資訊
2. 主伺服器屬下所有從伺服器的資訊
3. 擷取從伺服器資訊
當sentinel發現主伺服器有新的從伺服器時,sentinel①為這個從伺服器建立相應的執行個體結構;②建立連接配接到從伺服器的指令連接配接和訂閱連接配接。
4. 向主伺服器和從伺服器發送消息
sentinel會每2秒一次的頻率,通過指令連接配接向所有被監視的(主從)伺服器的_sentinel_:hello頻道發送消息:
PUBLISH _sentinel_:hello "<s_ip>,<s_port>,<s_runid>,<s_epoch>,<m_name>,<m_ip>,<m_epoch>"
1. 以s_開頭的參數記錄sentinel本身的資訊
2. 以m_開頭的參數記錄主伺服器的資訊
5. 接收來自主伺服器和從伺服器的頻道資訊
當sentinel與一個主伺服器或從伺服器建立訂閱連接配接後,通過訂閱連接配接發送訂閱指令 SUBSCRIBE _sentinel_:hello
對于監視同一個伺服器的多個sentinel來說,一個sentinel發送的資訊會被其他sentinel接收,這些資訊用于①更新其他sentinel對發送資訊的sentinel的認知②更新其他sentinel對被監視伺服器的認知。
(1)更新sentinel字典
sentinel為主伺服器建立的執行個體結構的sentinel字典儲存了除sentinel本身之外,所有同樣監視這個主伺服器的其他sentinel的資料。
(2)建立連向其他sentinel的指令連接配接
6. 檢測主觀下線狀态
sentinel會以每秒一次的頻率向所有與它從建立了指令連接配接的執行個體(包括主伺服器、從伺服器、其他sentinel在内)發送 PING
指令,并通過執行個體傳回的PING指令回複來判斷執行個體是否線上。
7. 檢測客觀下線狀态
當sentinel在判定一個主伺服器為主觀下線後,為了确認這個主伺服器是否真的下線,詢問同樣監視這個主伺服器的其他sentinel,當收到足夠數量的已下線判斷後,sentinel就會将伺服器判定為客觀下線,并對伺服器執行故障轉移操作。
8. 選舉領頭sentinel
當一個主伺服器被判斷為客觀下線後,監視這個下線伺服器的各個sentinel會進行協商,選舉出一個領頭sentinel,并由領頭sentinel對下線伺服器執行故障轉移操作。
9. 故障轉移
領頭sentinel對下線伺服器執行故障轉移操作,三個步驟:
1. 在已下線主伺服器屬下的所有從伺服器裡面,挑選出一個從伺服器,并将其轉換為主伺服器。
2. 讓已下線伺服器屬下的所有從伺服器改為複制新的主伺服器
3. 将已下線伺服器設定為新的主伺服器的從伺服器,當這個舊的主伺服器重新上線時,它就會成為新的主伺服器的從伺服器。