Redis Sentinel 的配置是最終一致性的,所以每個分區(qū)會被統(tǒng)一到一個可用的更高版本的配置。但是,在使用 Sentinel 的真實世界系統(tǒng)中有三個不同的角色:
為了定義系統(tǒng)的行為,我們得考慮這三個角色。
下面是一個有三個節(jié)點的簡單網(wǎng)絡,每一個節(jié)點運行一個 Redis 實例和一個 Sentinel 實例:
http://wiki.jikexueyuan.com/project/redis-guide/images/1.png" alt="" />
在這個系統(tǒng)中,初始狀態(tài)是 Redis 3 是主服務器,Redis 1 和 Redis 2 是從服務器。分割發(fā)生了,隔斷了老的主服務器。Sentinel 1 和 2 開始故障轉(zhuǎn)移,提升 Sentinel 1 作為新的主服務器。
Sentinel 的屬性保證,Sentinel 1 和 2 現(xiàn)在擁有主服務器的最新配置。但是,Sentinel 3 仍是舊的配置,因為它存在于一個不同的分割中。
當網(wǎng)絡分割恢復正常了,Sentinel 3 將會更新其配置,但是,如果有客戶端與老的主服務器被分割在一起,在分割期間會發(fā)生什么事情呢?
客戶端會繼續(xù)向 Redis 3 寫,即老的主服務器。當分割又聚合在一起,Redis 3 將會變成 Redis 1 的從服務器,分割期間所有寫入的數(shù)據(jù)會丟失。
你可能想或者不想這種場景發(fā)生,取決于你的配置:
因為 Redis 是異步復制,這種場景下沒有完全阻止數(shù)據(jù)丟失的辦法,但是你可以使用下面的 Redis 配置選項,來限制 Redis 3 和 Redis 1 之間的分歧:
min-slaves-to-write 1
min-slaves-max-lag 10
有了上面的配置(請查看 Redis 分發(fā)版本中自帶的 redis.conf 文件中的注釋獲取更多的信息),扮演主服務器的 Redis 實例如果不能寫入到至少一個從服務器,將會停止接受寫請求。由于復制是異步的,不能寫入的意思就是從服務器也是斷開的,或者在指定的 max-lag 秒數(shù)沒有發(fā)送異步回應 (acknowledges)。
使用這個配置,上面例子中的 Redis 3 在 10 秒鐘之后變得不可用。當分割恢復了,Sentinel 3 的配置將會統(tǒng)一為新的,客戶端 B 可以獲取合法的配置并且繼續(xù)。
Sentinel 的狀態(tài)被持久化在 Sentinel 的配置文件中。例如,每次創(chuàng)建(領導者 leader Sentinel)或者收到新的配置,主服務器會將配置連同配置紀元持久化到磁盤中。這意味著,停止和重啟 Sentinel 進程是安全的。
即使沒有故障轉(zhuǎn)移在進行中,Sentinel 也會一直嘗試在被監(jiān)控的實例上設置當前配置。尤其是:
這防止了持有舊配置(例如,因為剛剛從分割中恢復)的 Sentinel 會嘗試在收到變更之前改變從服務器的配置。
也要注意,一直嘗試使用當前配置使得故障轉(zhuǎn)移對分割具有更強的抵抗力的語義是什么:
當 Sentinel 實例準備執(zhí)行故障轉(zhuǎn)移,也就是當主服務器處于 ODOWN 狀態(tài),并且 Sentinel 從大多數(shù)已知 Sentinel 實例收到了故障轉(zhuǎn)移授權,需要選擇一個合適的從服務器。
從服務器的選擇過程評估從服務器的以下信息:
一個從服務器被發(fā)現(xiàn)從主服務器斷開超過十倍于配置的主服務器超時(down-after-milliseconds 選項),加上從正在執(zhí)行故障轉(zhuǎn)移的 Sentinel 的角度來看主服務器也不可用的時間,將會被認為不適合用于故障轉(zhuǎn)移并跳過。
更嚴謹?shù)卣f,一個從服務器的 INFO 輸出表明已從主服務器斷開超過:
(down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state
就被認為不可靠并且被拋棄。
從服務器選擇只考慮通過了上面測試的從服務器,并且基于上面的標準排序,使用下面的順序。
如果對機器有強烈的偏好的話,Redis 主服務器(故障轉(zhuǎn)移以后成為從服務器)和從服務器都需要配置 slave-priority。否則,所有的實例都可以使用默認的運行 ID 來運行(這是建議的設置,因為按照復制偏移量來選擇從服務器要有趣得多)。
Redis 實例可以配置一個特殊的 slave-priority 值 0,這樣就一定不會被 Sentinel 選舉為新的主服務器。但是,按照這樣配置的從服務器仍然會被 Sentinel 重新配置,從而在故障轉(zhuǎn)移后復制新的主服務器,唯一的區(qū)別是永遠不會變成主服務器。
Sentinel 和 Redis 身份驗證(authentication)
當主服務器被配置為需要客戶端傳遞密碼時,作為安全措施,從服務器也需要知道這個密碼來驗證主服務器,并且創(chuàng)建用于異步復制協(xié)議的主從連接。
使用下面的配置指令完成:
主服務器中的 requirepass 用來設置密碼驗證,以確保實例不會處理沒有驗證過的客戶端的請求。 從服務器中的 masterauth 用于從服務器驗證主服務器,以正確的從其復制數(shù)據(jù)。
當使用 Sentinel 就沒有單一的主服務器,因為故障轉(zhuǎn)移以后從服務器可以扮演主服務器的角色,老的主服務器被重新配置以扮演從服務器,所以你要做的就是在你所有的主服務器和從服務器實例中設置以上指令。
這通常是一種邏輯上健全的設置,因為你不想只是保護主服務器中的數(shù)據(jù),從服務器中也應擁有同樣可訪問的數(shù)據(jù)。
但是,在一些不常見的情況下,你需要從服務器無需驗證就能訪問,你仍可以通過設置從服務器的優(yōu)先級為 0(這將不允許從服務器被提升為主服務器),只為從服務器配置 masterauth 指令,不配置 requirepass 指令這樣來做到,這樣數(shù)據(jù)就可以讓未經(jīng)驗證的客戶端讀取。
Sentinel 運行默認使用 TCP 端口 26379(注意,6379 是正常的 Redis 端口)。Sentinel 接受使用 Redis 協(xié)議的命令,所以你可以使用 redis-cli 或者任何其他未修改的 Redis 客戶端與 Sentinel 對話。
有兩種方式與 Sentinel 對話:可以直接查詢它來檢查被監(jiān)控的 Redis 實例的狀態(tài),看看它知道的其他 Sentinel,等等。
另外一種方式是使用發(fā)布訂閱,每當某個事件發(fā)生時,例如故障轉(zhuǎn)移,或者一個實例進入到了一個錯誤條件,等等,接收從 Sentinel 推過來的通知。
下面是可接受的命令清單:
從 Redis 2.8.4 開始,Sentinel 提供了用于添加,刪除和改變指定主服務器配置的 API。注意,如果你有多個 Sentinel 實例,你得將改變應用到所有的 Redis Sentinel 實例才能運轉(zhuǎn)正常。也就是說,改變一個 Sentinel 的配置不會自動傳播到網(wǎng)絡中的其它 Sentinel。
下面是 SENTINEL 的子命令清單,用于更新 Sentinel 實例的配置。
下面是 SENTINEL SET 命令的一個例子,用于修改一個名為 objects-cache 的主服務器的 down-after-milliseconds 配置:
SENTINEL SET objects-cache-master down-after-milliseconds 1000
啟動以后,SENTINEL SET 能用于設置所有在啟動配置文件中可設置的配置參數(shù)。此外,還可以僅僅只改變主服務器的仲裁人數(shù)配置,而不需要使用 SENTINEL REMOVE 和 SENTINEL MONITOR 來刪除和重新添加主服務器,而只需要:
SENTINEL SET objects-cache-master quorum 5
注意,沒有與之等價的 GET 命令,因為 SENTINEL MASTER 以一種易于解析的格式(作為一個字段 - 值對數(shù)組)提供了所有的配置參數(shù)。
因為 Sentinel 實現(xiàn)的自動發(fā)現(xiàn)機制,添加一個新的 Sentinel 到你的部署中是一個很簡單的過程。所有你需要干的就是啟動一個配置用于監(jiān)控當前活躍主服務器的 Sentinel。在 10 秒鐘之內(nèi),Sentinel 就會獲得其他 Sentinel 的列表以及連接到主服務器的從服務器集合。
如果你想一次添加多個新的 Sentinel,建議一個一個的添加,等待所有其他的 Sentinel 知道了第一個再添加另一個。這在當添加新 Sentinel 的過程中發(fā)生錯誤時,仍然保證在分割的一側能達到大多數(shù)時很有用。
在沒有網(wǎng)絡分割時,這可以通過添加每個新的 Sentinel 時帶 30 秒的延遲來輕易實現(xiàn)。
在最后,可以使用命令 SENTINEL MASTER mastername 來檢查是否所有的 Sentinel 就監(jiān)控主服務器的 Sentinel 數(shù)量達成一致。
刪除一個 Sentinel 要稍微復雜一些:Sentinel 永遠不會忘記已經(jīng)發(fā)現(xiàn)的 Sentinel,即使他們在很長一段時間內(nèi)都不可達,因為我們不想動態(tài)改變用于授權故障轉(zhuǎn)移所需要的大多數(shù)以及創(chuàng)建新的配置版本。所以在沒有網(wǎng)絡分割情況下,需要執(zhí)行下面的步驟來刪除 Sentinel:
Sentinel 不會忘記主服務器的從服務器,即使在很長時間內(nèi)都不可達。這很有用,因為這樣 Sentinel 能夠在網(wǎng)絡分割或者錯誤事件恢復后正確地重新配置一個返回的從服務器。
此外,故障轉(zhuǎn)移之后,被故障轉(zhuǎn)移的主服務器事實上被添加為新主服務器的從服務器,這樣一旦恢復重新可用,就會被重新配置來復制新的主服務器。
但是,有時候你想從 Sentinel 監(jiān)控的從服務器列表中永久刪除一個從服務器(可能是舊的主服務器)。
要做到這個,你需要發(fā)送 SENTINEL RESET mastername 命令到所有的 Sentinel:在接下來的 10 秒內(nèi),他們會刷新從服務器列表,只添加當前主服務器 INFO 輸出中的正確復制的清單。
客戶端可以將 Sentinel 作為一個 Redis 兼容的發(fā)布訂閱服務器(但是你不能使用 PUBLISH)來使用,來訂閱或者發(fā)布到頻道,獲取指定事件通知。
頻道名稱與事件名稱是一樣的。例如,名為 + sdown 的頻道會收到所有關于實例進入 SDOWN 條件的通知。
簡單使用 PSUBSCRIBE * 訂閱來獲得所有的消息。
下面是頻道的清單,以及使用這個 API 你會收到的消息格式。第一個單詞是頻道/事件名稱,剩下的是數(shù)據(jù)的格式。
注意:指定 instance details 的地方表示提供了下面用于表示目標實例的參數(shù):
<instance-type> <name> <ip> <port> @ <master-name> <master-ip> <master-port>
標識主服務器的部分 (從 @參數(shù)到結束) 是可選的,只在實例不是主服務器本身時指定。
Redis Sentinel 嚴重依賴于計算機時間:例如,為了了解一個實例是否可用,Sentinel 會記住最近成功回復 PING 命令的時間,與當前時間對比來了解這有多久。
但是,如果計算機時間以不可預知的方式改變了,或者計算機非常繁忙,或者某些原因進程阻塞了,Sentinel 可能會開始表現(xiàn)得不可預知。
TILT 模式是一個特別的保護模式,當發(fā)現(xiàn)一些會降低系統(tǒng)可靠性的奇怪問題時,Sentinel 就會進入這種模式。Sentinel 的定時中斷通常每秒鐘執(zhí)行 10 次,所以我們期待兩次定時中斷調(diào)用之間相隔 100 毫秒左右。
Sentinel 做的就是記錄上一次定時中斷調(diào)用的時間,與當前調(diào)用進行比較:如果時間差是負數(shù)或者出乎意料的大(2 秒或更多),就進入了 TILT 模式(或者如果已經(jīng)進入了,退出 TILT 模式將被推遲)。
當處于 TILT 模式時,Sentinel 會繼續(xù)監(jiān)控一切,但是:
如果一切表現(xiàn)正常了 30 秒,將退出 TILT 模式。
(警告:還未實現(xiàn))
當腳本運行超過配置的腳本限制時間時返回 - BUSY 錯誤。當這種情況發(fā)生時,在觸發(fā)故障轉(zhuǎn)移之前 Redis Sentinel 會嘗試發(fā)送 SCRIPT KILL 命令,這只有在腳本是只讀的情況下才能成功。
Sentinel 需要顯式的客戶端支持,除非系統(tǒng)被配置為執(zhí)行一個腳本,來實現(xiàn)透明重定向所有請求到新的主服務器實例(虛擬 IP 或其它類似系統(tǒng))??蛻舳藥鞂崿F(xiàn)的主題在 Sentinel 客戶端指引手冊中討論(請期待本系列后續(xù)文檔,譯者注)。