Redis分布式集群搭建

Redis集群架构图

 

 

上图蓝色为redis集群的节点。node

节点之间经过ping命令来测试链接是否正常,节点之间没有主区分,链接到任何一个节点进行操做时,均可能会转发到其余节点。redis

一、Redis的容错机制

节点之间会定时的互相发送ping命令,测试节点的健康状态,当节点接受到ping命令后,会返回一个pong字符串。算法

投票机制:若是一个节点A给节点B发送ping没有获得pong返回,会通知其余节点再次给B发送ping,若是集群中有超过一半的节点收不B节点的pong。那么就认为B节点挂了。通常会为每一个节点提供一个备份节点,若是挂掉会切换到备份节点。spring

二、Redis集群存储原理

Redis对于每一个存放的key会进行hash操做,生成一个[0-16384]hash值(先进行ruby

crc 算法再对16384取余)。服务器

集群的状况下,就是把[0-16384]的区间进行拆分,放到不一样的redis中。架构

 

三、Redis的持久化

Snapshotting:定时的将Redis内存中的数据保存到硬盘中app

AOF:将全部的command操做保存到aof中,AOP的同步频率很高,数据即便丢失,粒度也很小,但会在性能上形成影响。工具

 

2、Redis集群准备工做

Redis安装

源码下载性能

下载地址https://pan.baidu.com/s/1bCcLv4  密码i5k6

解压源码

   tar -zxvf redis-3.0.0.tar.gz  

进入解压后的目录进行编译

cd /usr/local/redis-3.0.0

make

安装到指定目录,/usr/local/redis

cd /usr/local/redis-3.0.0

make PREFIX=/usr/local/redis install

 

nredis.conf

redis.confredis的配置文件,redis.confredis源码目录。

注意修改port做为redis进程的端口,port默认6379

 

拷贝配置文件到安装目录下

进入源码目录,里面有一份配置文件 redis.conf,而后将其拷贝到安装路径下

cd /usr/local/redis

mkdir conf

cp /usr/local/redis-3.0.0/redis.conf  /usr/local/redis/bin

运行:bin/redis-server  将出现下图画面:

 

 

Redis默认是前台运行的,能够修改redis.confdaemonize yes ,将其变成后台运行。

 

集群环境搭建

redis集群管理工具redis-trib.rb依赖ruby环境,首先须要安装ruby环境

安装ruby

yum install ruby

yum install rubygems

安装rubyredis的接口程序

拷贝redis-3.0.0.gem/usr/local

执行:

gem install /usr/local/redis-3.0.0.gem

 

3、建立Redis集群

在一台服务器上,能够用不一样端口号来表示不一样redis服务器。

Redis集群最少须要三台服务器,而每台服务器有须要备用服务器,因此最少须要6台服务器。端口规划以下:

主服务器:192.168.100.66 :7001  :7002  :7003

从服务器:192.168.100.66 :7004  :7005  :7006

/usr/local 建立文件夹用来存放服务器程序

mkdir 7001 7002 7003 7004 7005 7006

若是想让redis支持集群须要修改redis.config配置文件的cluster-enabled yes

本例中咱们以端口来区别不一样的redis服务,因此还须要修改redis.configport为对应端口

修改完配置文件,将redis安装目录的bin复制到上面每一个目录中。

分别进入7001/bin/ 7002/bin .....

启动服务./redis-server ./redis.conf

 

查看redis进程:ps -aux|grep redis 以下图则说明启动成功

 

建立集群:

将以前解压的文件夹的redis-3.0.0/src/redis-trib.rb复制到redis-cluster目录

运行

./redis-trib.rb create --replicas 1 192.168.100.66:7001 192.168.100.66:7002 192.168.100.66:7003 192.168.100.66:7004 192.168.100.66:7005  192.168.100.66:7006

 

若是执行时报以下错误:

[ERR] Node XXXXXX is not empty. Either the node already knows other nodes (check with CLUSTER NODES) or contains some key in database 0

解决方法是删除生成的配置文件nodes.conf,若是不行则说明如今建立的结点包括了旧集群的结点信息,须要删除redis的持久化文件后再重启redis,好比:appendonly.aofdump.rdb

若是成功最终输入以下:

 

查询集群信息:

说明:

./redis-cli -c -h 192.168.101.3 -p 7001 ,其中-c表示以集群方式链接redis-h指定ip地址,-p指定端口号

cluster nodes 查询集群结点信息

cluster info 查询集群状态信息

 

hash槽从新分配

第一步:链接上集群

./redis-trib.rb reshard 192.168.101.3:7001(链接集群中任意一个可用结点都行)

 

第二步:输入要分配的槽数量

 

输入 500表示要分配500个槽

 

第三步:输入接收槽的结点id

 

 

这里准备给7007分配槽,经过cluster nodes查看7007结点id15b809eadae88955e36bcdbb8144f61bbbaf38fb

输入:15b809eadae88955e36bcdbb8144f61bbbaf38fb

 

第四步:输入源结点id

 

这里输入all

第五步:输入yes开始移动槽到目标结点id

 

添加从节点

 

集群建立成功后能够向集群中添加节点,下面是添加一个slave从节点。

添加7008从结点,将7008做为7007的从结点。

 

