1. 程式人生 > >基於Redis Sentinel的Redis叢集(主從&Sharding)高可用方案

基於Redis Sentinel的Redis叢集(主從&Sharding)高可用方案

本文主要介紹一種通過Jedis&Sentinel實現Redis叢集高可用方案,該方案需要使用Jedis2.2.2及以上版本(強制),Redis2.8及以上版本(可選,Sentinel最早出現在Redis2.4中,Redis2.8中Sentinel更加穩定),Redis叢集是以分片(Sharding)加主從的方式搭建,滿足可擴充套件性的要求;

Redis Sentinel介紹

Redis Sentinel是Redis官方提供的叢集管理工具,主要有三大功能:  監控,能持續監控Redis的主從例項是否正常工作;  通知,當被監控的Redis例項出問題時,能通過API通知系統管理員或其他程式;  自動故障恢復,如果主例項無法正常工作,Sentinel將啟動故障恢復機制把一個從例項提升為主例項,其他的從例項將會被重新配置到新的主例項,且應用程式會得到一個更換新地址的通知。  Redis Sentinel是一個分散式系統,可以部署多個Sentinel例項來監控同一組Redis例項,它們通過Gossip協議來確定一個主例項宕機,通過Agreement協議來執行故障恢復和配置變更,一般在生產環境中部署多個例項來提高系統可用性,只要有一個Sentinel例項執行正常,就能保證被監控的Redis例項執行正常(類似Zookeeper,通過多個Zookeeper來提高系統可用性);  本文不涉及Sentinel的實現細節和工作原理,讀者可以閱讀其他文章瞭解;

Redis HA方案

HA的關鍵在於避免單點故障及故障恢復,在Redis Cluster未釋出之前,Redis一般以主/從方式部署(這裡討論的應用從例項主要用於備份,主例項提供讀寫,有不少應用是讀寫分離的,讀寫操作需要取不同的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例項會及時更新到新的主例項地址。  筆者所在的公司先使用了方案1一段時間後,發現keepalived在有些情況下會導致資料丟失,keepalived通過shell指令碼進行主從切換,配置複雜,而且keepalived成為新的單點,後來選用了方案3,使用Redis官方解決方案;(方案2需要編寫大量的監控程式碼,沒有方案3簡便,網上有人使用方案2讀者可自行檢視)

選用Sentinel出現的問題

Sentinel&Jedis看上去是個完美的解決方案,這句話只說對了一半,在無分片的情況是這樣,但我們的應用使用了資料分片-sharing,資料被平均分佈到4個不同的例項上,每個例項以主從結構部署,Jedis沒有提供基於Sentinel的ShardedJedisPool,也就是說在4個分片中,如果其中一個分片發生主從切換,應用所使用的ShardedJedisPool無法獲得通知,所有對那個分片的操作將會失敗。  本文提供一個基於Sentinel的ShardedJedisPool,能及時感知所有分片主從切換行為,進行連線池重建,原始碼見ShardedJedisSentinelPool.java

ShardedJedisSentinelPool實現分析

建構函式

 類似之前的Jedis Pool的構造方法,需要引數poolConfig提供諸如maxIdle,maxTotal之類的配置,masters是一個List,用來儲存所有分片Master在Sentinel中配置的名字(注意master的順序不能改變,因為Shard演算法是依據分片位置進行計算,如果順序錯誤將導致資料儲存混亂),sentinels是一個Set,其中存放所有Sentinel的地址(格式:IP:PORT,如127.0.0.1:26379),順序無關;

初始化連線池

在建構函式中,通過方法  取得當前所有分片的master地址(IP&PORT),對每個分片,通過順次連線Sentinel例項,獲取該分片的master地址,如果無法獲得,即所有Sentinel都無法連線,將休眠1秒後繼續重試,直到取得所有分片的master地址,程式碼塊如下:  通過  初始化連線池,到此連線池中的所有連線都指向分片的master;

監控每個Sentinel

在方法  最後,會為每個Sentinel啟動一個Thread來監控Sentinel做出的更改:  該執行緒的run方法通過Jedis Pub/Sub API(實現JedisPubSub介面,並通過jedis.subscribe進行訂閱)向Sentinel例項訂閱“+switch-master”頻道,當Sentinel進行主從切換時,該執行緒會得到新Master地址的通知,通過master name判斷哪個分片進行了切換,將新master地址替換原來位置的地址,並呼叫initPool(List masters)進行Jedis連線池重建;後續所有通過該連線池取得的連線都指向新Master地址,對應用程式透明;

應用示例

  總結

本文通過現實中遇到的問題,即在Redis資料分片的情況下,在使用Sentinel做HA時,如何做到主從的切換對應用程式透明,通過Jedis的Pub/Sub功能,能同時監控多個分片的主從切換情況,並通過監聽到的新地址重新構造連線池,後續從連線池中取得的所有連線都指向新地址。該方案的關鍵是:使用sentinel做HA,Jedis版本必須2.2.2及以上,所有訪問Redis例項的連線都必須從連線池中獲取;