六、Spring Boot 整合 NoSQL(2)

本章概要

  • 整合 Redis 集群

6.1.4 Redis 集群整合 Spring Boot

1. 搭建 Redis 集群

(1)集群原理

在 Redis 集群中,所有 Redis 节点彼此互联,节点内部使用二进制协议优化传输速度和宽带。当一个节点挂掉后,集群中超过半数的节点检测失效时才认为该节点已经失效。不同于 Tomcat 集群需要使用反向代理服务器,Redis 集群中的任意节点都可以直接和 Java 客户端连接。Redis 集群上的数据分配则是采用哈希槽(HASH SLOT),Redis 集群中内置了 16384 个哈希槽,当有数据需要存储时,Redis 会首先使用 CRC16 算法对 key 进行计算,将计算获得的结果对 16384 取余,这样每一个 key 都会对应一个取值在 0~16383 之间的哈希槽,Redis 根据这个余数将该条数据存储到对应的 Redis 节点上,开发者可根据每个 Redis 实例的性能来调整每个 Redis 实例上哈希槽的分布范围。

(2)集群规划

在同一台服务器上用不同的端口标识不同的 Redis 服务器(伪分布式集群)
主节点:ip:8001,ip:8002,ip:8003
从节点:ip:8004,ip:8005,ip:8006

(3)集群配置

Redis 集群管理工具 redis-trib.rb 依赖 Ruby 环境,首先需要安装 Ruby 环境,由于 Centos 7 yum 库中默认的 Ruby 版本较低,因此建议采用如下步骤进行安装。
安装基本工具

yum -y install ruby ruby-devel rubygems rpm-build

查看当前版本

ruby -v

在这里插入图片描述

安装yum 源

yum install -y centos-release-scl-rh

安装指定版本 ruby

yum install -y rh-ruby24

使升级后的配置生效

scl enable rh-ruby24 bash

查看当前 ruby 版本

ruby -v

在这里插入图片描述

最后安装 Redis 依赖

yum install gem -y
gem install redis 

在这里插入图片描述

创建 redisCluster 文件夹,将Redis压缩文件(详见CentOS 安装 Redis)复制到redisCluster 目录下,然后编译安装

mkdir /opt/soft/redisCluster
cp /opt/package/redis-6.2.6.tar.gz /opt/soft/redisCluster
tar -zxvf /opt/package/redis-6.2.6.tar.gz -C /opt/soft/redisCluster/
cd /opt/soft/redisCluster/redis-6.2.6
make PREFIX=/opt/soft/redisCluster/redis-6.2.6 install

安装成功后,将redis-6.2.6/src 目录下的 redis-trib.rb 文件复制到 /opt/soft/redisCluster/ 目录下(Redis版本较高,此步骤可以跳过,后面不用 redis-trib.rb 而是使用 redis-cli)

cp /opt/soft/redisCluster/redis-6.2.6/src/redis-trib.rb /opt/soft/redisCluster/

在 redisCluster 文件夹下创建6个文件夹,分别为 8001,8002,8003,8004,8005,8006,再将 redis.config 文件分别往这6个目录中复制一份

mkdir /opt/soft/redisCluster/8001/
mkdir /opt/soft/redisCluster/8002/
mkdir /opt/soft/redisCluster/8003/
mkdir /opt/soft/redisCluster/8004/
mkdir /opt/soft/redisCluster/8005/
mkdir /opt/soft/redisCluster/8006/
cp /opt/soft/redisCluster/redis-6.2.6/redis.conf /opt/soft/redisCluster/8001/
cp /opt/soft/redisCluster/redis-6.2.6/redis.conf /opt/soft/redisCluster/8002/
cp /opt/soft/redisCluster/redis-6.2.6/redis.conf /opt/soft/redisCluster/8003/
cp /opt/soft/redisCluster/redis-6.2.6/redis.conf /opt/soft/redisCluster/8004/
cp /opt/soft/redisCluster/redis-6.2.6/redis.conf /opt/soft/redisCluster/8005/
cp /opt/soft/redisCluster/redis-6.2.6/redis.conf /opt/soft/redisCluster/8006/

修改每个redis.conf 信息,,以8001目录下的为例,如下:

port 8001
# bind 127.0.0.1
cluster-enabled yes
cluster-config-file nodes-8001.conf
protected-mode no
daemonize yes
requirepass root
masterauth root
pidfile /var/run/redis_8001.pid

