您当前位置: 南顺网络>> 官方资讯>> 建站知识

redis分布式搭建

Redis集群架构图

 

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


节点之间通过ping命令来测试连接是否正常,节点之间没有主区分,连接到任何一个节点进行操作时,都可能会转发到其他节点。


1、Redis的容错机制

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


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


2、Redis集群存储原理

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


crc 算法再对16384取余)。


集群的情况下,就是把[0-16384]的区间进行拆分,放到不同的redis中。


 


3、Redis的持久化

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


AOF:将所有的command操作保存到aof中,AOP的同步频率很高,数据即使丢失,粒度也很小,但会在性能上造成影响。


 


二、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.conf是redis的配置文件,redis.conf在redis源码目录。


注意修改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.conf的daemonize yes ,将其变成后台运行。


 


集群环境搭建

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


安装ruby


yum install ruby


yum install rubygems


安装ruby和redis的接口程序


拷贝redis-3.0.0.gem至/usr/local下


执行:


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


 


三、创建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.config的port为对应端口


修改完配置文件,将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.aof、dump.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结点id为15b809eadae88955e36bcdbb8144f61bbbaf38fb


输入: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指令


查看集群中的结点,刚添加的7008为7007的从节点:

 


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);

    }

}


编辑:--ns868