./redis-trib.rb add-node --slave --master-id 主节点id 添加节点的ip和端口 集群中已存在节点ip和端口

 

 

执行以下命令:

./redis-trib.rb add-node --slave --master-id cad9f7413ec6842c971dbcc2c48b4ca959eb5db4  192.168.101.3:7008 192.168.101.3:7001

cad9f7413ec6842c971dbcc2c48b4ca959eb5db4  7007结点的id,可经过cluster nodes查看。

 

注意:若是原来该结点在集群中的配置信息已经生成cluster-config-file指定的配置文件中(若是cluster-config-file没有指定则默认为nodes.conf),这时可能会报错:

[ERR] Node XXXXXX is not empty. Either the node already knows other nodes (check with CLUSTER NODES) or contains some key in database 0

解决方法是删除生成的配置文件nodes.conf,删除后再执行./redis-trib.rb add-node指令

查看集群中的结点,刚添加的70087007的从节点:

 

1.1. 删除结点:

 

./redis-trib.rb del-node 127.0.0.1:7005 4b45eb75c8b428fbd77ab979b85080146a9bc017

 

删除已经占有hash槽的结点会失败,报错以下:

[ERR] Node 127.0.0.1:7005 is not empty! Reshard data away and try again.

 

须要将该结点占用的hash槽分配出去(参考hash槽从新分配章节)。

测试:

Maven:

<dependencies>
    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
        <version>2.7.0</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/junit/junit -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>4.3.10.RELEASE</version>
        <scope>test</scope>
    </dependency>
</dependencies>

 

 

普通测试:

@Test
public void redisClusterTest1(){
    JedisPoolConfig config=new JedisPoolConfig();
    config.setMaxTotal(30);
    config.setMaxIdle(2);

    Set<HostAndPort> jedisNode=new HashSet<HostAndPort>();
    jedisNode.add(new HostAndPort("192.168.100.66",7001));
    jedisNode.add(new HostAndPort("192.168.100.66",7002));
    jedisNode.add(new HostAndPort("192.168.100.66",7003));
    jedisNode.add(new HostAndPort("192.168.100.66",7004));
    jedisNode.add(new HostAndPort("192.168.100.66",7005));
    jedisNode.add(new HostAndPort("192.168.100.66",7006));

    JedisCluster jc=new JedisCluster(jedisNode,config);
    jc.set("name","老王");
    String value=jc.get("name");
    System.out.println(value);
}

Spring测试:

配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- 链接池配置 -->
    <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <!-- 最大链接数 -->
        <property name="maxTotal" value="30" />
        <!-- 最大空闲链接数 -->
        <property name="maxIdle" value="10" />
        <!-- 每次释放链接的最大数目 -->
        <property name="numTestsPerEvictionRun" value="1024" />
        <!-- 释放链接的扫描间隔(毫秒) -->
        <property name="timeBetweenEvictionRunsMillis" value="30000" />
        <!-- 链接最小空闲时间 -->
        <property name="minEvictableIdleTimeMillis" value="1800000" />
        <!-- 链接空闲多久后释放, 当空闲时间>该值 且 空闲链接>最大空闲链接数 时直接释放 -->
        <property name="softMinEvictableIdleTimeMillis" value="10000" />
        <!-- 获取链接时的最大等待毫秒数,小于零:阻塞不肯定的时间,默认-1 -->
        <property name="maxWaitMillis" value="1500" />
        <!-- 在获取链接的时候检查有效性, 默认false -->
        <property name="testOnBorrow" value="true" />
        <!-- 在空闲时检查有效性, 默认false -->
        <property name="testWhileIdle" value="true" />
        <!-- 链接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true -->
        <property name="blockWhenExhausted" value="false" />
    </bean>
    <!-- redis集群 -->
    <bean id="jedisCluster" class="redis.clients.jedis.JedisCluster">
        <constructor-arg index="0">
            <set>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg index="0" value="192.168.100.66"></constructor-arg>
                    <constructor-arg index="1" value="7001"></constructor-arg>
                </bean>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg index="0" value="192.168.100.66"></constructor-arg>
                    <constructor-arg index="1" value="7002"></constructor-arg>
                </bean>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg index="0" value="192.168.100.66"></constructor-arg>
                    <constructor-arg index="1" value="7003"></constructor-arg>
                </bean>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg index="0" value="192.168.100.66"></constructor-arg>
                    <constructor-arg index="1" value="7004"></constructor-arg>
                </bean>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg index="0" value="192.168.100.66"></constructor-arg>
                    <constructor-arg index="1" value="7005"></constructor-arg>
                </bean>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg index="0" value="192.168.100.66"></constructor-arg>
                    <constructor-arg index="1" value="7006"></constructor-arg>
                </bean>
            </set>
        </constructor-arg>
        <constructor-arg index="1" ref="jedisPoolConfig"></constructor-arg>
    </bean>
</beans>

 

测试类:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:spring-config.xml"})
public class RedisClusterTest {
    @Autowired
    private JedisCluster jedisCluster;
    @Test
    public void redisClusterTest2(){
        jedisCluster.set("username","小明啦啦");
        String name=jedisCluster.get("username");
        System.out.println(name);    }}

相关文章
相关标签/搜索