说明:

  • 端口号为8001
  • cluster-enabled yes 表示开启集群
  • cluster-config-file nodes-8001.conf 表示集群节点的配置文件
  • 每个节点都配置了密码,设置主节点密码 masterauth root ,使从节点可以登录主节点
  • pidfile /var/run/redis_8001.pid pid 文件路径

全部修改完毕后,分别启动 6 个 Redis 实例

/opt/soft/redisCluster/redis-6.2.6/bin/redis-server /opt/soft/redisCluster/8001/redis.conf
/opt/soft/redisCluster/redis-6.2.6/bin/redis-server /opt/soft/redisCluster/8002/redis.conf
/opt/soft/redisCluster/redis-6.2.6/bin/redis-server /opt/soft/redisCluster/8003/redis.conf
/opt/soft/redisCluster/redis-6.2.6/bin/redis-server /opt/soft/redisCluster/8004/redis.conf
/opt/soft/redisCluster/redis-6.2.6/bin/redis-server /opt/soft/redisCluster/8005/redis.conf
/opt/soft/redisCluster/redis-6.2.6/bin/redis-server /opt/soft/redisCluster/8006/redis.conf

在这里插入图片描述

(4)创建 Redis 集群

注意:使用 ip:port ,不能使用 localhost:port。如果使用的是云服务器,此处的 ip 应该是外网 ip ,我就是在创建集群的时候用了云服务器的内网 ip 导致redis 连不上,耗费了大量时间。

/opt/soft/redisCluster/redis-6.2.6/bin/redis-cli --cluster create ip:8001 ip:8002 ip:8003 ip:8004 ip:8005 ip:8006 --cluster-replicas 1 -a root

在这里插入图片描述

其中,–cluster-replicas 表示每个主节点的 slave 数量。在集群的创建过程中会分配主机和从机,每个集群在创建过程中都将分配一个唯一的id 并分配到一段 slot 。
创建成功后,登录任意 Redis 实例

/opt/soft/redisCluster/redis-6.2.6/bin/redis-cli -p 8001 -a root -c

-p 表示要登录的集群的端口,-a 表示要登录的集群的密码,-c 表示以集群的方式登录。登录成功后,通过 cluster info 命令可以查询集群状态信息。
在这里插入图片描述

通过 cluster nodes 命令可以查询集群节点信息 ,在集群节点信息中,可以看到每一个节点的id,该节点是 slave 还是master ,如果是slave ,那么它的 master 的 id 是什么,如果是 master ,那么每一个 master 的slot 范围是多少,这些信息都会显示出来。
在这里插入图片描述

(5)添加主节点

当集群创建成功后,随着业务的增长,有可能需要添加主节点,添加主节点需要先构建主节点实例,将 redisCluster 目录下的 8001 目录再复制一份,名为 8007 ,根据第(3)步 的集群配置修改 8007 目录下的 redis.conf 文件,修改完后,再运行如下命令启动该节点

/opt/soft/redisCluster/redis-6.2.6/bin/redis-server /opt/soft/redisCluster/8007/redis.conf

启动成功后,将该节点添加到集群中

/opt/soft/redisCluster/redis-6.2.6/bin/redis-cli --cluster add-node ip:8007 ip:8001 -a root

在这里插入图片描述

登录任意一个Redis实例,查看 Redis 集群信息

/opt/soft/redisCluster/redis-6.2.6/bin/redis-cli -p 8001 -a root -c
cluster nodes 

在这里插入图片描述

可以看到实例已经被添加进集群中,但是由于slot 已经被之前的实例分配完了,新添加的实例没有 slot ,也就意味着新添加的实例没有存储数据的机会,此时需要从另外三个实例中拿出一部分slot分配给新实例,先对 slot 重新分配(连接集群中的任意一个实例即可)

/opt/soft/redisCluster/redis-6.2.6/bin/redis-cli --cluster reshard ip:8001 -a root

在命令执行过程中有四个核心配置需要手动配置,如图
在这里插入图片描述

