最近在學(xué)習(xí) Redis 的高可用方案,就從 sentinel 開始。本篇文檔基本只是 redis sentinel 官方文檔 的摘要和總結(jié),感興趣的直接閱讀官方文檔是更好的選擇。
基本原理
Sentinel 的原理并不復(fù)雜:
- 啟動(dòng) n 個(gè) sentinel 實(shí)例,這些 sentinel 實(shí)例會(huì)去監(jiān)控你指定的 redis master/slaves
- 當(dāng) redis master 節(jié)點(diǎn)掛掉后, Sentinel 實(shí)例通過 ping 檢測(cè)失敗發(fā)現(xiàn)這種情況就認(rèn)為該節(jié)點(diǎn)進(jìn)入 SDOWN 狀態(tài),也就是檢測(cè)的 sentinel 實(shí)例主觀地(Subjectively)認(rèn)為該 redis master 節(jié)點(diǎn)掛掉。
- 當(dāng)一定數(shù)目(Quorum 參數(shù)設(shè)定)的 Sentinel 實(shí)例都認(rèn)為該 master 掛掉的情況下,該節(jié)點(diǎn)將轉(zhuǎn)換進(jìn)入 ODOWN 狀態(tài),也就是客觀地(Objectively)掛掉的狀態(tài)。
- 接下來 sentinel 實(shí)例之間發(fā)起選舉,選擇其中一個(gè) sentinel 實(shí)例發(fā)起 failover 過程:從 slave 中選擇一臺(tái)作為新的 master,讓其他 slave 從新的 master 復(fù)制數(shù)據(jù),并通過 Pub/Sub 發(fā)布事件。
- 使用者客戶端從任意 Sentinel 實(shí)例獲取 redis 配置信息,并監(jiān)聽(可選) Sentinel 發(fā)出的事件: SDOWN, ODOWN 以及 failover 等,并做相應(yīng)主從切換,Sentinel 還扮演了服務(wù)發(fā)現(xiàn)的角色。
- Sentinel 的 Leader 選舉采用的是 Raft 協(xié)議 。
一張示意圖,正常情況下:
當(dāng) M1 掛掉后:
節(jié)點(diǎn) 2 被提升為 master,Sentinel 通知客戶端和 slaves 去使用新的 Master。
搭建實(shí)驗(yàn)環(huán)境
- 兩個(gè) redis,一個(gè)主一個(gè)從,分別監(jiān)聽在 6379 和 6380 端口
$ redis-server $ redis-server --port 6380
redis-cli -p 6380
連上 6380 端口的 redis,執(zhí)行slaveof 127.0.0.1 6379
將它設(shè)置為 6379 的 slave。- 啟動(dòng)三個(gè) sentinel 實(shí)例,分別監(jiān)聽在 5000 – 5002 端口,并且監(jiān)控 6379 的 redis master,首先是配置文件
s1.conf:
port 5000 sentinel monitor mymaster 127.0.0.1 6370 2 sentinel down-after-milliseconds mymaster 1000 sentinel failover-timeout mymaster 60000
其他兩個(gè)配置文件是 s2.conf 和 s3.conf 只是將 port 5000
修改為 5001 和 5002,就不再重復(fù)。 需要確保配置文件是可寫的,因?yàn)?Sentinel 會(huì)往配置文件里添加很多信息作為狀態(tài)持久化,這是為了重啟等情況下可以正確地恢復(fù) sentinel 的狀態(tài)。
啟動(dòng):
$ redis-sentinel s1.conf $ redis-sentinel s2.conf $ redis-sentinel s3.conf
配置說明:
- port ,指定 sentinel 啟動(dòng)后監(jiān)聽的端口,sentinel 實(shí)例之間需要通過此端口通訊。
sentinel monitor [name] [ip] [port] [quorum]
,最重要的配置,指定要監(jiān)控的 redis master 的 IP 和端口,給這個(gè)監(jiān)控命名 name。Quorum 指定 至少 多少個(gè) sentinel 實(shí)例對(duì) redis master 掛掉的情況達(dá)成一致,只有達(dá)到這個(gè)數(shù)字后,Sentinel 才會(huì)去開始一次 failover 過程。- down-after-milliseconds,設(shè)定 Sentinel 發(fā)現(xiàn)一個(gè) redis 沒有響應(yīng) ping 到 Sentinel 認(rèn)為該 redis 實(shí)例不可訪問的時(shí)間。
- failover-timeout,Sentinel 實(shí)例投票對(duì)于同一個(gè) master 發(fā)起 failover 過程的間隔時(shí)間,防止同時(shí)開始多次 failover。
Sentinel 啟動(dòng)后會(huì)輸出類似的日志:
17326:X 13 Oct 12:00:55.143 # +monitor master mymaster 127.0.0.1 6379 quorum 2 17326:X 13 Oct 12:00:55.143 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
表示開始監(jiān)控 mymaster 集群,并輸出集群的基本信息。
以及 Sentinel 之間的感知日志,比如 s3 節(jié)點(diǎn)的輸出:
18441:X 13 Oct 12:01:39.985 * +sentinel sentinel eab05ac9fc34d8af6d59155caa195e0df5e80d73 127.0.0.1 5000 @ mymaster 127.0.0.1 6379 18441:X 13 Oct 12:01:52.918 * +sentinel sentinel 4bf24767144aea7b4d44a7253621cdd64cea6634 127.0.0.1 5002 @ mymaster 127.0.0.1 6379
查看信息
可以用 redis-cli 連上 sentinel 實(shí)例,查看信息:
$ redis-cli -p 5000 127.0.0.1:5000> sentinel master mymaster 1) "name" 2) "mymaster" 3) "ip" 4) "127.0.0.1" 5) "port" 6) "6379" 7) "runid" 8) "4b97e168125b735e034d49c7b1f45925f43aded9" 9) "flags" 10) "master" 11) "link-pending-commands" 12) "0" 13) "link-refcount" 14) "1" 15) "last-ping-sent" 16) "0" 17) "last-ok-ping-reply" 18) "729" 19) "last-ping-reply" 20) "729" 21) "down-after-milliseconds" 22) "1000" 23) "info-refresh" 24) "6258" 25) "role-reported" 26) "master" 27) "role-reported-time" 28) "11853370" 29) "config-epoch" 30) "0" 31) "num-slaves" 32) "1" 33) "num-other-sentinels" 34) "2" 35) "quorum" 36) "2" 37) "failover-timeout" 38) "60000" 39) "parallel-syncs" 40) "1"
sentinel master [name]
用于查看監(jiān)控的某個(gè) redis master 信息,包括配置和狀態(tài)等,其他命令還包括:
sentinel masters
查看所有監(jiān)控的 master 信息。sentinel slaves [name]
查看監(jiān)控的某個(gè) redis 集群的所有 slave 節(jié)點(diǎn)信息。sentinel sentinels [name]
查看所有 sentinel 實(shí)例信息。
更重要的一個(gè)命令是根據(jù)名稱來查詢 redis 信息,客戶端會(huì)用到:
127.0.0.1:5000> SENTINEL get-master-addr-by-name mymaster 1) "127.0.0.1" 2) "6379"
測(cè)試 Failover
我們讓 6379 的 master 主動(dòng)休眠 30 秒來觀察 failover 過程:
$ redis-cli -p 6379 DEBUG sleep 30
我們可以看到每個(gè) sentinel 進(jìn)程都監(jiān)控到 master 掛掉,從 sdown 狀態(tài)進(jìn)入 odown,然后選舉了一個(gè) leader 來進(jìn)行 failover,最終 6380 成為新的 master, sentinel 的日志輸出:
18441:X 13 Oct 15:26:51.735 # +sdown master mymaster 127.0.0.1 6379 18441:X 13 Oct 15:26:51.899 # +new-epoch 1 18441:X 13 Oct 15:26:51.900 # +vote-for-leader eab05ac9fc34d8af6d59155caa195e0df5e80d73 1 18441:X 13 Oct 15:26:52.854 # +odown master mymaster 127.0.0.1 6379 #quorum 3/2 18441:X 13 Oct 15:26:52.854 # Next failover delay: I will not start a failover before Thu Oct 13 15:28:52 2016 18441:X 13 Oct 15:26:53.034 # +config-update-from sentinel eab05ac9fc34d8af6d59155caa195e0df5e80d73 127.0.0.1 5000 @ mymaster 127.0.0.1 6379 18441:X 13 Oct 15:26:53.034 # +switch-master mymaster 127.0.0.1 6379 127.0.0.1 6380 18441:X 13 Oct 15:26:53.034 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6380 18441:X 13 Oct 15:26:54.045 # +sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6380 18441:X 13 Oct 15:27:20.383 # -sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6380
日志的幾個(gè)主要事件:
+sdown master mymaster 127.0.0.1 6379
,發(fā)現(xiàn) master 檢測(cè)失敗,主觀認(rèn)為該節(jié)點(diǎn)掛掉,進(jìn)入 sdown 狀態(tài)。+odown master mymaster 127.0.0.1 6379 #quorum 3/2
,有兩個(gè) sentinel 節(jié)點(diǎn)認(rèn)為 master 6379 掛掉,達(dá)到配置的 quorum 值 2,因此認(rèn)為 master 已經(jīng)客觀掛掉,進(jìn)入 odown 狀態(tài)。+vote-for-leader eab05ac9fc34d8af6d59155caa195e0df5e80d73
準(zhǔn)備選舉一個(gè) sentinel leader 來開始 failover。+switch-master mymaster 127.0.0.1 6379 127.0.0.1 6380
切換 master 節(jié)點(diǎn), failover 完成。+config-update-from sentinel eab05ac9fc34d8af6d59155caa195e0df5e80d73 127.0.0.1 5000 @ mymaster 127.0.0.1 6379
更新 sentinel 配置。- 6379 休眠回來,作為 slave 掛載到 6380 后面,可見 sentinel 確實(shí)同時(shí)在監(jiān)控 slave 狀態(tài),并且掛掉的節(jié)點(diǎn)不會(huì)自動(dòng)移除,而是繼續(xù)監(jiān)控。
此時(shí)查看 sentinel 配置文件,會(huì)發(fā)現(xiàn)增加了一些內(nèi)容:
# Generated by CONFIG REWRITE dir "/Users/dennis/opensources/redis-sentinel" sentinel failover-timeout mymaster 60000 sentinel config-epoch mymaster 1 sentinel leader-epoch mymaster 1 sentinel known-slave mymaster 127.0.0.1 6379 sentinel known-sentinel mymaster 127.0.0.1 5001 8ba1e75cbf4c268be4a2950ee7389df746c6b0b4 sentinel known-sentinel mymaster 127.0.0.1 5002 4bf24767144aea7b4d44a7253621cdd64cea6634 sentinel current-epoch 1
可以看到 sentinel 將最新的集群狀態(tài)寫入了配置文件。
運(yùn)維
命令
除了上面提到的一些查看信息的命令之外, sentinel 還支持下列命令來管理和檢測(cè) sentinel 配置:
SENTINEL reset <pattern>
強(qiáng)制重設(shè)所有監(jiān)控的 master 狀態(tài),清除已知的 slave 和 sentinel 實(shí)例信息,重新獲取并生成配置文件。SENTINEL failover <master name>
強(qiáng)制發(fā)起一次某個(gè) master 的 failover,如果該 master 不可訪問的話。SENTINEL ckquorum <master name>
檢測(cè) sentinel 配置是否合理, failover 的條件是否可能滿足,主要用來檢測(cè)你的 sentinel 配置是否正常。SENTINEL flushconfig
強(qiáng)制 sentinel 重寫所有配置信息到配置文件。
增加和移除監(jiān)控以及修改配置參數(shù):
SENTINEL MONITOR <name> <ip> <port> <quorum>
SENTINEL REMOVE <name>
SENTINEL SET <name> <option> <value>
增加和移除 Sentinel
增加新的 Sentinel 實(shí)例非常簡(jiǎn)單,修改好配置文件,啟動(dòng)即可,其他 Sentinel 會(huì)自動(dòng)發(fā)現(xiàn)該實(shí)例并加入集群。如果要批量啟動(dòng)一批 Sentinel 節(jié)點(diǎn),最好以 30 秒的間隔一個(gè)一個(gè)啟動(dòng)為好,這樣能確保整個(gè) Sentinel 集群的大多數(shù)能夠及時(shí)感知到新節(jié)點(diǎn),滿足當(dāng)時(shí)可能發(fā)生的選舉條件。
移除一個(gè) sentinel 實(shí)例會(huì)相對(duì)麻煩一些,因?yàn)?sentinel 不會(huì)忘記已經(jīng)感知到的 sentinel 實(shí)例,所以最好按照下列步驟來處理:
- 停止將要移除的 sentinel 進(jìn)程。
- 給其余的 sentinel 進(jìn)程發(fā)送
SENTINEL RESET *
命令來重置狀態(tài),忘記將要移除的 sentinel,每個(gè)進(jìn)程之間間隔 30 秒。 - 確保所有 sentinel 對(duì)于當(dāng)前存貨的 sentinel 數(shù)量達(dá)成一致,可以通過
SENTINEL MASTER [mastername]
命令來觀察,或者查看配置文件。
客戶端實(shí)現(xiàn)
客戶端從過去直接連接 redis ,變成:
- 先連接一個(gè) sentinel 實(shí)例
- 使用
SENTINEL get-master-addr-by-name master-name
獲取 redis 地址信息。 - 連接返回的 redis 地址信息,通過
ROLE
命令查詢是否是 master。如果是,連接進(jìn)入正常的服務(wù)環(huán)節(jié)。否則應(yīng)該斷開重新查詢。 - (可選)客戶端可以通過
SENTINEL sentinels [name]
來更新自己的 sentinel 實(shí)例列表。
當(dāng) Sentinel 發(fā)起 failover 后,切換了新的 master,sentinel 會(huì)發(fā)送 CLIENT KILL TYPE normal 命令給客戶端,客戶端需要主動(dòng)斷開對(duì)老的master 的鏈接,然后重新查詢新的 master 地址,再重復(fù)走上面的流程。這樣的方式仍然相對(duì)不夠?qū)崟r(shí),可以通過 sentinel 提供的 Pub/Sub 來更快地監(jiān)聽到 failover 事件,加快重連。
如果需要實(shí)現(xiàn)讀寫分離,讀走 slave,那可以走 SENTINEL slaves [name]
來查詢 slave 列表并連接。
生產(chǎn)環(huán)境推薦
對(duì)于一個(gè)最小集群,Redis 應(yīng)該是一個(gè) master 帶上兩個(gè) slave,并且開啟下列選項(xiàng):
min-slaves-to-write 1 min-slaves-max-lag 10
這樣能保證寫入 master 的同時(shí)至少寫入一個(gè) slave,如果出現(xiàn)網(wǎng)絡(luò)分區(qū)阻隔并發(fā)生 failover 的時(shí)候,可以保證寫入的數(shù)據(jù)最終一致而不是丟失,寫入老的 master 會(huì)直接失敗,參考 Consistency under partitions 。
Slave 可以適當(dāng)設(shè)置優(yōu)先級(jí),除了 0 之外(0 表示永遠(yuǎn)不提升為 master),越小的優(yōu)先級(jí),越有可能被提示為 master。如果 slave 分布在多個(gè)機(jī)房,可以考慮將和 master 同一個(gè)機(jī)房的 slave 的優(yōu)先級(jí)設(shè)置的更低以提升他被選為新的 master 的可能性。
考慮到可用性和選舉的需要,Sentinel 進(jìn)程至少為 3 個(gè),推薦為 5 個(gè),如果有網(wǎng)絡(luò)分區(qū),應(yīng)當(dāng)適當(dāng)分布(比如 2 個(gè)在 A 機(jī)房, 2 個(gè)在 B 機(jī)房,一個(gè)在 C 機(jī)房)等。
其他
由于 Redis 是異步復(fù)制,所以 sentinel 其實(shí)無法達(dá)到強(qiáng)一致性,它承諾的是最終一致性:最后一次 failover 的 redis master 贏者通吃,其他slave 的數(shù)據(jù)將被丟棄,重新從新的 master 復(fù)制數(shù)據(jù)。此外還有前面提到的分區(qū)帶來的一致性問題。
其次,Sentinel 的選舉算法依賴時(shí)間,因此要確保所有機(jī)器的時(shí)間同步,如果發(fā)現(xiàn)時(shí)間不一致,Sentinel 實(shí)現(xiàn)了一個(gè) TITL 模式來保護(hù)系統(tǒng)的可用性。