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

轉自:http://warm-breeze.iteye.com/blog/2020413

本文主要介紹一種通過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實例的連接都必須從連接池中獲取;


該項目的GitHub主頁: https://github.com/warmbreeze/sharded-jedis-sentinel-pool

發佈了16 篇原創文章 · 獲贊 0 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章