第一个配置是要拿出多少个slot分配给新的实例,此处配置了 1000 个。
第二个是把拿出来的 1000 个 slot 配置给谁,输入接收这 1000 个 slot 的 Redis 实例的 id,这个id在节点添加成功后就可以看到,也可以进入集群控制台后 利用 cluster nodes 查看。
第三个配置是这 1000 个 slot 由哪个实例出,例如从端口为 8001 的实例中拿出 1000 个 slot 分给端口 为 8007 的实例,那么这里输入 8001 的 id 后回车即可,如果想将 1000 个 slot 均摊到所有的实例中,这里输入 all 按回车即可。
第四个配置指这 1000 个 slot 已经从集群中均摊出来了,是否开始分给指定 Redis 实例,输入yes即可
在这里插入图片描述

再次查看节点信息,可以看到新实例也有slot 了,如图
在这里插入图片描述

(6)添加从节点

先将 redisCluster 目录下的 8001 目录复制一份,命名为 8008 ,然后修改相应配置 redis.conf。修改完毕后启动该实例,然后输入一下命令添加从节点:

/opt/soft/redisCluster/redis-6.2.6/bin/redis-cli --cluster add-node ip:8008 ip:8001 --cluster-slave --cluster-master-id e3fccbce09b63dab42b400e8676ffbabbe4c8eb4 -a root

说明:

  • 10.0.12.5:8008 从节点的地址
  • 10.0.12.5:8001 集群中的任意一个实例地址
  • e3fccbce09b63dab42b400e8676ffbabbe4c8eb4 把这个从节点分配给哪个主节点

在这里插入图片描述

再次查看集群节点信息
在这里插入图片描述

(7)删除节点

如果删除的节点是从节点,则可使用以下命令直接删除

/opt/soft/redisCluster/redis-6.2.6/bin/redis-cli --cluster del-node ip:8004 73bcf87ddd65228c9e7b79fb24e24def06f28867 -a root

在这里插入图片描述

查看集群信息,可以看到已经删除了
在这里插入图片描述

如果删除主节点需要先把它的从节点重新指定到其它主节点,登录将将要删除的主节点的从节点,重新指定它的主节点

cluster replicate af130b04eecd85692fdc2f53c1bd42c8de47d843

在这里插入图片描述

可以看到 8005 的主节点已由 8003 变为了 8001 ,而此时 8003 下面无任何从节点。然后再把将要删除的 master 的 slot 给分配给其它主节点

/opt/soft/redisCluster/redis-6.2.6/bin/redis-cli --cluster reshard ip:8003 -a root

把所有 slot 分出去
在这里插入图片描述

设置接收 slot 的 id
在这里插入图片描述

然后输入总slot数 ,接着输入 all ,然后输入 yes
在这里插入图片描述

最后查看节点信息,可以发现此 master 上已经没有 slot 了
在这里插入图片描述

接着删除 master

/opt/soft/redisCluster/redis-6.2.6/bin/redis-cli --cluster del-node ip:8003 1131d08bc3cc448367d7f5914c63ec8638c195e0 -a root

在这里插入图片描述

再次查看,此 master 已经被删除了
在这里插入图片描述

(8)重建集群

如果是重建的的话,先关闭所有redis服务,删除以下文件,可以先通过 find / -name appendonly.aof 命令查找文件所在位置

  • appendonly.aof
  • dump.rdb
  • nodes-*.conf(nodes-8001.conf、nodes-8002.conf…)

然后再重新创建集群即可

2.配置 Spring Boot

不同于单机版 Redis 整合 Spring Boot ,Redis 集群整合 Spring Boot 需要开发者手动配置,配置步骤如下:

(1)创建 Spring Boot 项目

首先创建一个 Spring Boot Web 项目,添加如下依赖

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
  <groupId>redis.clients</groupId>
  <artifactId>jedis</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.data</groupId>
  <artifactId>spring-data-redis</artifactId>
</dependency>
<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-configuration-processor</artifactId>
  <optional>true</optional>
</dependency>

(2)配置集群信息

由于集群节点有多个,可以保存在一个集合中,因此这里的配置文件使用YAML格式的,删除 resources 目录下的application.properties 文件,创建 application.yml 配置文件,文件内容如下:

spring:
  redis:
    cluster:
      ports:
        - 8001
        - 8002
        - 8003
        - 8004
        - 8005
        - 8006
        - 8007
        - 8008
      host:ip地址
      poolConfig:
        max-total:8
        max-idle:8
        max-wait-millis:-1
        min-idle:0

由于本案例 Redis 实例的 host 都是一样的,因此这里配置了一个 host ,而 port 配置成了一个集合,这些 port 将被注入一个集合中。poolConfig 则是基本的连接池信息配置。

(3)配置 Redis

