Redis Cluster集群搭建与应用

1.redis-cluster设计

    Redis集群搭建的方式有多种,例如使用zookeeper,但从redis 3.0之后版本支持redis-cluster集群,redis-cluster采用无中心结构,每个节点保存数据和整个集群状态,每个节点都和其他所有节点连接。其redis-cluster架构图如下:

其结构特点

  1. 所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽。
  2. 节点的fail是通过集群中超过半数的节点检测失效时才生效。
  3. 客户端与redis节点直连,不需要中间proxy层.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可。
  4. redis-cluster把所有的物理节点映射到[0-16383]slot上(不一定是平均分配),cluster 负责维护node<->slot<->value。
  5. Redis集群预分好16384个桶,当需要在 Redis 集群中放置一个 key-value 时,根据 CRC16(key) mod 16384的值,决定将一个key放到哪个桶中。

redis cluster节点分配

    例如分配三个主节点分别是:7000, 7001, 7002。三个从节点分别是7003,7004,7005。它们可以是一台机器上的六个端口,也可以是六台不同的服务器。采用哈希槽 (hash slot)的方式来分配16384个slot 的话,六个节点分别承担的slot 区间如同所示:

    

    获取数据: 如果存入一个值,按照redis cluster哈希槽的算法: CRC16('key')%16384 = 6782。 就会把这个key 的存储分配到7001 上了。同样,当连接(7000,7001,7002)任何一个节点想获取'key'这个key时,也会这样的算法,然后内部跳转到7001节点上获取数据 。

 2.redis-cluster主从模式

    redis cluster为了保证数据的高可用性,加入了主从模式,一个主节点对应一个或多个从节点,主节点提供数据存取,从节点则是从主节点拉取数据备份,当这个主节点挂掉后,就会有这个从节点选取一个来充当主节点,从而保证集群不会挂掉。主从模式具有如下特点:

  1. 集群有7000,7001,7002三个主节点, 如果这3个节点都没有加入从节点,如果7001挂掉了,就无法访问整个集群,7000和7002的slot也无法访问。
  2. 主从节点同时挂掉后,如节点7001和7004同时挂了,Redis集群将无法继续正确地提供服务。
  3. 为每个主节点设置从节点, 比如像这样, 集群包含主节点7000,7001,7002 以及从节点7003,7004,7005, 那么即使7001挂掉系统也可以继续正确工作。7004节点替代了7001节点,所以Redis集群将会选择7004节点作为新的主节点,集群将会继续正确地提供服务。 当7001重新开启后,它就会变成7004的从节点。

2.redis-cluster集群搭建

搭建环境

    本次测试采用虚拟机模式,在本机上做测试,虚拟机环境为Centos7.0。搭建集群需要如下相关依赖软件,下载地址为“

  1. rubygems软件包下载:https://rubygems.org/pages/download
  2. ruby软件包下载:http://www.ruby-lang.org/en/downloads/ 
  3. redis-3.2.2.gem依赖包下载:https://rubygems.global.ssl.fastly.net/gems/redis-3.2.2.gem
  4. openssl软件包下载: http://www.openssl.org/source/

    下载文件包截图如下:

     

   redis集群与大多数分布式中间件一样,redis的cluster也是依赖选举算法来保证集群的高可用,所以类似zookeeper一样,一般是奇数个节点(可以允许N/2以下的节点失效),再考虑到每个节点做Master-Slave互为备份,所以一个redis cluster集群最少也得6个节点。

步骤1:安装redis

    下载最新版redis并安装在linux系统中。具体操作可参考网址:Redis介绍及Jedis基础操作

步骤2:新建集群文件夹目录

    新建一个根目录data/cluster/。并在cluster目录下面建立6个子目录:mkdir 7000 7001 7002 7003 7004 7005。

步骤3:修改redis.conf配置文件

   修改redis的配置文件redis.conf,复制原有解压redis文件中的redis.conf文件到7000目录中,操作指令如:cp /usr/software/redis-4.0.6/redis.conf /data/cluster/7000。修改redis.conf文件中的配置字段,修改字段如下:

daemonize yes #后台启动
port 7000 #修改端口号,从7000到7005
cluster-enabled yes #开启cluster,去掉注释
cluster-config-file nodes-7000.conf
cluster-node-timeout 15000
appendonly yes
pidfile /var/run/redis_7000.pid

    相同操作处理其他五个文件夹,配置文件redis.conf中,将7000替换为对应的值。比如:7001文件下替换为7001。处理完成后,一次运行定义的每个文件夹下面的redis,查看是否启动成功。

    

 步骤4:安装Ruby环境

    安装Ruby环境。网上很多博客都是采用yum模式安装的,但考虑到FQ等限制条件,本文采用离线模式安装。

