Linux Redis集群配置

注意:本教程redis放在同一服务器(主要是演示),实际要放不同服务器

搭建集群需要使用到官方提供的ruby脚本。

需要安装ruby的环境

[root@localhost ~]# yum install ruby
[root@localhost ~]# yum install rubygems

redis集群管理工具redis-trib.rb

[root@localhost /]# cd redis-3.0.0
[root@localhost redis-3.0.0]# cd src/
[root@localhost src]# ll *.rb

进入src文件夹安装pem包

1.上传redis3.0.0.pem文件
2.安装pem文件

[root@localhost src]# gem install redis-3.0.0.gem

第一步:创建6个redis实例,端口号从7001~7006

[root@localhost src]# cd /usr/local

这里写图片描述
创建文件夹,放置redis实例

[root@localhost local]# mkdir redis-cluster
[root@localhost local]# cd redis
[root@localhost redis]# cp -r bin ../redis-cluster/redis01

这里写图片描述
删除dump.rdb 文件

[root@localhost redis01]# rm -f dump.rdb

第二步:修改redis的配置文件

1、修改端口号

[root@localhost redis01]# vim redis.conf

端口号从7001~7006模拟6个服务器
这里写图片描述
将下面注释去掉
这里写图片描述
创建6个实例,并将端口号改正
这里写图片描述
把创建集群的ruby脚本复制到redis-cluster目录下。

[root@localhost /]# cd redis-3.0.0/src
[root@localhost src]# cp *.rb /usr/local/redis-cluster/

启动6个redis实例,由于很麻烦,创建脚本启动

[root@localhost /]# cd /usr/local/redis-cluster/
[root@localhost redis-cluster]# vim startall.sh

脚本文件

cd redis01
redis-server redis.conf
cd ..
cd redis02
redis-server redis.conf
cd ..
cd redis03
redis-server redis.conf
cd ..
cd redis04
redis-server redis.conf
cd ..
cd redis05
redis-server redis.conf
cd ..
cd redis06
redis-server redis.conf
cd ..

[root@localhost redis-cluster]# chmod +x startall.sh
[root@localhost redis-cluster]# ./startall.sh

查看是否启动

[root@localhost redis-cluster]# ps aux | grep redis

这里写图片描述

创建集群

注意:此处的ip是因为我的6个redis实例在同一服务器,实际要放在不同服务器,故ip是不同的

[root@localhost redis-cluster]# ./redis-trib.rb create –replicas 1 192.168.184.130:7001 192.168.184.130:7002 192.168.184.130:7003 192.168.184.130:7004 192.168.184.130:7005 192.168.184.130:7006

这里写图片描述

成功之后的代码

>>> Creating cluster
Connecting to node 192.168.184.130:7001: OK
Connecting to node 192.168.184.130:7002: OK
Connecting to node 192.168.184.130:7003: OK
Connecting to node 192.168.184.130:7004: OK
Connecting to node 192.168.184.130:7005: OK
Connecting to node 192.168.184.130:7006: OK
>>> Performing hash slots allocation on 6 nodes...
Using 3 masters:
192.168.184.130:7001
192.168.184.130:7002
192.168.184.130:7003
Adding replica 192.168.184.130:7004 to 192.168.184.130:7001
Adding replica 192.168.184.130:7005 to 192.168.184.130:7002
Adding replica 192.168.184.130:7006 to 192.168.184.130:7003
M: 05cf32045c801e6f877c427708fd708d980ba143 192.168.184.130:7001
   slots:0-5460 (5461 slots) master
M: e41530a607efc8a1de06aae7c97dc3aac3109974 192.168.184.130:7002
   slots:5461-10922 (5462 slots) master
M: 13e9a59cda0697a78db718e07bf8274c0e3ad654 192.168.184.130:7003
   slots:10923-16383 (5461 slots) master
S: fdad4d10857d5bc014a57115cfdc05185cb632aa 192.168.184.130:7004
   replicates 05cf32045c801e6f877c427708fd708d980ba143
S: e34a844f113194f8ef153f4b7ce69f3aab1f0f47 192.168.184.130:7005
   replicates e41530a607efc8a1de06aae7c97dc3aac3109974
S: 2bcd831c97b81756f248fdcd4c33c3a3aa1b3d85 192.168.184.130:7006
   replicates 13e9a59cda0697a78db718e07bf8274c0e3ad654
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join..
>>> Performing Cluster Check (using node 192.168.184.130:7001)
M: 05cf32045c801e6f877c427708fd708d980ba143 192.168.184.130:7001
   slots:0-5460 (5461 slots) master
M: e41530a607efc8a1de06aae7c97dc3aac3109974 192.168.184.130:7002
   slots:5461-10922 (5462 slots) master