创建 RedisConfig,完成对 Redis 的配置,如下:

@Configuration
@ConfigurationProperties("spring.redis.cluster")
public class RedisConfig {
    
    
    List<Integer> ports;
    String host;
    JedisPoolConfig poolConfig;

    @Bean
    RedisClusterConfiguration redisClusterConfiguration() {
    
    
        RedisClusterConfiguration configuration = new RedisClusterConfiguration();
        List<RedisNode> nodes = new ArrayList<>();
        for (Integer port : ports) {
    
    
            nodes.add(new RedisNode(host, port));
        }
        configuration.setPassword(RedisPassword.of("root"));
        configuration.setClusterNodes(nodes);
        return configuration;
    }

    @Bean
    RedisTemplate redisTemplate() {
    
    
        RedisTemplate redisTemplate = new RedisTemplate();
        redisTemplate.setConnectionFactory(jedisConnectionFactory());
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
        return redisTemplate;
    }

    @Bean
    StringRedisTemplate stringRedisTemplate() {
    
    
        StringRedisTemplate stringRedisTemplate = new StringRedisTemplate(jedisConnectionFactory());
        stringRedisTemplate.setKeySerializer(new StringRedisSerializer());
        stringRedisTemplate.setKeySerializer(new StringRedisSerializer());
        return stringRedisTemplate;
    }

    @Bean
    JedisConnectionFactory jedisConnectionFactory() {
    
    
        JedisConnectionFactory factory = new JedisConnectionFactory(redisClusterConfiguration(),poolConfig);
        return factory;
    }

    public List<Integer> getPorts() {
    
    
        return ports;
    }

    public void setPorts(List<Integer> ports) {
    
    
        this.ports = ports;
    }

    public String getHost() {
    
    
        return host;
    }

    public void setHost(String host) {
    
    
        this.host = host;
    }

    public JedisPoolConfig getPoolConfig() {
    
    
        return poolConfig;
    }

    public void setPoolConfig(JedisPoolConfig poolConfig) {
    
    
        this.poolConfig = poolConfig;
    }
}

代码解释:

  • 通过 @ConfigurationProperties 注解声明配置文件前缀,配置文件中定义的 ports 数组、host 以及 连接池信息都将被注入 port、host、poolConfig 三个属性中
  • 配置 RedisClusterConfiguration 实例,设置 Redis 登录密码以及 Redis 节点信息
  • 根据 RedisClusterConfiguration 实例以及连接池配置信息创建 Jedis 连接工厂 JedisConnectionFactory
  • 根据 JedisConnectionFactory 创建 RedisTemplate 和 StringRedisTemplate,同时配置 key 和 value 的序列化方式。有了 RedisTemplate 和 StringRedisTemplate ,剩下的用法就和单实例的用法一致了

(4)创建 Controller 和 Book 实例

@RestController
public class BookController {
    
    
    @Autowired
    RedisTemplate redisTemplate;
    @Autowired
    StringRedisTemplate stringRedisTemplate;
    @GetMapping("/test1")
    public void test1() {
    
    
        ValueOperations ops = redisTemplate.opsForValue();
        Book book = new Book();
        book.setName("水浒传");
        book.setAuthor("施耐庵");
        ops.set("b1", book);
        System.out.println(ops.get("b1"));
        ValueOperations<String, String> ops2 = stringRedisTemplate.opsForValue();
        ops2.set("k1", "v1");
        System.out.println(ops2.get("k1"));
    }
}
public class Book implements Serializable {
    
    
    private String name;
    private String author;
    @Override
    public String toString() {
    
    
        return "Book{" +
                "name='" + name + '\'' +
                ", author='" + author + '\'' +
                '}';
    }

    public String getName() {
    
    
        return name;
    }

    public void setName(String name) {
    
    
        this.name = name;
    }

    public String getAuthor() {
    
    
        return author;
    }

    public void setAuthor(String author) {
    
    
        this.author = author;
    }
}

(5)测试

启动项目,http://localhost:8081/test1,控制台打印日志如下:

Book{name='水浒传', author='施耐庵'}
v1

然后登录任意一个 Redis 实例,查询数据,结果如下:
在这里插入图片描述

从日志中可以看出,查询时只需要登录任意一个 Redis 实例, RedisCluster 会负责将查询请求 Redirected 到相应的实例上去。

猜你喜欢

转载自blog.csdn.net/GXL_1012/article/details/126154839