Ruby简介

    Ruby是一种纯粹的面向对象编程语言。它由日本的松本行弘(まつもとゆきひろ/Yukihiro Matsumoto)创建于1993年。可以在 www.ruby-lang.org 的 Ruby 邮件列表上找到松本行弘(まつもとゆきひろ/Yukihiro Matsumoto)的名字。在 Ruby 社区,松本也被称为马茨(Matz)。Ruby 是"程序员的最佳朋友"。Ruby 的特性与 Smalltalk、Perl 和 Python 类似。Perl、Python 和 Smalltalk 是脚本语言。Smalltalk 是一个真正的面向对象语言。Ruby,与 Smalltalk 一样,是一个完美的面向对象语言。使用 Ruby 的语法比使用 Smalltalk 的语法要容易得多。

Ruby离线安装

  1. Ruby下载地址:http://www.ruby-lang.org/en/downloads/  ,最新的版本为2.5.0。
  2. Ruby安装参考网址:Linux 安装Ruby详解(在线和离线安装)
  3. 在安装之前,请确保使用账号具有Root权限,将下载的Ruby安装包上传到服务器当中,通过xtfp5工具进行文件上传:解压上传文件, tar -zxvf ruby-2.5.0.tar.gz,进入到ruby-2.5.0目录执行指令make && make install安装,安装成功可通过指令:ruby -v查看版本号:

       

步骤5:安装RubyGems环境

    安装RubyGems环境。网上很多博客都是采用yum模式安装的,但考虑到FQ等限制条件,本文采用离线模式安装。

RubyGems简介

    RubyGems 是 Ruby 的一个包管理器,它提供一个分发 Ruby 程序和库的标准格式,还提供一个管理程序包安装的工具。RubyGems 旨在方便地管理 gem 安装的工具,以及用于分发 gem 的服务器。这类似于 Ubuntu 下的apt-get, Centos 的 yum,Python 的 pip。RubyGems大约创建于2003年11月,从Ruby 1.9版起成为Ruby标准库的一部分。

离线安装RubyGems

如果你的 Ruby 低于 1.9 版本,也可以通过手动安装:

  1. RubyGems下载地址:https://rubygems.org/pages/download
  2. RubyGems安装参考网址:Linux 离线安装Rubygems详解
  3. 解压下载文件并进入目录,解压指令tar -zxvf rubygems-2.7.4.tgz,执行命令:ruby setup.rb

       

步骤6:安装openssl

    使用gem install 安装 ruby redis。直接操作会报如下错误,查看原因是因为缺少openssl。

    

离线安装openssl

  1. openssl下载地址:http://www.openssl.org/source/
  2. openssl安装参考网址:配置群集时# gem install redis 报错:Unable to require openssl, install OpenSSL and rebuild ruby
  3. 解压下载文件并进入目录,解压指令如: tar -xzvf openssl-1.0.2n.tar.gz  ,执行以下命令:
tar -xzvf openssl-1.0.2n.tar.gz  
cd openssl-1.0.2n 
./config -fPIC --prefix=/usr/local/openssl enable-shared  
./config -t  make && make install
  1. 执行以上命令安装openssl,安装后查看版本号如下:

     

  1. 解决ssl.h文件找不到的问题,配置ruby文件,# ruby extconf.rb --with-openssl-include=/usr/local/openssl/include/ --with-openssl-lib=/usr/local/openssl/lib 
  2. 设置软链接:ln -s /usr/local/src/ruby-2.2.3/include /  
  3. 再次编译安装,成功后如下图所示:

    

步骤7:  安装redis-trib.rb运行依赖的ruby的包redis-3.2.2.gem

  1. redis-3.2.2.gem下载地址:https://rubygems.global.ssl.fastly.net/gems/redis-3.2.2.gem
  2. 下载完成后上传到服务器上面,执行安装命令如:gem install /usr/software/redis-3.2.2.gem   

      

步骤8:  使用redis-trib.rb创建集群

    使用create命令 --replicas 1 参数表示为每个主节点创建一个从节点,其他参数是实例的地址集合。可利用命令:./redis-trib.rb help查看使用介绍。运行集群创建shell脚本,cluster就创建成功了。最终的结果是后面的192.168.210.128:7000~192.168.210.128:7005中,会有3个会指定成master,而其它3个会指定成slave。

    注:利用redis-trib创建cluster的操作,只需要一次即可,假设系统关机,把所有6个节点全关闭后,下次重启后,即自动进入cluster模式,不用再次redis-trib.rb create。

    

     查看redis进程启动状态,并开放防火墙中的对应端口。

    

    查看节点分配指令为:./redis-trib.rb check 192.168.210.128:7002  (任意一个集群的ip地址)

    

