歡迎轉(zhuǎn)載。轉(zhuǎn)載請注明出處。
概要
- Redis server 基于5.0.0的stable版本
- Client基于 Jedis 2.9.0
- Scala 基于 2.11.X
本文內(nèi)容:
- redis集群模式配置參數(shù)說明
- 以實例說明如何配置集群模式
- 創(chuàng)建集群模式連接池的scala實現(xiàn)
- 遇到和解決問題
cluster服務(wù)配置參數(shù)
cluster模式所有節(jié)點都是Redis實例,不存在sentinel模式下的監(jiān)控進(jìn)程。所以不需要附加配置文件,一切配置信息在redis-port.conf。如下:
cluster-enabled yes
cluster-config-file nodes-6379.conf #啟動該進(jìn)程對應(yīng)的配置文件名稱
cluster-node-timeout 15000
# cluster-replica-no-failover no
# cluster-require-full-coverage yes
# cluster-migration-barrier 1
如何搭建cluster集群
搭建和配置集群是redis模式中“最復(fù)雜的”。
下面就逐步說明:
- 啟動多個實例,以6個實例(3主3從為例)所有數(shù)據(jù)只有一個備份。啟動命令:
redis-server conf/xxxx.conf - 節(jié)點配對。建立節(jié)點間連接。
redis-cli -h ip -p port cluster meet destip dest port,生成40字符ID標(biāo)識節(jié)點,后續(xù)據(jù)此區(qū)分節(jié)點. - 建立集群備份機制,分配數(shù)據(jù)如何存放。
redis-cli -h ip -p port cluster addslots {0...5461}自動分配失敗,可以手動修改。分配完成之后,所有給其它的部分節(jié)點設(shè)置了slot。完成之后,所有節(jié)點都是主節(jié)點,還需要建立主從關(guān)系. - 建立主從關(guān)系.
cluster replicate 主節(jié)點的id,使用命令查看對應(yīng)關(guān)系./cluster-cli -h ip -p port cluster nodes
數(shù)據(jù)存放和分區(qū)
cluster模式下,每個master節(jié)點存儲部分?jǐn)?shù)據(jù),master集合存儲完整一份數(shù)據(jù)。
在如何搭建cluster集群時步驟3 提到需要分配數(shù)據(jù),那么cluster集群是如何分配數(shù)據(jù),也是如何進(jìn)行數(shù)據(jù)分區(qū)哪?
分布式系統(tǒng)數(shù)據(jù)分區(qū)分為哈希分區(qū)和順序分區(qū)。Redis使用哈??臻g,所有數(shù)據(jù)映射到一個整數(shù)集合內(nèi),也就是我們說的slot(哈希槽)。步驟3提到數(shù)據(jù)分區(qū)就是設(shè)置某個節(jié)點存放slot的范圍。slot值的計算公式:slot=CRC16(key)&16383。通過CRC16算法強制計算出slot值,存儲在對應(yīng)的節(jié)點上。數(shù)據(jù)分區(qū)如同小學(xué)生入學(xué)分班,根據(jù)表現(xiàn)分散在各個地方。
scala創(chuàng)建cluster模式連接池
-
集群模式下配置參數(shù)
trait MyClusterPool extends Pool { def pool: JedisCluster } class JedisClusterConf(conf: JedisPoolConfig, nodes: Set[HostAndPort]) extends MyClusterPool { lazy private val pl = init override def pool : JedisCluster = pl def init: JedisCluster = { val s: util.Set[HostAndPort] = JavaConversions.setAsJavaSet[HostAndPort](nodes) new JedisCluster(nodes, conf) } }
-
創(chuàng)建cluster單例對象
object RedisFactory extends LogSupport { def newJedisPool(conf: JedisPoolConfig, nodes: Set[HostAndPort]): MyClusterPool = { logWarning("#### RedisFactory create ClusterPool start") val ret = new JedisClusterConf(conf, nodes) logWarning("#### RedisFactory create ClusterPool end") ret } } -
如何使用
trait RedisClusterInterface extends LogSupport with RedisAction with RedisPara{ val hostList = nodesList.map(HostAndPort.parseString(_)) val nodes = JavaConversions.setAsJavaSet[HostAndPort](hostList) lazy val handler = new JedisSlotBasedConnectionHandler(nodes, conf, 2000) private lazy val jedisCluster = { //cluster pool RedisFactory.newJedisPool(conf, hostList) } def connect: JedisCluster = { redisCluster.pool } lazy val clusterNodes = jedisCluster.pool.getClusterNodes //todo in formal version val should be update period. //節(jié)點故障問題會導(dǎo)致主從變換,需要周期更新狀態(tài)。jedis沒有自動更新機制 lazy val clusterInfoCache = { val cache = new JedisClusterInfoCache(conf, 1000) val it = nodesList.map(HostAndPort.parseString(_)) for(el <- it.toList) { val ne = el val jedis = new Jedis(ne.getHost,ne.getPort) cache.discoverClusterNodesAndSlots(jedis) jedis.close() //close connect jedis } for(el <- it.toList) cache.setupNodeIfNotExist(el) cache } //批量數(shù)據(jù)分配到不同的slot上,要對k-v進(jìn)行分組,才能使用pipeline 事務(wù)等批處理 def group(t: List[(String, util.HashMap[String,String])]) = { val ret = t.map{ case (k, v) => val slot = JedisClusterCRC16.getSlot(k) val conn = clusterInfoCache.getSlotPool(slot) (conn, (k,v)) } ret.groupBy(_._1).map { el => val key = el._1 val re = el._2.map { v => (v._2._1,v._2._2) } (key, re) } } }
以上是集群模式代碼實現(xiàn),拿來即用。祝你嗨皮!
實踐中遇到問題
集群模式下,Redis接受到任何k-v相關(guān)的命令,都會先計算出slot的值,然后根據(jù)slot去訪問節(jié)點。數(shù)據(jù)是分布在不同的Redis節(jié)點上,使用客戶端命令訪問時,會出現(xiàn)數(shù)據(jù)不在該客戶端訪問節(jié)點上,Redis提供重定向(MOVED)實現(xiàn)對數(shù)據(jù)的訪問??蛻舳说卿洉r,通過設(shè)置可以實現(xiàn)自動重定向:
./redis-cli -h ip -p port -c.發(fā)生重定向時,提示**-> Redirected to slot [5798] located **
訪問流程:key -> slot -> node -> 數(shù)據(jù)操作
原因: 讀寫分離指 redis 服務(wù)端 提供機制,只有在master提供讀寫功能,slave只能讀不能寫。用戶利用該原理在客戶端實現(xiàn) 讀寫分離的功能,redis提供了相關(guān)API。
批量設(shè)置slot失敗 (error) ERR Invalid or out of range slot?
原因:redis集群服務(wù)創(chuàng)建問題。檢查各個集群節(jié)點的IP和port
集群模式是更高復(fù)雜度系統(tǒng),數(shù)據(jù)分區(qū)、故障遷移、負(fù)載均衡、開發(fā)運維困難等,每一個都是大課題,有機會在討論。
后記
Redis提供緩存服務(wù)分布式鎖解決數(shù)據(jù)一致性問題;數(shù)據(jù)恢復(fù)和容災(zāi)需要優(yōu)化Redis配置;負(fù)載均衡用戶使用第三方工具解決 ......等等。 遺留問題隨著使用深入會有整理出更多的文檔。
sohutv開源了Redis運維平臺CacheCloud,非常棒的運維工具。
參考資料
- Redis開發(fā)和運維 [付磊 張益軍]
- Redisn 英文官網(wǎng)
- Redis中國社區(qū)
寫在最后:
本系列redis基礎(chǔ)功能和基于scala的實現(xiàn)到這里,依然到了尾聲.理論層面上文章很多,我只是蜻蜓點水略過,深入探索靠實踐.scala代碼實現(xiàn)讀寫和各中模式下訪問,奉行拿來主義.
我們征程是星辰大海,持續(xù)探索才有是未來.
被人追問:只有那點訪問和點贊數(shù),寫作意義何在?有一個閱讀量也是需求。凡事講意義,就是功利。
對于ta,出門左轉(zhuǎn),不送。
對讀者,只有感謝.感謝你們.