M: 13e9a59cda0697a78db718e07bf8274c0e3ad654 192.168.184.130:7003
   slots:10923-16383 (5461 slots) master
M: fdad4d10857d5bc014a57115cfdc05185cb632aa 192.168.184.130:7004
   slots: (0 slots) master
   replicates 05cf32045c801e6f877c427708fd708d980ba143
M: e34a844f113194f8ef153f4b7ce69f3aab1f0f47 192.168.184.130:7005
   slots: (0 slots) master
   replicates e41530a607efc8a1de06aae7c97dc3aac3109974
M: 2bcd831c97b81756f248fdcd4c33c3a3aa1b3d85 192.168.184.130:7006
   slots: (0 slots) master
   replicates 13e9a59cda0697a78db718e07bf8274c0e3ad654
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

测试集群(-c 一定要加,表示连接集群)

[root@localhost redis-cluster]# redis01/redis-cli -h 192.168.184.130 -p 7002 -c

会将不同的值存进不同的redis

[root@localhost redis-cluster]# redis01/redis-cli -h 192.168.184.130 -p 7002 -c
192.168.184.130:7002> set a 100
-> Redirected to slot [15495] located at 192.168.184.130:7003
OK
192.168.184.130:7003>  set a 100
OK
192.168.184.130:7003> get a
"100"
192.168.184.130:7003> set b 1000
-> Redirected to slot [3300] located at 192.168.184.130:7001
OK
192.168.184.130:7001> get a
-> Redirected to slot [15495] located at 192.168.184.130:7003
"100"
192.168.184.130:7003> get b
-> Redirected to slot [3300] located at 192.168.184.130:7001
"1000"
192.168.184.130:7001> 

查询集群信息
集群创建成功登陆任意redis结点查询集群中的节点情况。
客户端以集群方式登陆:
说明:
./redis-cli -c -h 192.168.101.3 -p 7001 ,其中-c表示以集群方式连接redis,-h指定ip地址,-p指定端口号
cluster nodes 查询集群结点信息
cluster info 查询集群状态信息
添加主节点
集群创建成功后可以向集群中添加节点,下面是添加一个master主节点
添加7007结点,参考集群结点规划章节添加一个“7007”目录作为新节点。
执行下边命令:
./redis-trib.rb add-node 192.168.101.3:7007 192.168.101.3:7001
查看集群结点发现7007已添加到集群中:
hash槽重新分配
添加完主节点需要对主节点进行hash槽分配这样该主节才可以存储数据。
redis集群有16384个槽,集群中的每个结点分配自已槽,通过查看集群结点可以看到槽占用情况。
给刚添加的7007结点分配槽:
第一步:连接上集群
./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的从节点:
删除结点:
./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.**

关闭redis

[root@localhost redis-cluster]# redis01/redis-cli -p 7001 shutdown
[root@localhost redis-cluster]# redis01/redis-cli -p 7002 shutdown
[root@localhost redis-cluster]# redis01/redis-cli -p 7003 shutdown
[root@localhost redis-cluster]# redis01/redis-cli -p 7004 shutdown
[root@localhost redis-cluster]# redis01/redis-cli -p 7005 shutdown
[root@localhost redis-cluster]# redis01/redis-cli -p 7006 shutdown

也可以将上面代码同开启redis集群方式,新建脚本,将上面命令加进去

java调用redis代码

单机版测试

public class JedisTest {

    @Test
    public void testJedisSingle() {
        //创建一个jedis的对象。
        Jedis jedis = new Jedis("192.168.25.153", 6379);
        //调用jedis对象的方法,方法名称和redis的命令一致。
        jedis.set("key1", "jedis test");
        String string = jedis.get("key1");
        System.out.println(string);
        //关闭jedis。
        jedis.close();
    }

    /**
     * 使用连接池
     */
    @Test
    public void testJedisPool() {
        //创建jedis连接池
        JedisPool pool = new JedisPool("192.168.25.153", 6379);
        //从连接池中获得Jedis对象
        Jedis jedis = pool.getResource();
        String string = jedis.get("key1");
        System.out.println(string);
        //关闭jedis对象
        jedis.close();
        pool.close();
    }

集群版测试

@Test
public void testJedisCluster() {
        HashSet<HostAndPort> nodes = new HashSet<>();
        nodes.add(new HostAndPort("192.168.25.153", 7001));
        nodes.add(new HostAndPort("192.168.25.153", 7002));
        nodes.add(new HostAndPort("192.168.25.153", 7003));
        nodes.add(new HostAndPort("192.168.25.153", 7004));
        nodes.add(new HostAndPort("192.168.25.153", 7005));
        nodes.add(new HostAndPort("192.168.25.153", 7006));

        JedisCluster cluster = new JedisCluster(nodes);

        cluster.set("key1", "1000");
        String string = cluster.get("key1");
        System.out.println(string);

        cluster.close();
    }

jedis整合spring

单机版整合

配置文件 applicationContext-jedis.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    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-4.0.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
    http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.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> 

