美文网首页
Redis 功能入门大全和基于scala实现示例(6) -- 集

Redis 功能入门大全和基于scala实现示例(6) -- 集

作者: 小赵营 | 来源:发表于2019-04-18 22:02 被阅读0次

欢迎转载。转载请注明出处。

image

概要

  • Redis server 基于5.0.0的stable版本
  • Client基于 Jedis 2.9.0
  • Scala 基于 2.11.X

本文内容:

  • redis集群模式配置参数说明
  • 以实例说明如何配置集群模式
  • 创建集群模式连接池的scala实现
  • 遇到和解决问题

cluster服务配置参数

cluster模式所有节点都是Redis实例,不存在sentinel模式下的监控进程。所以不需要附加配置文件,一切配置信息在redis-port.conf。如下:

cluster-enabled yes
cluster-config-file nodes-6379.conf #启动该进程对应的配置文件名称
cluster-node-timeout 15000
# cluster-replica-no-failover no
# cluster-require-full-coverage yes
# cluster-migration-barrier 1

如何搭建cluster集群

搭建和配置集群是redis模式中“最复杂的”。
下面就逐步说明:

  1. 启动多个实例,以6个实例(3主3从为例)所有数据只有一个备份。启动命令: redis-server conf/xxxx.conf
  2. 节点配对。建立节点间连接。 redis-cli -h ip -p port cluster meet destip dest port ,生成40字符ID标识节点,后续据此区分节点.
  3. 建立集群备份机制,分配数据如何存放。redis-cli -h ip -p port cluster addslots {0...5461} 自动分配失败,可以手动修改。分配完成之后,所有给其它的部分节点设置了slot。完成之后,所有节点都是主节点,还需要建立主从关系.
  4. 建立主从关系. cluster replicate 主节点的id,使用命令查看对应关系./cluster-cli -h ip -p port cluster nodes
数据存放和分区

cluster模式下,每个master节点存储部分数据,master集合存储完整一份数据。

在如何搭建cluster集群时步骤3 提到需要分配数据,那么cluster集群是如何分配数据,也是如何进行数据分区哪?

分布式系统数据分区分为哈希分区和顺序分区。Redis使用哈希空间,所有数据映射到一个整数集合内,也就是我们说的slot(哈希槽)。步骤3提到数据分区就是设置某个节点存放slot的范围。slot值的计算公式:slot=CRC16(key)&16383。通过CRC16算法强制计算出slot值,存储在对应的节点上。数据分区如同小学生入学分班,根据表现分散在各个地方。

scala创建cluster模式连接池

  • 集群模式下配置参数

    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)
      
      }
    }
    
  • 创建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.
      //节点故障问题会导致主从变换,需要周期更新状态。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
     }
    //批量数据分配到不同的slot上,要对k-v进行分组,才能使用pipeline 事务等批处理
      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)
       }
      }
    }  
    

以上是集群模式代码实现,拿来即用。祝你嗨皮!

实践中遇到问题

集群模式下,Redis接受到任何k-v相关的命令,都会先计算出slot的值,然后根据slot去访问节点。数据是分布在不同的Redis节点上,使用客户端命令访问时,会出现数据不在该客户端访问节点上,Redis提供重定向(MOVED)实现对数据的访问。客户端登录时,通过设置可以实现自动重定向: ./redis-cli -h ip -p port -c.发生重定向时,提示**-> Redirected to slot [5798] located **

访问流程:key -> slot -> node -> 数据操作

原因: 读写分离指 redis 服务端 提供机制,只有在master提供读写功能,slave只能读不能写。用户利用该原理在客户端实现 读写分离的功能,redis提供了相关API。

批量设置slot失败 (error) ERR Invalid or out of range slot?

原因:redis集群服务创建问题。检查各个集群节点的IP和port

集群模式是更高复杂度系统,数据分区、故障迁移、负载均衡、开发运维困难等,每一个都是大课题,有机会在讨论。

后记

Redis提供缓存服务分布式锁解决数据一致性问题;数据恢复和容灾需要优化Redis配置;负载均衡用户使用第三方工具解决 ......等等。 遗留问题随着使用深入会有整理出更多的文档。

sohutv开源了Redis运维平台CacheCloud,非常棒的运维工具。

参考资料

  1. Redis开发和运维 [付磊 张益军]
  2. Redisn 英文官网
  3. Redis中国社区

写在最后:

本系列redis基础功能和基于scala的实现到这里,依然到了尾声.理论层面上文章很多,我只是蜻蜓点水略过,深入探索靠实践.scala代码实现读写和各中模式下访问,奉行拿来主义.

我们征程是星辰大海,持续探索才有是未来.
被人追问:只有那点访问和点赞数,写作意义何在?有一个阅读量也是需求。凡事讲意义,就是功利。

对于ta,出门左转,不送。
对读者,只有感谢.感谢你们.

相关文章

网友评论

      本文标题:Redis 功能入门大全和基于scala实现示例(6) -- 集

      本文链接:https://www.haomeiwen.com/subject/vmyiwqtx.html