在前兩篇Redis中sentinel集群的搭建和Jedis測試 圖文教程[一] 和Redis中sentinel集群的搭建和Jedis測試 圖文教程[二] 中分別簡述了Redis中sentinel集群的搭建和Java代碼的Jedis測試。
這篇主要來簡單分析一下Redis-sentinel集群的原理,根據追蹤sentinel信息來完成Redis-sentinel集群測試中的詳細的原理分析。包括master-slave各個中的sentinel信息的分析,failover過程,master宕機後的leader選舉過程等等方面深層次詳細簡述了其各個原理。
HA(High Available,高可用性群集)機集群系統簡稱,是保證業務連續性的有效解決方案,一般有兩個或兩個以上的節點,且分為活動節點及備用節點。通常把正在執
行業務的稱為活動節點,而作為活動節點的一個備份的則稱為備用節點。當活動節點出現問題,導致正在運行的業務(任務)不能正常運行時,備用節點此時就會偵測到,並立即接續活動節點來執行業務。從而實現業務的不中斷或短暫中斷。From 百度百科.
Redis 一般以主/從方式部署(這裡討論的應用從實例主要用於備份,主實例提供讀寫)該方式要實現 HA 主要有如下幾種方案:
1)keepalived:通過 keepalived 的虛擬 IP,提供主從的統一訪問,在主出現問題時, 通過 keepalived 運行腳本將從提升為主,待主恢復後先同步後自動變為主,該方案的好處是主從切換後,應用程序不需要知道(因為訪問的虛擬 IP 不變),壞處是引入 keepalived 增加部署復雜性,在有些情況下會導致數據丟失;
2) zookeeper: 通過 zookeeper 來監控主從實例, 維護最新有效的 IP, 應用通過 zookeeper取得 IP,對 Redis 進行訪問,該方案需要編寫大量的監控代碼;
3)sentinel:通過 Sentinel 監控主從實例,自動進行故障恢復,該方案有個缺陷:因為主從實例地址(IP&PORT)是不同的, 當故障發生進行主從切換後, 應用程序無法知道新地址,故 在 Jedis2.2.2 中 新 增 了 對 Sentinel 的 支 持 , 應 用 通 過redis.clients.jedis.JedisSentinelPool.getResource()取得的Jedis實例會及時更新到新的主實例地址。
下面是該方案的部署邏輯圖。
詳情請見Redis中sentinel集群的搭建和Jedis測試 圖文教程[一]
由於 sentinel 服務器已經在 redis 服務器的環境中(redis-sentinel),我們這裡就直接使用它們(在生產環境中,sentinel 服務器和 redis-server 服務器一般是分離的,部署在不同的pc-server 上面,同時 sentinel 的個數和 redis-server 個數也沒聯系,下面為了方便學習使用將3 台 sentinel 服務都放到了一台 pc 上)。
注意: sentinel 配置文件只和默認的 master 有關系,和 slave 都沒有關系。我們上面的例子是用了 3 個 redis-server 和 3 個 redis-sentinel,其實 redis-sentinel的個數不一定要和 redis-sever 對應,1~n 個都可以。
首先要清楚,sentinel是一個獨立於redis之外的進程,不對外提供key/value服務。在redis的安裝目錄下名稱叫 redis-sentinel 。 主要用來監控redis-server進程,進行master/slave管理,如果你的redis沒有運行在master/slave模式下,不需要設置sentinel。
分別啟動 redis-server 和redis-sentinel。
1)啟動 redis-0 時 redis-0 的 sentinel 控制台
2)啟動 redis-1 時 reids-1 的 sentinel 控制台
啟動 redis-1 時 reids-0 的 sentinel 控制台
3)啟動 redis-2 時 reids-2 的 sentinel 控制台
啟動 redis-2 時 reids-0 的 sentinel 控制台
啟動 redis-2 時 reids-1 的 sentinel 控制台
<喎?http://www.Bkjia.com/kf/ware/vc/" target="_blank" class="keylink">vcD4KCjxwPqGhoaHNqLn9yc+1xL/Y1sbK5LP20MXPor/J0tS/tLP2o6zU2tLAtM7G9Lavt/7O8cqxo6y21NPDv9jWxsyoyuSz9sHLK3NlbnRpbmVsoaIrc2xhdmWhoi1zZG93biC1yNDFz6KjrMu1w/e497j2IHNlbnRpbmVsINauvOS9+NDQwcvNqNG2o6y2+MfS0rK84L/Ytb3ByyByZWRpcy1zZXJ2ZXIgtcTH6b/2oaMrc2VudGluZWwgse3KvtPQ0MK1xCBzZW50aW5lbCDKtcD9vNPI67W9vOC/2KGjzOHKvqO6yte0zrm5vaggc2VudGluZWwgu7e+s8qxo6yx2NDrytfPyMb0tq8gbWFzdGVyILv6xvehoyAgPGJyPgqhoaGhIDxicj4KoaGhoTSjqbLpv7TP4LnY0MXPoiAgPGJyPgqhoaGhyrnTwyAjIG5ldHN0YXQgLW50bHAg" grep redis 命令可以看到當前 redis 運行情況。
通過 redis-cli 查看 redis-server 的狀態
/usr/local/webserver/redis/redis-cli -h 127.0.0.1 -p 6379 -a abcd123457 info Replication
(上面的 -a 用來輸入密碼)
說明:info 指令
該指令將會打印完整的服務信息,包括集群,我們只需要關注”Replication”部分(在上面的命令中,我們在 info 後面加上了 Replication 的限制,如果不加,這還會輸出 Server、Clients、Memory、Persistence、Stats、CPU 和 Keyspace 等信息),這部分信息將會告訴我們”當前 server 的角色”以及指向它的所有的 slave 信息。可以通過在任何一個 slave 上,使用”INFO”指令獲得當前 slave 所指向的 master 信息。下面是 slave1 的 Replication 信息。
同時,該指令不僅可以幫助我們獲得集群的情況,當然 sentinel 組件也是使用”INFO”做同樣的事情。下面是 slave2 的 sentinel 信息。
通過上面信息,可以清楚看到 redis 服務狀態和主從關系。
5)failover 測試
當上述部署環境穩定後,我們直接關閉 redis-0,在等待”down-after-milliseconds”秒之後(30 秒),redis-0/redis-1/redis-2 的 sentinel 窗口會立即打印”+sdown”、”+odown”、”+failover”、”+selected-slave”、 “+promoted-slave”、 “+slave-reconf”等等一系列指令, 這些指令標明當 master失效後,sentinel 組件進行 failover 的過程。
模擬 mater 宕機的情況。此時各 sentinel 控制台輸出如下信息。
redis-0 上的 sentinel 信息.
redis-1 上的 sentinel 信息
redis-2 上的 sentinel 信息
從上面三個窗口的信息可以看出, 當 master 宕機後, 三個 sentinel(哨兵)進行了對 master進行了故障轉移。
{從 redis-1 的 sentinel 控制台可以看出,進行了下面的操作。
a.+sdown mater mymaster 127.0.0.1 6379 (主觀認為 mater 失效);
b.+odown mater mymaster 127.0.0.1 6379 #quorum 2/2(已經有兩個哨兵認為 master 主觀失效,則認為 mater 客觀失效);
c.+new-epoch 1(准備進行新 mater 的選取);
d.+try-failover master mymaster 127.0.0.1 6379(嘗試熱備份切換,master 讓出位置);
e.+vote-for-leader 1eb0f03b7a7815c3c5506b0fa041ad8d6ca9db90 1(投票選舉 Leader);
f.127.0.0.1:16379 voted for 1eb0f03b7a7815c3c5506b0fa041ad8d6ca9db90 1(投票選舉Leader);
g.127.0.0.1:36379 voted for 1eb0f03b7a7815c3c5506b0fa041ad8d6ca9db90 1(投票選舉Leader);
h.+elected-leader master mymaster 127.0.0.1 6379(之前被選舉出來的 master);
i.+failover-state-select-slave master mymaster 127.0.0.1 6379(Leader 開始查找合適的slave);
j.+selected-slave slave 127.0.0.1:63792 127.0.0.1 63792 @ mymaster 127.0.0.1 6379 (leader已經找到合適的 slave);
k.+failover-state-send-slaveof-noone slave 127.0.0.1:63792 127.0.0.1 63792 @ mymaster 127.0.0.1 6379(Leader 向 slave 發送“slaveof no one”指令,此時 slave 已經完成角色轉換,此 slave 即為 master);
l.+failover-state-wait-promotion slave 127.0.0.1:63792 127.0.0.1 63792 @ mymaster 127.0.0.1 6379(等待其他 sentinel 確認 slave);
m.+promoted-slave slave 127.0.0.1:63792 127.0.0.1 63792 @ mymaster 127.0.0.1 6379(確認成功);
n.+failover-state-reconf-slaves master mymaster 127.0.0.1 6379 (開始對slaves進行reconfig 操作);
o.+slave-reconf-sent slave 127.0.0.1:63791 127.0.0.1 63791 @ mymaster 127.0.0.1 6379 (向指定的 slave 發送“slaveof”指令,告知此 slave 跟隨新的 master);
p. +slave-reconf-inprog slave 127.0.0.1:63791 127.0.0.1 63791 @ mymaster 127.0.0.1 6379(此 slave 正在執行 slaveof + SYNC 過程,如過 slave 收到“+slave-reconf-sent”之後將會執行 slaveof 操作,循環 n);
q.+slave-reconf-done slave 127.0.0.1:63791 127.0.0.1 63791 @ mymaster 127.0.0.1 6379(此 slave 同步完成,此後 leader 可以繼續下一個 slave 的 reconfig 操作);
r.+failover-end master mymaster 127.0.0.1 6379(故障轉移結束);
s.+switch-master mymaster 127.0.0.1 6379 127.0.0.1 63792(故障轉移成功後,各個sentinel 實例開始監控新的 master);
t.+slave slave 127.0.0.1:63791 127.0.0.1 63791 @ mymaster 127.0.0.1 63792(下面幾步在給新的 master 加入 slave);
u.+slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 63792;
v.+sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 63792。
當環境穩定後,我們發現,redis-2 被提升(“promoted”)為 master,且 redis-1 也通過”slave-reconf”過程之後跟隨了 redis-2。
如果此時 redis-0 服務器恢復正常, sentinel 會自動將 redis-0 作為 slave 加入到 redis-2 中。
下面是 redis-0 的 sentinel 控制台輸出的信息。
再次查看 redis-2(當前 master 的 info)
提示:sentinel 實例需要全程處於啟動狀態,如果只啟動 server 而不啟動相應的 sentinel,仍然不能確保 server 能夠正確的被監控和管理。
1)SDOWN 和 ODOWN 轉換過程
【名詞解釋】
SDOWN:subjectively down,直接翻譯的為”主觀”失效,即當前 sentinel 實例認為某個redis 服務為”不可用”狀態。
ODOWN:objectively down,直接翻譯為”客觀”失效,即多個 sentinel 實例都認為 master處於”SDOWN”狀態,那麼此時 master 將處於 ODOWN,ODOWN 可以簡單理解為 master已經被集群確定為”不可用”,將會開啟 failover。
( SDOWN 適合於 master 和 slave,但是 ODOWN 只會使用於 master;當 slave 失效超過”down-after-milliseconds”後,那麼所有 sentinel 實例都會將其標記為”SDOWN”。)
【轉換過程】
a.每個 sentinel 實例在啟動後,都會和已知的 slaves/master 以及其他 sentinels 建立 TCP連接,並周期性發送 PING(默認為 1 秒);
b.在交互中,如果 redis-server 無法在”down-after-milliseconds”時間內響應或者響應錯誤信息,都會被認為此 redis-server 處於 SDOWN 狀態;
c.如果 b.中 SDOWN 的 server 為 master, 那麼此時 sentinel 實例將會向其他 sentinel 間歇性(一秒)發送”is-master-down-by-addr “指令並獲取響應信息, 如果足夠多的 sentinel 實例檢測到 master 處於 SDOWN,那麼此時當前 sentinel 實例標記 master 為 ODOWN…其他 sentinel實例做同樣的交互操作.配置項”sentinel monitor “,如果檢測到 master 處於 SDOWN 狀態的slave 個數達到,那麼此時此 sentinel 實例將會認為 master 處於 ODOWN;
d.每個 sentinel 實例將會間歇性(10 秒)向 master 和 slaves 發送”INFO”指令,如果 master失效且沒有新 master 選出時,每 1 秒發送一次”INFO”;”INFO”的主要目的就是獲取並確認當前集群環境中 slaves 和 master 的存活情況;
經過上述過程後,所有的 sentinel 對 master 失效達成一致後,開始 failover。
2) Sentinel 與 slaves”自動發現”機制
在 sentinel 的配置文件中(local-sentinel.conf),都指定了 port,此 port 就是 sentinel 實例偵聽其他 sentinel 實例建立鏈接的端口.在集群穩定後,最終會每個 sentinel 實例之間都會建立一個 tcp 鏈接,此鏈接中發送”PING”以及類似於”is-master-down-by-addr”指令集,可用用來檢測其他 sentinel 實例的有效性以及”ODOWN”和”failover”過程中信息的交互。
在 sentinel 之間建立連接之前,sentinel 將會盡力和配置文件中指定的 master 建立連接。sentinel 與 master 的連接中的通信主要是基於 pub/sub 來發布和接收信息,發布的信息內容包括當前 sentinel 實例的偵聽端口。
3)Leader 選舉
其實在 sentinels 故障轉移中,仍然需要一個“Leader”來調度整個過程:master 的選舉以及 slave 的重配置和同步。當集群中有多個 sentinel 實例時,如何選舉其中一個 sentinel 為leader 呢?
redis2.8.7的選舉有兩個條件,首先是要下面的條件過濾掉一些節點
我們期望有足夠多的sentinel實例, 這樣能夠確保當leader失效時, 能夠選舉某個sentinel為 leader,以便進行 failover。如果 leader 無法產生,比如較少的 sentinels 實例有效,那麼failover 過程將無法繼續。
4)failover 過程
在 Leader 觸發 failover 之前,首先 wait 數秒(隨即 0~5),以便讓其他 sentinel 實例准備和調整,如果一切正常,那麼 leader 就需要開始將一個 salve 提升為 master,此 slave 必須為狀態良好(不能處於 SDOWN/ODOWN 狀態)且權重值最低(redis.conf 中)的, 當 master 身份被確認後,開始 failover。
1)redis 的水平擴展。 前文所實現的是 redis 的主從 HA 集群 (從服務器做備份而存在) ,試想當緩存到了一個級別一台服務器已經不能滿足了,就想到了 redis 的分布式,將緩存放到分配到多台服務器中。Redis 官方也提供了 redis cluster 來實現分布式,但目還沒有正式版發布(redis 3.0貌似提供了支持,還沒來得及研究)。Java 可以通過 jedis 的 ShardedJedis 來做分片 。
2)redis 的監控。一個東西運行的是否正常、穩定和性能情況,這就涉及到了對它的監控。目前 redis 的監控工具有:redmon、redis-live 等。本文暫不做監控,讀者可參考其他資料學習使用。
3)集群時的讀寫分離。主用來寫,從用來讀。在 redis 的 HA 集群中,主從服務器是變化的,這就導致在程序中,不容易獲得當前哪台服務是主,哪幾台服務是從。我們可以自己通過代碼實現獲得從服務器的 jedis 實例,具體可以參見Redis中sentinel集群的搭建和Jedis測試 圖文教程[二] 從而達到讀寫分離。
4)“緩存數據同步”也是所有緩存工具的一個必須思考的問題。