redis集群模式

集群的特点

去中心化,客户端与redis节点直连,不需要中间proxy层(不象哨兵)。客户端不需要连接集群所有节点,连接集群中任何一个可用节点(主节点)即可。
所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽。
分片,把所有的物理节点映射到[0-16383]slot上,cluster 负责维护node<->slot<->value映射关系,无需关心数据落到那个节点。
节点的fail是通过集群中超过半数的节点(主节点)检测失效时才生效。
选举容错,自动将节点分成主从,形成主从复制(从只读),数据备份容灾,同时实现读写分离。
集群最少6个节点,3主3从。

选举过程是集群中所有master参与,如果半数以上master节点与master节点通信超过(cluster-node-timeout),认为当前master节点挂掉。
什么时候整个集群不可用(cluster_state:fail),当集群不可用时,所有对集群的操作做都不可用,收到((error) CLUSTERDOWN The cluster is down)错误:
a: 如果集群任意master挂掉,且当前master没有slave。集群进入fail状态,也可以理解成进群的slot映射[0-16383]不完成全进入fail状态。
b: 如果进群超过半数以上master挂掉,无论是否有slave集群进入fail状态。

 

安装Ruby

yum install ruby
ruby –v              #ruby 1.8.7
yum install rubygems    #也可以一句执行 yum install ruby rubygems -y
gem install redis        #安装redis的接口包
gem list             #查看是否安装了gem相关的接口包,检查redis是否已经存在
如果出现:Error installing redis: redis requires Ruby version >= 2.2.2.则执行下面操作:
1.安装curl
sudo yum install curl

2安装RVM
gpg2 --keyserver hkp://keys.gnupg.net --recv-keys D39DC0E3  
curl -L get.rvm.io | bash -s stable  
source /usr/local/rvm/scripts/rvm
3、删除之前安装的ruby
ruby --version  
rvm remove 2.0.0

安装新的ruby 2.3.3版本的
rvm list known  
rvm install 2.3.3
rvm use 2.3.3 --default 
gem install redis
gem list        #查看是否安装了gem相关的接口包,检查redis是否已经存在

由于cluster的特点,与redis传统主从不同的是,cluster中的节点需要配置在不同的文件夹,否则无法创建集群(尝试过一次,失败)。进入redis根目录,创建节点文件夹,同一个服务器上的不同节点文件夹可以用端口号来命名即可。

1、在redis的安装目录下创建七个目录

 

2、每个目录都放置一个redis的配置文件redis.conf,修改以下内容

P61     bind 127.0.0.1      //默认ip为127.0.0.1改为其他节点机器可访问的ip 
P80     protected-mode no        //yes修改为no,启动报错就删除它
P84     port 7000                 //端口7000 
P128    daemonize yes             //后台运行
P150    pidfile /usr/local/src/redis/redis3.2.6/redis_7000.pid     //pidfile文件对应7000 
P593    appendonly yes            //默认是rdb方式持久化要改成AOF模式
P163    logfile=7000/redis.log    //相对路径,启动时在redis的根目录
P721    cluster-enabled yes       //开启集群
P729    cluster-config-file nodes_7000.conf         //集群的配置
P735    cluster-node-timeout 15000             //请求超时默认15秒,可自行设置

3、在redis根目录创建快速启动脚本:start.sh,路径为:/home/redis/redis-3.0.7/start.sh

#!/bin/sh        //开头必须写它
redis-server 7000/redis.conf&
redis-server 7001/redis.conf&
redis-server 7002/redis.conf&
redis-server 7003/redis.conf&
redis-server 7004/redis.conf&
redis-server 7005/redis.conf&
redis-server 7006/redis.conf&

4、关闭防火墙或打开对应端口:

service iptables stop

5、启动脚本

[root@localhost redis-3.0.7]# bash start.sh

6、查看redis的进程是否正常启动了

ps - ef |grep redis

7、在redis的根目录启动集群

[root@localhost redis-3.0.7]# ./src/redis-trib.rb create --replicas 1 192.168.29.129:7000 192.168.29.129:7001 192.168.29.129:7002 192.168.29.129:7003 192.168.29.129:7004 192.168.29.129:7005 192.168.29.129:7006

#replicas 1 表示希望为集群中的每个主节点创建一个从节点(一主一从)
前几个自动做为主,后面几个做为从,主节点少于从节点,个数不对应时,多从挂接在一个主上。
注意:提示必须敲入yes,不能是y,y则按不接受处理。
注意:redis-trib-rb的路径,可以配置环境变量来解决
export PATH=/usr/local/ruby-2.1.2/bin;&PATH

8、如果上述启动失败需要先删除临时文件方可重新启动

 

9、登录集群

redis-cli -c -p 7000    #任何一个端口都可以
cluster info                #查看集群状态
cluster nodes    #查看集群节点状态
#杀掉一个7001,集群继续运行 ps
-ef |grep redis kill 5398 127.0.0.1:7000> cluster nodes #被杀的主节点,由从节点顶上成为master redis-server 7001/redis.conf #重新启动的节点会成为slave挂到顶替它的节点后面

10、sprig整合redis集群模式