3.redis-cluster集群节点选举,扩容与删除

集群选举

    现在模拟将7002节点挂掉,按照redis-cluster原理会选举将 7002的从节点7005选举为主节点。直接关闭7002的进程,在重新check可发现7005已经被自动选举为主节点。当启动7002后,7002将作为7005的从节点。

新增主节点

   新增一个节点D,redis cluster的这种做法是从各个节点的前面各拿取一部分slot到新增点上,也可设置从指定部分节点获取。举例如:在A,B,C节点上新增节点D。

  • 节点A覆盖1365-5460
  • 节点B覆盖6827-10922
  • 节点C覆盖12288-16383
  • 节点D覆盖0-1364,5461-6826,10923-12287

    新增一个节点7006作为主节点,操作步骤如下:

  1. 修改配置文件,新建一个对应的文件7006,并把复制配置文件redis.conf放入到文件7006下面,并修改配置文件,把端口修改为7006,其他配置信息也参考前面的案例对应修改。节点配置信息成功后,启动7006下面的redis。
  2. 将7006加入到现有的集群中,输入指令:./redis-trib.rb add-node 192.168.210.128:7006 192.168.210.128:7002。指令说明:dd-node是加入集群节点,192.168.210.128:7006为要加入的节点,192.168.210.128:7002 表示加入的集群的一个节点,用来辨识是哪个集群,理论上那个集群的节点都可以。
  3. 目前cluster已经定义7006为主节点,但是Cluster并未给7006分配哈希卡槽(0 slots)。
  4. redis-cluster在新增节点时并未分配卡槽,需要操作者手动对集群进行重新分片迁移数据,需要重新分片命令:reshard。操作如:redis-trib.rb reshard 192.168.210.128:7002。指令说明:这个命令是用来迁移slot节点的,后面的192.168.210.128:7002是表示是哪个集群,端口填[7000-7006]都可以,执行后:它提示需要迁移多少slot到7006上平分16384个哈希槽给4个节点:16384/4 = 4096,可移动4096个槽点到7006上。填写7006的id:如ee3efb90e5ac0725f15238a64fc60a18a71205d7。

    

  1. redis-trib 会向你询问重新分片的源节点(source node),即,要从特定的哪个节点中取出 4096 个哈希槽,还是从全部节点提取4096个哈希槽, 并将这些槽移动到7006节点上。如果不打算从特定的节点上取出指定数量的哈希槽,那么可以向redis-trib输入 all,这样的话, 集群中的所有主节点都会成为源节点,redis-trib从各个源节点中各取出一部分哈希槽,凑够4096个,然后移动到7006节点上。操作命令为:Source node #1:all  。

    

  1. 确认之后,redis-trib就开始执行分片操作,将哈希槽一个一个从源主节点移动到7006目标主节点。重新分片结束后可以check以下节点的分配情况。指令为:./redis-trib.rb check 192.168.210.128:7002。可查看扩容主节点是否成功。

新增从节点

  1.  新增一个节点7007作为从节点修改配置文件,新建一个对应的文件7007,并把复制配置文件redis.conf放入到文件7007下面,并修改配置文件,把端口修改为7007,其他配置信息也参考前面的案例对应修改。节点配置信息成功后,启动7007下面的redis并加入到现有集群中。
  2. redis-trib增加从节点的命令为:./redis-trib.rb add-node --slave --master-id $[nodeid] 192.168.210.128:7007 192.168.210.128:7000 。操作指令含义:nodeid为要加到master主节点的node id,192.168.210.128:7007为新增的从节点,192.168.210.128:7000为集群的一个节点(集群的任意节点都行),用来辨识是哪个集群;如果没有给定那个主节点--master-id的话,redis-trib将会将新增的从节点随机到从节点较少的主节点上。
  3. 从节点不存在分片操作,与主节点对应的片一致。

