Redis Cluster深入与实践(续)
前文回顧
上一篇文章基于redis的分布式鎖實(shí)現(xiàn)寫了基于redis實(shí)現(xiàn)的分布式鎖。分布式環(huán)境下,不會(huì)還使用單點(diǎn)的redis,做到高可用和容災(zāi),起碼也是redis主從。redis的單線程工作,一臺(tái)物理機(jī)只運(yùn)行一個(gè)redis實(shí)例太過(guò)浪費(fèi),redis單機(jī)顯然是存在單點(diǎn)故障的隱患。內(nèi)存資源往往受限,縱向不停擴(kuò)展內(nèi)存并不是很實(shí)際,因此橫向可伸縮擴(kuò)展,需要多臺(tái)主機(jī)協(xié)同提供服務(wù),即分布式下多個(gè)Redis實(shí)例協(xié)同運(yùn)行。
在之前的文章Redis Cluster深入與實(shí)踐介紹過(guò)Redis Cluster的相關(guān)內(nèi)容,之前特地花時(shí)間在redis官網(wǎng)看了redis cluster的相關(guān)文檔和實(shí)現(xiàn)。本文是那篇文章的續(xù)集,因?yàn)楣P者最近在調(diào)研redis的主從切換到redis 集群的方案,將會(huì)講下redis集群的幾種方案選型和redis cluster的實(shí)踐。
redis集群的幾種實(shí)現(xiàn)方式如下:
- 客戶端分片,如redis的Java客戶端jedis也是支持的,使用一致性hash
- 基于代理的分片,如codis和Twemproxy
- 路由查詢, redis-cluster
下面我們分別介紹下這幾種方案。
客戶端分片
Redis Sharding是Redis Cluster出來(lái)之前,業(yè)界普遍使用的多Redis實(shí)例集群方法。其主要思想是采用哈希算法將Redis數(shù)據(jù)的key進(jìn)行散列,通過(guò)hash函數(shù),特定的key會(huì)映射到特定的Redis節(jié)點(diǎn)上。java redis客戶端驅(qū)動(dòng)jedis,支持Redis Sharding功能,即ShardedJedis以及結(jié)合緩存池的ShardedJedisPool。
ShardingRedis Sentinel提供了主備模式下Redis監(jiān)控、故障轉(zhuǎn)移功能達(dá)到系統(tǒng)的高可用性。在主Redis宕機(jī)時(shí),備Redis接管過(guò)來(lái),上升為主Redis,繼續(xù)提供服務(wù)。主備共同組成一個(gè)Redis節(jié)點(diǎn),通過(guò)自動(dòng)故障轉(zhuǎn)移,保證了節(jié)點(diǎn)的高可用性。
客戶端sharding技術(shù)其優(yōu)勢(shì)在于非常簡(jiǎn)單,服務(wù)端的Redis實(shí)例彼此獨(dú)立,相互無(wú)關(guān)聯(lián),每個(gè)Redis實(shí)例像單服務(wù)器一樣運(yùn)行,非常容易線性擴(kuò)展,系統(tǒng)的靈活性很強(qiáng)。
客戶端sharding的劣勢(shì)也是很明顯的。由于sharding處理放到客戶端,規(guī)模進(jìn)一步擴(kuò)大時(shí)給運(yùn)維帶來(lái)挑戰(zhàn)。客戶端sharding不支持動(dòng)態(tài)增刪節(jié)點(diǎn)。服務(wù)端Redis實(shí)例群拓?fù)浣Y(jié)構(gòu)有變化時(shí),每個(gè)客戶端都需要更新調(diào)整。連接不能共享,當(dāng)應(yīng)用規(guī)模增大時(shí),資源浪費(fèi)制約優(yōu)化。
基于代理的分片
客戶端發(fā)送請(qǐng)求到一個(gè)代理組件,代理解析客戶端的數(shù)據(jù),并將請(qǐng)求轉(zhuǎn)發(fā)至正確的節(jié)點(diǎn),最后將結(jié)果回復(fù)給客戶端。
該模式的特性如下:
- 透明接入,業(yè)務(wù)程序不用關(guān)心后端Redis實(shí)例,切換成本低。
- Proxy 的邏輯和存儲(chǔ)的邏輯是隔離的。
- 代理層多了一次轉(zhuǎn)發(fā),性能有所損耗。
簡(jiǎn)單的結(jié)構(gòu)圖如下:
proxy主流的組件有:Twemproxy和Codis。
Twemproxy
Twemproxy也叫nutcraker,是twtter開(kāi)源的一個(gè)redis和memcache代理服務(wù)器程序。redis作為一個(gè)高效的緩存服務(wù)器,非常具有應(yīng)用價(jià)值。但在用戶數(shù)據(jù)量增大時(shí),需要運(yùn)行多個(gè)redis實(shí)例,此時(shí)將迫切需要一種工具統(tǒng)一管理多個(gè)redis實(shí)例,避免在每個(gè)客戶端管理所有連接帶來(lái)的不方便和不易維護(hù),Twemproxy即為此目標(biāo)而生。
Twemproxy有以下幾個(gè)特點(diǎn):
- 快
- 輕量級(jí)
- 維持永久的服務(wù)端連接
- 支持失敗節(jié)點(diǎn)自動(dòng)刪除;可以設(shè)置重新連接該節(jié)點(diǎn)的時(shí)間,還可以設(shè)置連接多少次之后刪除該節(jié)點(diǎn)
- 支持設(shè)置HashTag;通過(guò)HashTag可以自己設(shè)定將同一類型的key映射到同一個(gè)實(shí)例上去。
- 減少與redis的直接連接數(shù),保持與redis的長(zhǎng)連接,可設(shè)置代理與后臺(tái)每個(gè)redis連接的數(shù)目
- 自帶一致性hash算法,能夠?qū)?shù)據(jù)自動(dòng)分片到后端多個(gè)redis實(shí)例上;支持多種hash算法,可以設(shè)置后端實(shí)例的權(quán)重,目前redis支持的hash算法有:one_at_a_time、md5、crc16、crc32、fnv1_64、fnv1a_64、fnv1_32、fnv1a_32、hsieh、murmur、jenkins。
- 支持redis pipelining request,將多個(gè)連接請(qǐng)求,組成reids pipelining統(tǒng)一向redis請(qǐng)求。
- 支持狀態(tài)監(jiān)控;可設(shè)置狀態(tài)監(jiān)控ip和端口,訪問(wèn)ip和端口可以得到一個(gè)json格式的狀態(tài)信息串;可設(shè)置監(jiān)控信息刷新間隔時(shí)間。
TwemProxy 官網(wǎng)介紹了如上的特性。TwemProxy的使用可以像訪問(wèn)redis客戶端一樣訪問(wèn)TwemProxy。然而Twitter已經(jīng)很久放棄了更新TwemProxy。Twemproxy最大的痛點(diǎn)在于,無(wú)法平滑地?cái)U(kuò)容/縮容。Twemproxy另一個(gè)痛點(diǎn)是,運(yùn)維不友好,甚至沒(méi)有控制面板。
Codis
Codis是豌豆莢開(kāi)源的redis集群方案,是一個(gè)分布式 Redis 解決方案, 對(duì)于上層的應(yīng)用來(lái)說(shuō), 連接到 Codis Proxy 和連接原生的 Redis Server 沒(méi)有顯著區(qū)別 , 上層應(yīng)用可以像使用單機(jī)的 Redis 一樣使用, Codis 底層會(huì)處理請(qǐng)求的轉(zhuǎn)發(fā), 不停機(jī)的數(shù)據(jù)遷移等工作, 所有后邊的一切事情, 對(duì)于前面的客戶端來(lái)說(shuō)是透明的, 可以簡(jiǎn)單的認(rèn)為后邊連接的是一個(gè)內(nèi)存無(wú)限大的 Redis 服務(wù)。
Codis當(dāng)前最新release 版本為 codis-3.2,codis-server 基于 redis-3.2.8。有一下組件組成:
Codis架構(gòu)- Codis Server:基于 redis-3.2.8 分支開(kāi)發(fā)。增加了額外的數(shù)據(jù)結(jié)構(gòu),以支持 slot 有關(guān)的操作以及數(shù)據(jù)遷移指令。
- Codis Proxy:客戶端連接的 Redis 代理服務(wù), 實(shí)現(xiàn)了 Redis 協(xié)議。 除部分命令不支持以外(不支持的命令列表),表現(xiàn)的和原生的 Redis 沒(méi)有區(qū)別(就像 Twemproxy)。
- Codis Dashboard:集群管理工具,支持 codis-proxy、codis-server 的添加、刪除,以及據(jù)遷移等操作。在集群狀態(tài)發(fā)生改變時(shí),codis-dashboard 維護(hù)集群下所有 codis-proxy 的狀態(tài)的一致性。 對(duì)于同一個(gè)業(yè)務(wù)集群而言,同一個(gè)時(shí)刻 codis-dashboard 只能有 0個(gè)或者1個(gè);所有對(duì)集群的修改都必須通過(guò) codis-dashboard 完成。
- Codis Admin:集群管理的命令行工具。 可用于控制 codis-proxy、codis-dashboard 狀態(tài)以及訪問(wèn)外部存儲(chǔ)。
- Codis FE:集群管理界面。 多個(gè)集群實(shí)例共享可以共享同一個(gè)前端展示頁(yè)面; 通過(guò)配置文件管理后端 codis-dashboard 列表,配置文件可自動(dòng)更新。
- Storage:為集群狀態(tài)提供外部存儲(chǔ)。 提供 Namespace 概念,不同集群的會(huì)按照不同 product name 進(jìn)行組織;目前僅提供了 Zookeeper、Etcd、Fs 三種實(shí)現(xiàn),但是提供了抽象的 interface 可自行擴(kuò)展。
至于具體的安裝與使用,見(jiàn)官網(wǎng)CodisLabs,不在此涉及。
Codis的特性:
- Codis支持的命令更加豐富,基本支持redis的命令。
- 遷移成本低,遷移到codis沒(méi)這么麻煩,只要使用的redis命令在codis支持的范圍之內(nèi),只要修改一下配置即可接入。
- Codis提供的運(yùn)維工具更加友好,提供web圖形界面管理集群。
- 支持多核心CPU,twemproxy只能單核
- 支持group劃分,組內(nèi)可以設(shè)置一個(gè)主多個(gè)從,通過(guò)sentinel 監(jiān)控redis主從,當(dāng)主down了自動(dòng)將從切換為主
路由查詢
Redis Cluster是一種服務(wù)器Sharding技術(shù),3.0版本開(kāi)始正式提供。Redis Cluster并沒(méi)有使用一致性hash,而是采用slot(槽)的概念,一共分成16384個(gè)槽。將請(qǐng)求發(fā)送到任意節(jié)點(diǎn),接收到請(qǐng)求的節(jié)點(diǎn)會(huì)將查詢請(qǐng)求發(fā)送到正確的節(jié)點(diǎn)上執(zhí)行。當(dāng)客戶端操作的key沒(méi)有分配到該node上時(shí),就像操作單一Redis實(shí)例一樣,當(dāng)客戶端操作的key沒(méi)有分配到該node上時(shí),Redis會(huì)返回轉(zhuǎn)向指令,指向正確的node,這有點(diǎn)兒像瀏覽器頁(yè)面的302 redirect跳轉(zhuǎn)。
Redis集群,要保證16384個(gè)槽對(duì)應(yīng)的node都正常工作,如果某個(gè)node發(fā)生故障,那它負(fù)責(zé)的slots也就失效,整個(gè)集群將不能工作。為了增加集群的可訪問(wèn)性,官方推薦的方案是將node配置成主從結(jié)構(gòu),即一個(gè)master主節(jié)點(diǎn),掛n個(gè)slave從節(jié)點(diǎn)。這時(shí),如果主節(jié)點(diǎn)失效,Redis Cluster會(huì)根據(jù)選舉算法從slave節(jié)點(diǎn)中選擇一個(gè)上升為主節(jié)點(diǎn),整個(gè)集群繼續(xù)對(duì)外提供服務(wù)。
Redis Cluster特點(diǎn):
- 無(wú)中心架構(gòu),支持動(dòng)態(tài)擴(kuò)容,對(duì)業(yè)務(wù)透明
- 具備Sentinel的監(jiān)控和自動(dòng)Failover能力
- 客戶端不需要連接集群所有節(jié)點(diǎn),連接集群中任何一個(gè)可用節(jié)點(diǎn)即可
- 高性能,客戶端直連redis服務(wù),免去了proxy代理的損耗
缺點(diǎn)是運(yùn)維也很復(fù)雜,數(shù)據(jù)遷移需要人工干預(yù),只能使用0號(hào)數(shù)據(jù)庫(kù),不支持批量操作,分布式邏輯和存儲(chǔ)模塊耦合等。
選型最后確定redis cluster。主要原因是性能高,去中心化支持?jǐn)U展。運(yùn)維方面的數(shù)據(jù)遷移暫時(shí)業(yè)內(nèi)也沒(méi)有特別成熟的方案解決,redis cluster是redis官方提供,我們期待redis官方在后面能夠完美支持。
安裝
官方推薦集群至少需要六個(gè)節(jié)點(diǎn),即三主三從。六個(gè)節(jié)點(diǎn)的配置文件基本相同,只需要修改端口號(hào)。
port 7000 cluster-enabled yes #開(kāi)啟集群模式 cluster-config-file nodes.conf cluster-node-timeout 5000 appendonly yes 復(fù)制代碼啟動(dòng)后,可以看到如下的日志。
[82462] 26 Nov 11:56:55.329 * No cluster configuration found, I'm 97a3a64667477371c4479320d683e4c8db5858b1
由于沒(méi)有nodes.conf存在,每個(gè)實(shí)例啟動(dòng)后都會(huì)給自己分配一個(gè)ID。為了在集群的環(huán)境中有一個(gè)唯一的名字,該ID將會(huì)被永久使用。每個(gè)實(shí)例都會(huì)保存其他節(jié)點(diǎn)使用的ID,而不是通過(guò)IP和端口。IP和端口可能會(huì)改變,但是唯一的node ID將不會(huì)改變直至該node的死亡。
我們現(xiàn)在已經(jīng)啟動(dòng)了六個(gè)redis實(shí)例, 需要通過(guò)寫一些有意義的配置信息到各個(gè)節(jié)點(diǎn)來(lái)創(chuàng)建集群。 redis cluster的命令行工具redis-trib,利用Ruby程序在實(shí)例上執(zhí)行一些特殊的命令,很容易實(shí)現(xiàn)創(chuàng)建新的集群、檢查或者reshard現(xiàn)有的集群等。
./redis-trib.rb create --replicas 1 127.0.0.1:7000 127.0.0.1:7001 \ 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 復(fù)制代碼--replicas 1參數(shù)是將每個(gè)master帶上一個(gè)slave。
配置JedisClusterConfig
public class JedisClusterConfig {private static Logger logger = LoggerFactory.getLogger(JedisClusterConfig.class);("${redis.cluster.nodes}")private String clusterNodes;("${redis.cluster.timeout}")private int timeout;("${redis.cluster.max-redirects}")private int redirects;private JedisPoolConfig jedisPoolConfig;public RedisClusterConfiguration getClusterConfiguration() {Map<String, Object> source = new HashMap();source.put("spring.redis.cluster.nodes", clusterNodes);logger.info("clusterNodes: {}", clusterNodes);source.put("spring.redis.cluster.max-redirects", redirects);return new RedisClusterConfiguration(new MapPropertySource("RedisClusterConfiguration", source));}public JedisConnectionFactory getConnectionFactory() {JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(getClusterConfiguration());jedisConnectionFactory.setTimeout(timeout);return jedisConnectionFactory;}public JedisClusterConnection getJedisClusterConnection() {return (JedisClusterConnection) getConnectionFactory().getConnection();}public RedisTemplate getRedisTemplate() {RedisTemplate clusterTemplate = new RedisTemplate();clusterTemplate.setConnectionFactory(getConnectionFactory());clusterTemplate.setKeySerializer(new StringRedisSerializer());clusterTemplate.setDefaultSerializer(new GenericJackson2JsonRedisSerializer());return clusterTemplate;}} 復(fù)制代碼可以配置密碼,cluster對(duì)密碼支持不太友好,如果對(duì)集群設(shè)置密碼,那么requirepass和masterauth都需要設(shè)置,否則發(fā)生主從切換時(shí),就會(huì)遇到授權(quán)問(wèn)題。
配置redis cluster
redis: cluster: enabled: true timeout: 2000 max-redirects: 8 nodes: 127.0.0.1:7000,127.0.0.1:7001 復(fù)制代碼主要配置了redis cluster的節(jié)點(diǎn)、超時(shí)時(shí)間等。
使用RedisTemplate
(SpringRunner.class) public class RedisConfigTest {RedisTemplate redisTemplate;public void clusterTest() {redisTemplate.opsForValue().set("foo", "bar");System.out.println(redisTemplate.opsForValue().get("foo"));} } 復(fù)制代碼用法很簡(jiǎn)單,注入RedisTemplate即可進(jìn)行操作,RedisTemplate用法比較豐富,可以自行查閱。
總結(jié)
本文主要講了redis集群的選型,主要有三種:客戶端分片、基于代理的分片以及路由查詢。對(duì)于前兩種方式,分別進(jìn)行簡(jiǎn)單地介紹,最后選擇redis官方提供的redis cluster方案,并進(jìn)行了實(shí)踐。雖然正式版的推出時(shí)間不長(zhǎng),目前成功實(shí)踐的案例也還不多,但是總體來(lái)說(shuō),redis cluster的整個(gè)設(shè)計(jì)是比較簡(jiǎn)單的,大部分操作都可以按照單點(diǎn)的操作流程進(jìn)行操作。筆者使用的jedis客戶端支持JedisCluster也是比較好,用起來(lái)也很方便。其實(shí)還有個(gè)壓測(cè)的數(shù)據(jù),后面再補(bǔ)上吧。
訂閱最新文章,歡迎關(guān)注我的公眾號(hào)
參考
總結(jié)
以上是生活随笔為你收集整理的Redis Cluster深入与实践(续)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 第五期 IP数据包结构和OSI第三层网络
- 下一篇: nodejs(log4js)服务中应用s