分布式缓存中间件Codis简单介绍

Posted 海科融通技术团队

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了分布式缓存中间件Codis简单介绍相关的知识,希望对你有一定的参考价值。

Codis 是一个分布式 Redis 解决方案, 对于上层的应用来说, 连接到 Codis Proxy 和连接原生的 Redis Server 没有显著区别 (不支持的命令列表), 上层应用可以像使用单机的 Redis 一样使用, Codis 底层会处理请求的转发, 不停机的数据迁移等工作, 所有后边的一切事情, 对于前面的客户端来说是透明的, 可以简单的认为后边连接的是一个内存无限大的 Redis 服务。

方案提供了

  • 数据分片存储,缓存动态扩容

  • 缓存访问负载均衡,且高可用

  • 客户端与服务端解耦,服务端变化不影响客户端使用

  • 可视化运维管理后台,集群状态一目了然

方案是基于redis实现的,我们先简单了解一下redis。

Redis

简单介绍

Redis 是一个开源 , 高性能的key-value内存数据库,可以作为数据库、缓存或者消息服务使用。基于内存运行并支持持久化。支持复杂的数据结构并且提供原子性操作 。Redis的数据类型都是基本的数据结构,简单易懂,如List、Set、Map等等。

使用场景
  • 缓存-热数据

  • 计数器-递增、递减

  • 队列

  • 排重-集合之间交集、并集、差集

  • 排行榜(有序集合)

  • ……

在新平台中,redis可以用来做:

用途 用法
分布式Session 集群部署管理后台统一存储用户session信息
防止表单重复提交 表单提交使用redis校验请求是否合法有效
短信验证码 短信验证码时效性校验
唯一id 大批量生成唯一id,放入队列,减少服务调用,保证数据唯一
银联商户路由 缓存银联商户路由信息,减少复杂查询,缩短系统响应时间
热数据访问 缓存热点数据,降低数据库压力

由上可见,redis存在于系统的各个功能点中,一点一点使用redis来优化系统,提高系统稳定性和健壮性的效果是非常可观的。因此,redis相关知识是非常值得学习并应用到项目之中的。

单机缓存服务随着系统慢慢发展,无论存储量还是并发请求数都到了一个瓶颈期,缓存分片迫在眉睫。

集群方案

分片技术

客户端分片

客户端做key与后台实例的关系映射,每次操作都需要根据映射关系寻找对应的实例。 

这种方案将分片工作放在业务程序端,程序代码根据预先设置的路由规则,直接对多个Redis实例进行分布式访问。这样的好处是,不依赖于第三方分布式中间件,实现方法和代码都自己掌控,可随时调整,不用担心踩到坑。 

这实际上是一种静态分片技术。Redis实例的增减,都得手工调整分片程序。这种分片机制的性能比代理式更好(少了一个中间分发环节)。但缺点是升级麻烦,对研发人员的个人依赖性强。 

所以,这种方式下,可运维性较差。出现故障,定位和解决都得研发和运维配合着解决,故障时间变长。这种方案,难以进行标准化运维,所以这种方案是不可取的。基于此分片机制的开源产品,到现在几户没有了。

代理分片

这种方案,将分片工作交给专门的代理程序来做。代理程序接收到客户端的数据请求,根据路由规则,将这些请求分发给正确的Redis实例并返回给客户端。

这种机制下,一般会选用第三方代理程序(而不是自己研发),因为后端有多个Redis实例,所以这类程序又称为分布式中间件。这样的好处是,客户端不用关心后端Redis实例,运维起来也方便。由于中间加了一层代理路由转发 ,虽然会因此带来些性能损耗,但对于Redis这种内存读写型应用,相对而言性能还是可观的。

这是我们推荐的集群实现方案,像基于该机制的开源产品Twemproxy,豌豆荚的codis,便是其中代表,应用非常广泛。

Redis官方集群

无中心集群方案,客户端随机连接集群中的某个节点,数据不在该节点,则二次路由定位到正确的节点上操作。

简单了解一下Redis官方集群 Redis Cluster。

分布式缓存中间件Codis简单介绍

由图所示,该方案是一个3主3从6节点架构(最少3主)。整个集群分配16384 个slot(槽)。A节点分配[0-5000]、B节点分配[5001-10000]、C节点分配[10001-16383]。对于每个Redis的key都有唯一的slot来与之对应,具体算法为CRC16(key)%16384 = 0-16383中的某个值。

客户端创建对所有主节点的连接,客户端发起一个请求,并不知道key对应处于哪个节点上 , 所以他会选择从连接池随机获取一个连接 , 服务端判断key是否在当前节点 , 如果在,返回对应操作的结果。如果不在,获取key对应的节点信息,返回给客户端,客户端重新连接到对应的正确节点上执行对应操作。

最好的情况一次命中节点,最坏的情况是两次。会有性能损耗。

集群健康检测,选举容错

容错选举过程是集群中所有master参与,如果半数以上master节点与master节点通信超时(cluster-node-timeout),认为当前master节点挂掉。

什么时候整个集群不可用(cluster_state:fail),当集群不可用时,所有对集群的操作做都不可用,收到((error) CLUSTERDOWN The cluster is down)错误:

