Redis 的 Sentinel 系統用於管理多個 Redis 服務器(instance), 該系統執行如下三個任務:html
Redis Sentinel 是一個分佈式系統, 你能夠在一個架構中運行多個 Sentinel 進程(progress), 這些進程使用流言協議(gossip protocols)來接收關於主服務器是否下線的信息, 並使用投票協議(agreement protocols)來決定是否執行自動故障遷移, 以及選擇哪一個從服務器做爲新的主服務器。事實上,你能夠用zk實現一樣的功能。java
雖然 Redis Sentinel 釋出爲一個單獨的可執行文件 redis-sentinel , 但實際上它只是一個運行在特殊模式下的 Redis 服務器, 你能夠在啓動一個普通 Redis 服務器時經過給定 –sentinel 選項來啓動 Redis Sentinel 。redis
目前 Sentinel 系統是 Redis 的 unstable 分支的一部分, 你必須到 Redis 項目的 Github 頁面 克隆一份 unstable 分支, 而後經過編譯來得到 Sentinel 系統。算法
Sentinel 程序能夠在編譯後的 src 文檔中發現, 它是一個命名爲 redis-sentinel 的程序。安全
你也能夠經過下一節介紹的方法, 讓 redis-server 程序運行在 Sentinel 模式之下。服務器
另外, 一個新版本的 Sentinel 已經包含在了 Redis 2.8.0 版本的釋出文件中。網絡
對於 redis-sentinel 程序, 你能夠用如下命令來啓動 Sentinel 系統:架構
redis-sentinel /path/to/sentinel.conf
對於 redis-server 程序, 你能夠用如下命令來啓動一個運行在 Sentinel 模式下的 Redis 服務器:分佈式
redis-server /path/to/sentinel.conf --sentinel
兩種方法均可以啓動一個 Sentinel 實例。spa
啓動 Sentinel 實例必須指定相應的配置文件, 系統會使用配置文件來保存 Sentinel 的當前狀態, 並在 Sentinel 重啓時經過載入配置文件來進行狀態還原。
若是啓動 Sentinel 時沒有指定相應的配置文件, 或者指定的配置文件不可寫(not writable), 那麼 Sentinel 會拒絕啓動。
Redis 源碼中包含了一個名爲 sentinel.conf 的文件, 這個文件是一個帶有詳細註釋的 Sentinel 配置文件示例。
運行一個 Sentinel 所需的最少配置以下所示:
sentinel monitor mymaster 127.0.0.1 6379 2 sentinel down-after-milliseconds mymaster 60000 sentinel failover-timeout mymaster 180000 sentinel parallel-syncs mymaster 1 sentinel monitor resque 192.168.1.3 6380 4 sentinel down-after-milliseconds resque 10000 sentinel failover-timeout resque 180000 sentinel parallel-syncs resque 5
第一行配置指示 Sentinel 去監視一個名爲 mymaster 的主服務器, 這個主服務器的 IP 地址爲 127.0.0.1 , 端口號爲 6379 , 而將這個主服務器判斷爲失效至少須要 2 個 Sentinel 贊成 (只要贊成 Sentinel 的數量不達標,自動故障遷移就不會執行)。
不過要注意, 不管你設置要多少個 Sentinel 贊成才能判斷一個服務器失效, 一個 Sentinel 都須要得到系統中多數(majority) Sentinel 的支持, 才能發起一次自動故障遷移, 並預留一個給定的配置紀元 (configuration Epoch ,一個配置紀元就是一個新主服務器配置的版本號)。
換句話說, 在只有少數(minority) Sentinel 進程正常運做的狀況下, Sentinel 是不能執行自動故障遷移的。
其餘選項的基本格式以下:
sentinel <選項的名字> <主服務器的名字> <選項的值>
各個選項的功能以下:
若是服務器在給定的毫秒數以內, 沒有返回 Sentinel 發送的 PING 命令的回覆, 或者返回一個錯誤, 那麼 Sentinel 將這個服務器標記爲主觀下線(subjectively down,簡稱 SDOWN )。
不過只有一個 Sentinel 將服務器標記爲主觀下線並不必定會引發服務器的自動故障遷移: 只有在足夠數量的 Sentinel 都將一個服務器標記爲主觀下線以後, 服務器纔會被標記爲客觀下線(objectively down, 簡稱 ODOWN ), 這時自動故障遷移纔會執行。
將服務器標記爲客觀下線所需的 Sentinel 數量由對主服務器的配置決定。
若是從服務器被設置爲容許使用過時數據集(參見對 redis.conf 文件中對 slave-serve-stale-data 選項的說明), 那麼你可能不但願全部從服務器都在同一時間向新的主服務器發送同步請求, 由於儘管複製過程的絕大部分步驟都不會阻塞從服務器, 但從服務器在載入主服務器發來的 RDB 文件時, 仍然會形成從服務器在一段時間內不能處理命令請求: 若是所有從服務器一塊兒對新的主服務器進行同步, 那麼就可能會形成全部從服務器在短期內所有不可用的狀況出現。
你能夠經過將這個值設爲 1 來保證每次只有一個從服務器處於不能處理命令請求的狀態。
本文檔剩餘的內容將對 Sentinel 系統的其餘選項進行介紹, 示例配置文件 sentinel.conf 也對相關的選項進行了完整的註釋。
前面說過, Redis 的 Sentinel 中關於下線(down)有兩個不一樣的概念:
若是一個服務器沒有在 master-down-after-milliseconds 選項所指定的時間內, 對向它發送 PING 命令的 Sentinel 返回一個有效回覆(valid reply), 那麼 Sentinel 就會將這個服務器標記爲主觀下線。
服務器對 PING 命令的有效回覆能夠是如下三種回覆的其中一種:
若是服務器返回除以上三種回覆以外的其餘回覆, 又或者在指定時間內沒有回覆 PING 命令, 那麼 Sentinel 認爲服務器返回的回覆無效(non-valid)。
注意, 一個服務器必須在 master-down-after-milliseconds 毫秒內, 一直返回無效回覆纔會被 Sentinel 標記爲主觀下線。
舉個例子, 若是 master-down-after-milliseconds 選項的值爲 30000 毫秒(30 秒), 那麼只要服務器能在每 29 秒以內返回至少一次有效回覆, 這個服務器就仍然會被認爲是處於正常狀態的。
從主觀下線狀態切換到客觀下線狀態並無使用嚴格的法定人數算法(strong quorum algorithm), 而是使用了流言協議: 若是 Sentinel 在給定的時間範圍內, 從其餘 Sentinel 那裏接收到了足夠數量的主服務器下線報告, 那麼 Sentinel 就會將主服務器的狀態從主觀下線改變爲客觀下線。 若是以後其餘 Sentinel 再也不報告主服務器已下線, 那麼客觀下線狀態就會被移除。
客觀下線條件只適用於主服務器: 對於任何其餘類型的 Redis 實例, Sentinel 在將它們判斷爲下線前不須要進行協商, 因此從服務器或者其餘 Sentinel 永遠不會達到客觀下線條件。
只要一個 Sentinel 發現某個主服務器進入了客觀下線狀態, 這個 Sentinel 就可能會被其餘 Sentinel 推選出, 並對失效的主服務器執行自動故障遷移操做。
一個 Sentinel 能夠與其餘多個 Sentinel 進行鏈接, 各個 Sentinel 之間能夠互相檢查對方的可用性, 並進行信息交換。
你無須爲運行的每一個 Sentinel 分別設置其餘 Sentinel 的地址, 由於 Sentinel 能夠經過發佈與訂閱功能來自動發現正在監視相同主服務器的其餘 Sentinel , 這一功能是經過向頻道 sentinel:hello 發送信息來實現的。
與此相似, 你也沒必要手動列出主服務器屬下的全部從服務器, 由於 Sentinel 能夠經過詢問主服務器來得到全部從服務器的信息。
在默認狀況下, Sentinel 使用 TCP
端口 26379 (普通 Redis 服務器使用的是 6379 )。
Sentinel 接受 Redis 協議格式的命令請求, 因此你可使用 redis-cli 或者任何其餘 Redis 客戶端來與 Sentinel 進行通信。
有兩種方式能夠和 Sentinel 進行通信:
如下列出的是 Sentinel 接受的命令:
客戶端能夠將 Sentinel 看做是一個只提供了訂閱功能的 Redis 服務器: 你不可使用 PUBLISH 命令向這個服務器發送信息, 但你能夠用 SUBSCRIBE 命令或者 PSUBSCRIBE 命令, 經過訂閱給定的頻道來獲取相應的事件提醒。
一個頻道可以接收和這個頻道的名字相同的事件。 好比說, 名爲 +sdown 的頻道就能夠接收全部實例進入主觀下線(SDOWN)狀態的事件。
經過執行 PSUBSCRIBE * 命令能夠接收全部事件信息。注意, 當信息中包含 instance details 字樣時, 表示頻道所返回的信息中包含了如下用於識別目標實例的內容:
<instance-type> <name> <ip> <port> @ <master-name> <master-ip> <master-port>
@ 字符以後的內容用於指定主服務器, 這些內容是可選的, 它們僅在 @ 字符以前的內容指定的實例不是主服務器時使用。
能夠訂閱的頻道以下:
一次故障轉移操做由如下步驟組成:
SLAVEOF NO ONE
命令,讓它轉變爲主服務器。每當一個 Redis 實例被從新配置(reconfigured) —— 不管是被設置成主服務器、從服務器、又或者被設置成其餘主服務器的從服務器 —— Sentinel 都會向被從新配置的實例發送一個 CONFIG REWRITE 命令, 從而確保這些配置會持久化在硬盤裏。
Sentinel 使用如下規則來選擇新的主服務器:
關於sentinel leader選舉和failover的詳細過程,參見以下,
1) Leader選舉:
其實在sentinels故障轉移中,仍然須要一個「Leader」來調度整個過程:master的選舉以及slave的重配置和同步。當集羣中有多個sentinel實例時,如何選舉其中一個sentinel爲leader呢?
在配置文件中「can-failover」「quorum」參數,以及「is-master-down-by-addr」指令配合來完成整個過程。
A) 「can-failover」用來代表當前sentinel是否能夠參與「failover」過程,若是爲「YES」則代表它將有能力參與「Leader」的選舉,不然它將做爲「Observer」,observer參與leader選舉投票但不能被選舉;
B) 「quorum」不只用來控制master ODOWN狀態確認,同時還用來選舉leader時最小「贊同票」數;
C) 「is-master-down-by-addr」,在上文中以及提到,它能夠用來檢測「ip + port」的master是否已經處於SDOWN狀態,不過此指令不只可以得到master是否處於SDOWN,同時它還額外的返回當前sentinel本地「投票選舉」的Leader信息(runid);
每一個sentinel實例都持有其餘的sentinels信息,在Leader選舉過程當中(當爲leader的sentinel實例失效時,有可能master server並沒失效,注意分開理解),sentinel實例將從全部的sentinels集合中去除「can-failover = no」和狀態爲SDOWN的sentinels,在剩餘的sentinels列表中按照runid按照「字典」順序排序後,取出runid最小的sentinel實例,並將它「投票選舉」爲Leader,並在其餘sentinel發送的「is-master-down-by-addr」指令時將推選的runid追加到響應中。每一個sentinel實例都會檢測「is-master-down-by-addr」的響應結果,若是「投票選舉」的leader爲本身,且狀態正常的sentinels實例中,「贊同者」的本身的sentinel個數不小於(>=) 50% + 1,且不小與<quorum>,那麼此sentinel就會認爲選舉成功且leader爲本身。
在sentinel.conf文件中,咱們指望有足夠多的sentinel實例配置「can-failover yes」,這樣可以確保當leader失效時,可以選舉某個sentinel爲leader,以便進行failover。若是leader沒法產生,好比較少的sentinels實例有效,那麼failover過程將沒法繼續.
2) failover過程:
在Leader觸發failover以前,首先wait數秒(隨即0~5),以便讓其餘sentinel實例準備和調整(有可能多個leader??),若是一切正常,那麼leader就須要開始將一個salve提高爲master,此slave必須爲狀態良好(不能處於SDOWN/ODOWN狀態)且權重值最低(redis.conf中)的,當master身份被確認後,開始failover
A)「+failover-triggered」: Leader開始進行failover,此後緊跟着「+failover-state-wait-start」,wait數秒。
B)「+failover-state-select-slave」: Leader開始查找合適的slave
C)「+selected-slave」: 已經找到合適的slave
D) 「+failover-state-sen-slaveof-noone」: Leader向slave發送「slaveof no one」指令,此時slave已經完成角色轉換,此slave即爲master
E) 「+failover-state-wait-promotition」: 等待其餘sentinel確認slave
F)「+promoted-slave」:確認成功
G)「+failover-state-reconf-slaves」: 開始對slaves進行reconfig操做。
H)「+slave-reconf-sent」:向指定的slave發送「slaveof」指令,告知此slave跟隨新的master
I)「+slave-reconf-inprog」: 此slave正在執行slaveof + SYNC過程,如過slave收到「+slave-reconf-sent」以後將會執行slaveof操做。
J)「+slave-reconf-done」: 此slave同步完成,此後leader能夠繼續下一個slave的reconfig操做。循環G)
K)「+failover-end」: 故障轉移結束
L)「+switch-master」:故障轉移成功後,各個sentinel實例開始監控新的master。
Sentinel 自動故障遷移使用 Raft 算法來選舉領頭(leader) Sentinel , 從而確保在一個給定的紀元(epoch)裏, 只有一個領頭產生。
這表示在同一個紀元中, 不會有兩個 Sentinel 同時被選中爲領頭, 而且各個 Sentinel 在同一個紀元中只會對一個領頭進行投票。
更高的配置紀元老是優於較低的紀元, 所以每一個 Sentinel 都會主動使用更新的紀元來代替本身的配置。
簡單來講, 咱們能夠將 Sentinel 配置看做是一個帶有版本號的狀態。 一個狀態會以最後寫入者勝出(last-write-wins)的方式(也便是,最新的配置老是勝出)傳播至全部其餘 Sentinel 。
舉個例子, 當出現網絡分割(network partitions)時, 一個 Sentinel 可能會包含了較舊的配置, 而當這個 Sentinel 接到其餘 Sentinel 發來的版本更新的配置時, Sentinel 就會對本身的配置進行更新。
若是要在網絡分割出現的狀況下仍然保持一致性, 那麼應該使用 min-slaves-to-write 選項, 讓主服務器在鏈接的從實例少於給定數量時中止執行寫操做, 與此同時, 應該在每一個運行 Redis 主服務器或從服務器的機器上運行 Redis Sentinel 進程。
Sentinel 的狀態會被持久化在 Sentinel 配置文件裏面。
每當 Sentinel 接收到一個新的配置, 或者當領頭 Sentinel 爲主服務器建立一個新的配置時, 這個配置會與配置紀元一塊兒被保存到磁盤裏面。
這意味着中止和重啓 Sentinel 進程都是安全的。
即便沒有自動故障遷移操做在進行, Sentinel 總會嘗試將當前的配置設置到被監視的實例上面。 特別是:
不過, 在以上這些條件知足以後, Sentinel 在對實例進行從新配置以前仍然會等待一段足夠長的時間, 確保能夠接收到其餘 Sentinel 發來的配置更新, 從而避免自身由於保存了過時的配置而對實例進行了沒必要要的從新配置。
Redis Sentinel 嚴重依賴計算機的時間功能: 好比說, 爲了判斷一個實例是否可用, Sentinel 會記錄這個實例最後一次相應 PING 命令的時間, 並將這個時間和當前時間進行對比, 從而知道這個實例有多長時間沒有和 Sentinel 進行任何成功通信。
不過, 一旦計算機的時間功能出現故障, 或者計算機很是忙碌, 又或者進程由於某些緣由而被阻塞時, Sentinel 可能也會跟着出現故障。
TILT 模式是一種特殊的保護模式: 當 Sentinel 發現系統有些不對勁時, Sentinel 就會進入 TILT 模式。
由於 Sentinel 的時間中斷器默認每秒執行 10 次, 因此咱們預期時間中斷器的兩次執行之間的間隔爲 100 毫秒左右。 Sentinel 的作法是, 記錄上一次時間中斷器執行時的時間, 並將它和這一次時間中斷器執行的時間進行對比:
當 Sentinel 進入 TILT 模式時, 它仍然會繼續監視全部目標, 可是:
若是 TILT 能夠正常維持 30 秒鐘, 那麼 Sentinel 退出 TILT 模式。
當 Lua 腳本的運行時間超過指定時限時, Redis 就會返回 -BUSY 錯誤。
當出現這種狀況時, Sentinel 在嘗試執行故障轉移操做以前, 會先向服務器發送一個 SCRIPT KILL 命令, 若是服務器正在執行的是一個只讀腳本的話, 那麼這個腳本就會被殺死, 服務器就會回到正常狀態。
附:
一.Sentinel.conf詳解
##sentinel實例之間的通信端口 ##redis-0 port 26379 ##sentinel須要監控的master信息:<mastername> <masterIP> <masterPort> <quorum> ##<quorum>應該小於集羣中slave的個數,只有當至少<quorum>個sentinel實例提交"master失效" ##纔會認爲master爲O_DWON("客觀"失效) sentinel monitor def_master 127.0.0.1 6379 2 sentinel auth-pass def_master 012_345^678-90 ##master被當前sentinel實例認定爲「失效」的間隔時間 ##若是當前sentinel與master直接的通信中,在指定時間內沒有響應或者響應錯誤代碼,那麼 ##當前sentinel就認爲master失效(SDOWN,「主觀」失效) ##<mastername> <millseconds> ##默認爲30秒 sentinel down-after-milliseconds def_master 30000 ##當前sentinel實例是否容許實施「failover」(故障轉移) ##no表示當前sentinel爲「觀察者」(只參與"投票".不參與實施failover), ##全局中至少有一個爲yes sentinel can-failover def_master yes ##當新master產生時,同時進行「slaveof」到新master並進行「SYNC」的slave個數。 ##默認爲1,建議保持默認值 ##在salve執行salveof與同步時,將會終止客戶端請求。 ##此值較大,意味着「集羣」終止客戶端請求的時間總和和較大。 ##此值較小,意味着「集羣」在故障轉移期間,多個salve向客戶端提供服務時仍然使用舊數據。 sentinel parallel-syncs def_master 1 ##failover過時時間,當failover開始後,在此時間內仍然沒有觸發任何failover操做, ##當前sentinel將會認爲這次failoer失敗。 sentinel failover-timeout def_master 900000 ##當failover時,能夠指定一個「通知」腳本用來告知系統管理員,當前集羣的狀況。 ##腳本被容許執行的最大時間爲60秒,若是超時,腳本將會被終止(KILL) ##腳本執行的結果: ## 1 -> 稍後重試,最大重試次數爲10; ## 2 -> 執行結束,無需重試 ##sentinel notification-script mymaster /var/redis/notify.sh ##failover以後重配置客戶端,執行腳本時會傳遞大量參數,請參考相關文檔 # sentinel client-reconfig-script <master-name> <script-path>
更詳細信息,請參考src/sentinel.c源碼 .配置文件加載過程參見方法:sentinelHandlerConfiguration(..)
2、Jedis客戶端與Setinel
Java代碼
Set<String> sentinels = new HashSet<String>(16); sentinels.add("127.0.0.1:26379");//集羣中全部sentinels的地址 sentinels.add("127.0.0.1:26479"); sentinels.add("127.0.0.1:26579"); GenericObjectPoolConfig config = new GenericObjectPoolConfig(); config.setMaxTotal(32); //setinel客戶端提供了master自動發現功能 JedisSentinelPool jedisSentinelPool = new JedisSentinelPool("def_master", sentinels,config, "012_345^678-90"); Jedis jedis = jedisSentinelPool.getResource(); try{ // jedis.set("key","value"); } finally { jedisSentinelPool.returnResource(jedis); } jedisSentinelPool.close();
JedisSentinelPool將遍歷sentinels列表,並嘗試與每一個sentinel創建鏈接直到成功爲止;若是創建鏈接成功,就向此sentinel發送「get-master-addr-by-name」指令,獲取master的位置,此後將創建與master的鏈接。此後JedisSentinelPool還會啓動一個監測線程,用於監測master的存活狀況,若是master角色遷移,則從新獲取新的master地址,並從新初始化鏈接池。注意JedisSentinelPool並無提供「M-S」下讀寫分離的設計,即讀寫操做均在master上發生。不過很遺憾,我以爲仍是須要一種客戶端解決方案,可以實現讀寫分離。