移除主节点

  1. 移除节点使用redis-trib的del-node命令,redis-trib del-node 192.168.210.128:7002  ${node-id}  。操作指令含义: 192.168.210.128:7000为指定集群,node-id为要删除的主节点。 和添加节点不同,移除节点node-id是必需的。

  2. 测试删除7001主节点,redis cluster提示7001已经有数据了,不能够被删除,需要将他的数据转移出去,也就是和新增主节点一样需重新分片。

  3. 分区指令: ./redis-trib.rb reshard 192.168.210.128:7002  
  4. 输入提示的需要移动的分片大小,分配给7001的slots为4096,输入需要移动的片为4096。
  5. 输入这些移除的slots如何分配给其他node,可指定一个具体node的id或者选择所有。
  6. 最后确认后,开始移除节点。

    

移除从节点

  1. 移除节点使用redis-trib的del-node命令,redis-trib del-node 192.168.210.128:7002  ${node-id}  。操作指令含义: 192.168.210.128:7000为指定集群,node-id为要删除的节点。 和添加节点不同,移除节点node-id是必需的。
  2. 从节点不存在分片问题,直接执行命令,确认移除即可。

4.redis-cluster集群与分布式连接池区别

    ShardedJedisPool是redis没有集群功能之前客户端实现的一个数据分布式方案,redis3.0提供集群之后,客户端则采用JedisCluster实现连接redis集群环境。 ShardedJedisPool使用的是JedisShardInfo的instance的顺序或者name来做的一致性哈希,JedisCluster使用的是CRC16算法来做的哈希槽。 

    集群环境各个服务之间的数据是隔离的。无论是ShardedJedisPool的一致性哈希算法还是JedisCluster的CRC16哈希槽算法,都是把所有的服务叠加然后进行均匀的分割,分割出来的每一个段或槽都是不重复的,所以导致存储的数据彼此之间也是处于隔离状态的。  

    jediscluster通过在客户端调用捕捉异常,可实现集群环境下的高可用。Jedis还提供了对jedis sentinel pool的封装,所以ShardedJedisPool发生主从切换的时候,web server都不需要重新配置和deploy。高可用性的极佳体现啊。

5.java客户端调用redis-cluster

    更新pom文件中redis-clients的版本,低版本会报错。<redis-clients.version>2.9.0</redis-clients.version>

    java客户端调用redis-cluster可通过在java代码中直接填写地址或通过spring配置文件填写,具体可参考上传的代码。在java中调用集群案例代码如下:

/**
 * Description:  redis cluster 测试
 * Copyright:  2018 CSNT. All rights reserved.
 * Company:CSNT
 *
 * @author wangling
 * @version 1.0
 */
public class RedisClusterTestDemo {
            @Test
           public void testRedisCluster() throws Exception {
                JedisPoolConfig poolConfig = new JedisPoolConfig();
                Set<HostAndPort> nodes = new HashSet<HostAndPort>();
                HostAndPort hostAndPort = new HostAndPort("192.168.210.128", 7000);
                HostAndPort hostAndPort1 = new HostAndPort("192.168.210.128", 7001);
                HostAndPort hostAndPort2 = new HostAndPort("192.168.210.128", 7002);
                HostAndPort hostAndPort3 = new HostAndPort("192.168.210.128", 7003);
                HostAndPort hostAndPort4 = new HostAndPort("192.168.210.128", 7004);
                HostAndPort hostAndPort5 = new HostAndPort("192.168.210.128", 7005);
                nodes.add(hostAndPort);
                nodes.add(hostAndPort1);
                nodes.add(hostAndPort2);
                nodes.add(hostAndPort3);
                nodes.add(hostAndPort4);
                nodes.add(hostAndPort5);
                JedisCluster jedisCluster = new JedisCluster(nodes,5000,1000);
                jedisCluster.set("jedisKey","wangling test jedisKey");
                    //redis内部会创建连接池,从连接池中获取连接使用,然后再把连接返回给连接池
                String string = jedisCluster.get("jedisKey");
                System.out.println(string);
                 }
         }

//基于配置文件调用,完整配置信息参考上传的源代码
/**
 * Description:  redis cluster 测试
 * Copyright:  2018 CSNT. All rights reserved.
 * Company:CSNT
 *
 * @author wangling
 * @version 1.0
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:redis-cluster-context.xml")
public class JedeisClusterTest {
     @Autowired
     private JedisCluster jedisCluster;

     @Test
     public void testJedisCluster(){
          jedisCluster.set("jedisCluster", "wangling test jedisCluster");
          String val = jedisCluster.get("jedisCluster");
          System.out.println(val);
     }
}

    软件运行截图如下:

6.参考网址

7.源码下载

在Git上面下载:https://github.com/wuya11/redisClusterDemo

posted @ 2018-01-25 11:49  无涯Ⅱ  阅读(3306)  评论(0编辑  收藏  举报