1.如果集群任意master挂掉,且当前master没有slave。集群进入fail状态,也可以理解成进群的slot映射[0-16383]不完成时进入fail状态。

2.如果进群超过半数以上master挂掉,无论是否有slave集群进入fail状态。

客户端连接
    JedisPoolConfig config = new JedisPoolConfig();
    // 最大连接数
    config.setMaxTotal(30);
    // 最大连接空闲数
    config.setMaxIdle(2);
    //集群结点
    Set<HostAndPort> jedisClusterNode = new HashSet<HostAndPort>();
    jedisClusterNode.add(new HostAndPort("192.168.101.3"7001));
    jedisClusterNode.add(new HostAndPort("192.168.101.3"7002));
    jedisClusterNode.add(new HostAndPort("192.168.101.3"7003));
    jedisClusterNode.add(new HostAndPort("192.168.101.3"7004));
    jedisClusterNode.add(new HostAndPort("192.168.101.3"7005));
    jedisClusterNode.add(new HostAndPort("192.168.101.3"7006));
    JedisCluster jc = new JedisCluster(jedisClusterNode, config);
    JedisCluster jcd = new JedisCluster(jedisClusterNode);
    jcd.set("name""zhangsan");
    String value = jcd.get("name");
    System.out.println(value);

客户端硬编码,由于后端节点的变化,导致客户端也需要做代码更新调整,服务重启。这样不仅影响线上业务,对程序人员也是一种摧残。理想情况是服务端做好封装,前端只负责发送请求,无论服务端有什么变化,前端并不需要关心。

由于存在二次路由,会有性能损耗,且无可视化运维界面。因此,我们将目光转向Codis。

Codis使用

分布式缓存中间件Codis简单介绍

组件
  • codis-server  对原生redis进行封装使用,等同于redis

  • codis-group  主从实例进行分组区分(分片),不同的分组负责不同的slot

  • codis-proxy  负责连接redis,可集群部署,用以提高并发处理能力

  • codis-dashboard  集群管理工具,支持 codis-proxy、codis-server 的添加、删除,以及据迁移等操作。在集群状态发生改变时,codis-dashboard 维护集群下所有 codis-proxy 的状态的一致性。

  • codis-fe  集群管理界面,集群状态一目了然


管理后台
  • qps 动态展示redis每秒操作数

分布式缓存中间件Codis简单介绍


  • 添加codis-proxy 做负载均衡和高可用

后台命令行启动codis-proxy

分布式缓存中间件Codis简单介绍


管理页面将启动好的codis-proxy添加到集群中

分布式缓存中间件Codis简单介绍

此时,后续客户端请求会轮询列表显示的无状态的codis-proxy,以达到负载高可用目的。

  • 组之间进行slot迁移

固定槽迁移,将783槽里的key迁移到组2中

分布式缓存中间件Codis简单介绍


指定数量的槽迁移到新的组中,将5个槽数据从组1迁移到组3中

分布式缓存中间件Codis简单介绍


通过slot的迁移,我们可以根据每个实例内存空间大小来动态调整内存使用情况,使slot分配更加合理。

  • 添加分组,并分配实例

启动codis-server实例,将192.168.0.163:6390实例启动

分布式缓存中间件Codis简单介绍


添加分组,并分配codis-server

分布式缓存中间件Codis简单介绍


指定数量的槽迁移到新的组中,将5个槽数据从组1迁移到组4中

分布式缓存中间件Codis简单介绍


这样,就实现了动态扩容。接着,我们为组4中的主节点添加一个从节点,达到高可用。

至此,集群扩容、高可用多负载的目的就通过Codis达到了。

客户端Jodis

客户端可以使用开源的jodis来轮询请求后端的无状态codis-proxy,由此达到负载均衡的目的。我们看一下关键代码。

   //监听zookeepr路径下节点
   watcher.getListenable().addListener(new PathChildrenCacheListener() {
        @Override
        public void childEvent(CuratorFramework client, PathChildrenCacheEvent event)
                throws Exception {
            StringBuilder sb = new StringBuilder("zookeeper event received: type=")
                    .append(event.getType());
            if (event.getData() != null) {
                ChildData data = event.getData();
                sb.append(", path=").append(data.getPath()).append(", stat=")
                        .append(data.getStat());
            }
            LOG.info(sb.toString());
            //当有节点(codis-proxy信息)添加、修改、删除
            if (RESET_TYPES.contains(event.getType())) {
                //进行重置连接池
                resetPools();
            }
        }
    });

由关键代码可知,后端服务的变化对前端都是可以动态感知,由此可以达到动态调整连接池,而前端是无需关心。

编辑:闫杰明

以上是关于分布式缓存中间件Codis简单介绍的主要内容,如果未能解决你的问题,请参考以下文章

Zookeeper集群搭建

6.《持续演进的Cloud Native 云原生架构下微服务最佳实践》读书笔记-第三章基于Codis实现Redis分布式缓存集群

codis

codis集群部署实战

缓存系列面试题Redis 集群模式的工作原理能说一下么?key 是如何寻址的?分布式寻址都有哪些算法?

基础架构组件选型及服务化