添加依赖

 <!--redis begin-->
    <dependency>
      <groupId>redis.clients</groupId>
      <artifactId>jedis</artifactId>
      <version>2.9.0</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-redis</artifactId>
      <version>2.0.7.RELEASE</version>
    </dependency>
    <!--redis end-->
View Code

配置配置文件:applicationContext-rediscluster.xml (以spring工厂方式创建redisCluster对象)

<?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">

    <!-- jedis 配置-->
    <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig" >
        <!--最大空闲数-->
        <property name="maxIdle" value="${redis.maxIdle}" />
        <!--最大建立连接等待时间-->
        <property name="maxWaitMillis" value="${redis.maxWait}" />
        <!--是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个-->
        <property name="testOnBorrow" value="${redis.testOnBorrow}" />
        <property name="maxTotal" value="${redis.maxTotal}" />
        <property name="minIdle" value="${redis.minIdle}" />
    </bean>

    <bean id="jedisCluster"  class="cn.jiaming.chen.utils.RedisClusterFactor" >
        <property name="addressConfig">
            <value>classpath:redis.properties</value>
        </property>
        <!--  属性文件里  key的前缀 -->
        <property name="addressKeyPrefix" value="redis.cluster" /><property name="timeout" value="${redis.timeout}" />
        <property name="maxRedirections" value="6" />
        <property name="genericObjectPoolConfig" ref="poolConfig" />
    </bean>
</beans>

配置redis.properties配置文件

#最小空闲数
redis.minIdle=100
#最大空闲数
redis.maxIdle=300
redis.maxActive=300
#最大连接数
redis.maxTotal=1000
#客户端超时时间单位是毫秒
redis.timeout=1000
#最大建立连接等待时间
redis.maxWait=1000
#是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个
redis.testOnBorrow=true

redis.node1.ip=192.168.29.129
redis.node1.port=6379

redis.node2.ip=192.168.29.129
redis.node2.port=6380

redis.node3.ip=192.168.29.129
redis.node3.port=6381

#redis cluster
redis.cluster0.host.port=192.168.29.129:7000
redis.cluster1.host.port=192.168.29.129:7001
redis.cluster2.host.port=192.168.29.129:7002
redis.cluster3.host.port=192.168.29.129:7003
redis.cluster4.host.port=192.168.29.129:7004
redis.cluster5.host.port=192.168.29.129:7005
redis.cluster6.host.port=192.168.29.129:7006

添加自定义spring工厂类

package cn.jiaming.chen.utils;

import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
import java.util.regex.Pattern;

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.io.Resource;

import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;

public class RedisClusterFactor implements FactoryBean<JedisCluster>, InitializingBean {

    private Resource addressConfig;
    private String addressKeyPrefix;
    private JedisCluster jedisCluster;
    private Integer timeout;
    private Integer maxRedirections;
    private GenericObjectPoolConfig poolConfig;
    private Pattern p = Pattern.compile("^.+[:]\\d{1,5}\\s*$");

    public JedisCluster getObject() throws Exception {
        return jedisCluster;
    }

    public Class<? extends JedisCluster> getObjectType() {
        return (this.jedisCluster != null ? this.jedisCluster.getClass() : JedisCluster.class);
    }

    public boolean isSingleton() {
        return true;
    }

    private Set<HostAndPort> parseHostAndPort() {
        try {
            Properties prop = new Properties();
            prop.load(this.addressConfig.getInputStream());

            Set<HostAndPort> haps = new HashSet<HostAndPort>();
            for (Object key : prop.keySet()) {

                if (!((String) key).startsWith(addressKeyPrefix)) {
                    continue;
                }

                String val = (String) prop.get(key);

                boolean isIpPort = p.matcher(val).matches();

                if (!isIpPort) {
                    throw new IllegalArgumentException("ip 或 port 不合法");
                }
                String[] ipAndPort = val.split(":");

                HostAndPort hap = new HostAndPort(ipAndPort[0], Integer.parseInt(ipAndPort[1]));
                haps.add(hap);
            }

            return haps;
        } catch (IllegalArgumentException ex) {
            ex.printStackTrace();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }

    public void afterPropertiesSet() throws Exception {
        Set<HostAndPort> haps = this.parseHostAndPort();
        jedisCluster = new JedisCluster(haps, timeout, maxRedirections, poolConfig);
    }

    public void setAddressConfig(Resource addressConfig) {
        this.addressConfig = addressConfig;
    }

    public void setTimeout(int timeout) {
        this.timeout = timeout;
    }

    public void setMaxRedirections(int maxRedirections) {
        this.maxRedirections = maxRedirections;
    }

    public void setAddressKeyPrefix(String addressKeyPrefix) {
        this.addressKeyPrefix = addressKeyPrefix;
    }

    public void setGenericObjectPoolConfig(GenericObjectPoolConfig poolConfig) {
        this.poolConfig = poolConfig;
    }
}

在需要使用redis集群的地方注入spring工厂创建的类即可

@Autowired
private JedisCluster jedisCluster;

//测试
jedisCluster.exists(unitKey);
jedisCluster.set(unitKey,"123");

 

posted on 2018-06-06 13:38  javaGreenHand。。。  阅读(100)  评论(0)    收藏  举报