    <!-- jedis客户端单机版 -->
    <bean id="redisClient" class="redis.clients.jedis.JedisPool">
        <constructor-arg name="host" value="192.168.184.130"></constructor-arg>
        <constructor-arg name="port" value="6379"></constructor-arg>
        <constructor-arg name="poolConfig" ref="jedisPoolConfig"></constructor-arg>
    </bean>



</beans>

Spring Redis整合单机版测试

    /**
     * 单机版测试
     * <p>Title: testSpringJedisSingle</p>
     * <p>Description: </p>
     */
    @Test
    public void testSpringJedisSingle() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring/applicationContext-*.xml");
        JedisPool pool = (JedisPool) applicationContext.getBean("redisClient");
        Jedis jedis = pool.getResource();
        String string = jedis.get("key1");
        System.out.println(string);
        jedis.close();
        pool.close();
    }

集群版整合

配置文件 applicationContext-jediss.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    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-4.0.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
    http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">


        <bean id="redisClient" class="redis.clients.jedis.JedisCluster">

        <constructor-arg name="nodes">
            <set>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg name="host" value="192.168.184.130"></constructor-arg>
                    <constructor-arg name="port" value="7001"></constructor-arg>
                </bean>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg name="host" value="192.168.184.130"></constructor-arg>
                    <constructor-arg name="port" value="7002"></constructor-arg>
                </bean>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg name="host" value="192.168.184.130"></constructor-arg>
                    <constructor-arg name="port" value="7003"></constructor-arg>
                </bean>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg name="host" value="192.168.184.130"></constructor-arg>
                    <constructor-arg name="port" value="7004"></constructor-arg>
                </bean>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg name="host" value="192.168.184.130"></constructor-arg>
                    <constructor-arg name="port" value="7005"></constructor-arg>
                </bean>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg name="host" value="192.168.184.130"></constructor-arg>
                    <constructor-arg name="port" value="7006"></constructor-arg>
                </bean>
            </set>
        </constructor-arg>
        <constructor-arg name="poolConfig" ref="jedisPoolConfig"></constructor-arg>
    </bean>




</beans>

Spring Redis整合集群版测试

@Test
public void testSpringJedisCluster() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring/applicationContext-*.xml");
        JedisCluster jedisCluster =  (JedisCluster) applicationContext.getBean("redisClient");
        String string = jedisCluster.get("key1");
        System.out.println(string);
        jedisCluster.close();
    }

java项目中实现

注意:以下是为了方便将单机和集群共用

接口
public interface JedisClient {

     String get(String key);
     String set(String key, String value);
     String hget(String hkey, String key);
     long hset(String hkey, String key ,String value);
     long incr(String key);
     //设置过期时间
     long expire(String key, int second);
     //查询
     long ttl(String key);

}

单机实现

/**
 * 单机版reids
 * @author Administrator
 *
 */
public class JedisClientSingle implements JedisClient{


    @Autowired
    private JedisPool jedisPool;

    @Override
    public String get(String key) {

        Jedis jedis = jedisPool.getResource();
        String string = jedis.get(key);
        jedis.close();

        return string;
    }

    @Override
    public String set(String key, String value) {

        Jedis jedis = jedisPool.getResource();
        String string = jedis.set(key, value);
        jedis.close();
        return string;
    }

    @Override
    public String hget(String hkey, String key) {

        Jedis jedis = jedisPool.getResource();
        String string = jedis.hget(hkey, key);
        jedis.close();
        return string;
    }

    @Override
    public long hset(String hkey, String key, String value) {

        Jedis jedis = jedisPool.getResource();
        long result = jedis.hset(key, key, value);
        jedis.close();
        return result;
    }

    @Override
    public long incr(String key) {

        Jedis jedis = jedisPool.getResource();
        Long result = jedis.incr(key);
        jedis.close();
        return result;

    }

    @Override
    public long expire(String key, int second) {

        Jedis jedis = jedisPool.getResource();
        Long result = jedis.expire(key, second);
        jedis.close();
        return result;

    }

    @Override
    public long ttl(String key) {

        Jedis jedis = jedisPool.getResource();
        Long result = jedis.ttl(key);
        jedis.close();
        return result;

    }

}

注入applicationContext-jedis.xml文件

<bean id="jedisClient" class="com.taotao.rest.dao.JedisClientSingle"></bean>

集群实现

/**
 * 集群版
 * @author Administrator
 *
 */
public class JedisClientCluster implements JedisClient {

    @Autowired
    private JedisCluster jedisCluster;

    @Override
    public String get(String key) {
        return jedisCluster.get(key);
    }

    @Override
    public String set(String key, String value) {
        return jedisCluster.set(key, value);
    }

    @Override
    public String hget(String hkey, String key) {
        return jedisCluster.hget(hkey, key);
    }

    @Override
    public long hset(String hkey, String key, String value) {
        return jedisCluster.hset(hkey, key, value);
    }

    @Override
    public long incr(String key) {
        return jedisCluster.incr(key);
    }

    @Override
    public long expire(String key, int second) {
        return jedisCluster.expire(key, second);
    }

    @Override
    public long ttl(String key) {
        return jedisCluster.ttl(key);
    }

}

注入applicationContext-jediss.xml文件

<bean id="jedisClientCluster" class="com.taotao.rest.dao.JedisClientCluster"></bean>

把缓存添加到业务逻辑

    @Value("${INDEX_CONTENT_REDIS_KEY}")
    private String INDEX_CONTENT_REDIS_KEY; //这个常量key设置在resource.properties文件中

    @Override
    public List<TbContent> getContentList(long contentid) {


            //从缓存中取内容
            try {
              String result = jedisClient.hget(INDEX_CONTENT_REDIS_KEY, contentid + "");
              //StringUtils 是org.apache.commons.lang3.StringUtils包
              if (!StringUtils.isBlank(result)) {
                            //把字符串转换成list
                            List<TbContent> resultList = JsonUtils.jsonToList(result, TbContent.class);
                            return resultList;
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }


            //根据内容分类id查询内容列表
            TbContentExample example = new TbContentExample();
            Criteria criteria = example.createCriteria();
            criteria.andCategoryIdEqualTo(contentid);
            //执行查询
            List<TbContent> list = tbContentMapper.selectByExample(example);


            //向缓存中添加内容
            try {
                //把list转换成字符串
                String cacheString = JsonUtils.objectToJson(list);
                jedisClient.hset(INDEX_CONTENT_REDIS_KEY, contentid + "", cacheString);
            } catch (Exception e) {
                e.printStackTrace();
            }
        return list;
    }

假如数据发生改变,那么就要进行缓存同步

JedisClient添加接口,用之前的共用接口,向之前的接口中添加del方法

//删除
long del(String key);
long hdel(String hkey, String key);

实现类中(单机)

    @Override
    public long del(String key) {

        Jedis jedis = jedisPool.getResource();
        Long result = jedis.del(key);
        jedis.close();
        return result;
    }

    @Override
    public long hdel(String hkey, String key) {

        Jedis jedis = jedisPool.getResource();
        Long result = jedis.hdel(hkey, key);
        jedis.close();
        return result;
    }

实现类中(集群)

    @Override
    public long del(String key) {
        return jedisCluster.del(key);
    }

    @Override
    public long hdel(String hkey, String key) {
        return jedisCluster.hdel(key);
    }

新建RedisService接口

/**
 * redis同步
 * @author Administrator
 *
 */
public interface RedisService {

    /**
     * redis同步
     * @param contentid
     * @return
     */
    public TaotaoResult syncContent(long contentid);
}

Controller层

@Controller
@RequestMapping(value = "/cache/sync")
public class RedisController {

    @Autowired
    private RedisService redisService;


    @RequestMapping(value = "/content/{contentCid}")
    @ResponseBody
    public TaotaoResult contentCacheSync(@PathVariable Long contentCid) {
        TaotaoResult result = redisService.syncContent(contentCid);
        return result;
    }


}

同步缓存服务的调用

需要在后台管理系统中添加一个服务调用的逻辑。当修改内容信息后,需要调用此服务同步缓存。

设置在调用层的resource.properties

#服务层基础URL
REST_BASE_URL=http://localhost:8081/rest
REST_CONTENT_SYNC_URL=/cache/sync/content/

修改,删除,新增服务添加

    @Override
    public TaotaoResult addContent(TbContent content) {

        //把图片信息保存至数据库
        content.setCreated(new Date());
        content.setUpdated(new Date());
        //把内容信息添加到数据库
        contentMapper.insert(content);

        //添加缓存同步逻辑
        HttpClientUtil.doGet(REST_BASE_URL + REST_CONTENT_SYNC_URL + content.getCategoryId());

        return TaotaoResult.ok();

    }

猜你喜欢

转载自blog.csdn.net/weixin_40263776/article/details/79449017