Redis 哨兵集群搭建并使用 RedisTemplate 实现读写分离

发布时间 2023-12-23 21:20:25作者: 乔京飞

上篇博客介绍的 Redis 主从集群搭建,有一个缺点就是 master 和 slave 的角色是固定的,不会发生变化。一旦 master 节点宕机,那么集群就只能提供读服务,无法提供写服务。本篇博客介绍 Redis 哨兵集群的搭建,可以监控 Redis 集群的 master 和 slave 节点,最重要的是一旦 master 宕机,哨兵集群内部会投票选举出一个 slave 节点作为 master 节点,之前的 master 节点如果恢复后,就会变成新 master 的 slave 节点。

本篇博客的代码 Demo 也在上篇博客的基础上,不需要修改任何代码,只需要修改一下 application.yml 文件,即可实现 RedisTemplate 连接哨兵集群实现读写分离的效果,在博客的最后会提供源代码的下载。


一、哨兵集群的搭建

有了上篇博客搭建 Redis 主从集群的经验,那么搭建 Redis 哨兵集群就非常简单,本篇博客仍然使用虚拟机进行模拟搭建,我的虚拟机操作系统是 CentOS7(ip 地址是 192.168.136.128),已经安装好了 docker 和 docker-compose

首先我们先创建好目录 /app/redis-cluster-sentinel ,在其下面创建好相关的 redis 和 sentinel 目录:

image

上图是我已经搭建好的 Redis 哨兵集群目录结构,直接拿来用了。除了 Redis 是主从集群之外,哨兵 Sentinel 本身也是一个 3 节点的集群,因此创建了 3 个 sentinel 的文件夹,里面只存放 sentinel.conf 配置文件。

Redis 集群 3 个节点的配置文件 redis.conf 跟上篇博客的主从集群配置文件一模一样。唯一的不同在于 master 节点的 redis.conf 配置文件,需要多配置一行访问主节点的密码配置,即 masterauth [主节点的访问密码] ,因为 master 节点在实际情况下,有可能会宕机,sentinel 集群会从其它 slave 节点选出一个作为 master 节点,此时之前的 master 节点恢复后就会变成 slave 节点,去连接新的 master 节点同步数据时,也是需要访问 master 节点密码的。

具体 redis1(初始化为 mater 节点)、redis2 和 redis3(这两个节点初始化为 slave 节点)的配置文件如下:

# 这个是 redis1 的配置文件,初始化为 master 节点

protected-mode no
bind 0.0.0.0
save 900 1
save 300 10
save 60 10000
rdbcompression yes
dbfilename dump.rdb
dir /data
# 关闭 aof 日志备份
appendonly no
# 自定义密码
requirepass root
# 启动端口
port 6379
# 访问 master 节点时需要提供的密码
masterauth root
# 虚拟机会有多个 ip,这里指定具体一个 ip 地址
replica-announce-ip 192.168.136.128
# 这个是 redis2 的配置文件,初始化为 slave 节点

protected-mode no
bind 0.0.0.0
save 900 1
save 300 10
save 60 10000
rdbcompression yes
dbfilename dump.rdb
dir /data
# 关闭 aof 日志备份
appendonly no
# 启动端口
port 6479
# 将当前 redis 作为 redis1 的 slave
# 由于 docker 使用 host 模式,使用的是宿主机的 ip
slaveof 192.168.136.128 6379
# 自定义密码
requirepass root
# 访问 master 节点时需要提供的密码
masterauth root
# 虚拟机会有多个 ip,这里指定具体一个 ip 地址
replica-announce-ip 192.168.136.128
# 这个是 redis3 的配置文件,初始化为 slave 节点

protected-mode no
bind 0.0.0.0
save 900 1
save 300 10
save 60 10000
rdbcompression yes
dbfilename dump.rdb
dir /data
# 关闭 aof 日志备份
appendonly no
# 启动端口
port 6579
# 将当前 redis 作为 redis1 的 slave
# 由于 docker 使用 host 模式,使用的是宿主机的 ip
slaveof 192.168.136.128 6379
# 自定义密码
requirepass root
# 访问 master 节点时需要提供的密码
masterauth root
# 虚拟机会有多个 ip,这里指定具体一个 ip 地址
replica-announce-ip 192.168.136.128

然后我们列出 3 个哨兵的配置文件 sentinel.conf 的具体细节:

# 这是 sentinel1 的配置文件

port 26379
protected-mode no
# 虚拟机会有多个 ip,这里指定具体一个 ip 地址
sentinel announce-ip 192.168.136.128
# 配置监控的集群主节点的 ip 和 端口
# 如果有 2 个 sentinel 节点认为主节点主观下线,
# 则主节点就被认为是客观下线
# redis 的集群名称可以随意配置,这里配置为 jobs
sentinel monitor jobs 192.168.136.128 6379 2
# sentinel 会向集群中的节点定期发送心跳检测命令,
# 如果在配置时间(毫秒)内没有响应回复,则被认为是主观下线
sentinel down-after-milliseconds jobs 5000
# 主备切换时,最多有多少个 slave 同时对新的 master 进行同步
sentinel parallel-syncs jobs 2
# 故障转移的超时时间
sentinel failover-timeout jobs 60000
# 如果在 Redis 设置了密码,这里就需要提供访问密码
sentinel auth-pass jobs root
# 这里设置访问 sentinel 的密码
requirepass root
# 数据存储目录
dir /data
# 这是 sentinel2 的配置文件

port 26479
protected-mode no
# 虚拟机会有多个 ip,这里指定具体一个 ip 地址
sentinel announce-ip 192.168.136.128
# 配置监控的集群主节点的 ip 和 端口
# 如果有 2 个 sentinel 节点认为主节点主观下线,
# 则主节点就被认为是客观下线
# redis 的集群名称可以随意配置,这里配置为 jobs
sentinel monitor jobs 192.168.136.128 6379 2
# sentinel 会向集群中的节点定期发送心跳检测命令,
# 如果在配置时间(毫秒)内没有响应回复,则被认为是主观下线
sentinel down-after-milliseconds jobs 5000
# 主备切换时,最多有多少个 slave 同时对新的 master 进行同步
sentinel parallel-syncs jobs 2
# 故障转移的超时时间
sentinel failover-timeout jobs 60000
# 如果在 Redis 设置了密码,这里就需要提供访问密码
sentinel auth-pass jobs root
# 这里设置访问 sentinel 的密码
requirepass root
# 数据存储目录
dir /data
# 这是 sentinel3 的配置文件

port 26579
protected-mode no
# 虚拟机会有多个 ip,这里指定具体一个 ip 地址
sentinel announce-ip 192.168.136.128
# 配置监控的集群主节点的 ip 和 端口
# 如果有 2 个 sentinel 节点认为主节点主观下线,
# 则主节点就被认为是客观下线
# redis 的集群名称可以随意配置,这里配置为 jobs
sentinel monitor jobs 192.168.136.128 6379 2
# sentinel 会向集群中的节点定期发送心跳检测命令,
# 如果在配置时间(毫秒)内没有响应回复,则被认为是主观下线
sentinel down-after-milliseconds jobs 5000
# 主备切换时,最多有多少个 slave 同时对新的 master 进行同步
sentinel parallel-syncs jobs 2
# 故障转移的超时时间
sentinel failover-timeout jobs 60000
# 如果在 Redis 设置了密码,这里就需要提供访问密码
sentinel auth-pass jobs root
# 这里设置访问 sentinel 的密码
requirepass root
# 数据存储目录
dir /data

由于我们要采用 docker 的 host 模式进行部署,因此这里无论是 redis 集群,还是 sentinel 集群,都使用的是虚拟机的 ip 地址 192.168.136.128,因此必须配置不同的端口,具体细节如下:

  • redis 集群的 3 个节点的端口为 6379、6479、6579
  • sentinel 集群的 3 个节点的端口为 26379、26479、26579

最后在 /app/redis-cluster-sentinel 目录下创建 docker-compose.yml 文件,具体内容如下:

version: "3.5"
services:
  redis1:
    image: redis
    container_name: redis1
    restart: always
    privileged: true
    network_mode: "host"
    volumes:
      - /app/redis-cluster-sentinel/redis1/data:/data
      - /app/redis-cluster-sentinel/redis1/redis.conf:/etc/redis.conf
    command:
      redis-server /etc/redis.conf

  redis2:
    image: redis
    container_name: redis2
    restart: always
    privileged: true
    network_mode: "host"
    volumes:
      - /app/redis-cluster-sentinel/redis2/data:/data
      - /app/redis-cluster-sentinel/redis2/redis.conf:/etc/redis.conf
    command:
      redis-server /etc/redis.conf
    depends_on:
      - redis1

  redis3:
    image: redis
    container_name: redis3
    restart: always
    privileged: true
    network_mode: "host"
    volumes:
      - /app/redis-cluster-sentinel/redis3/data:/data
      - /app/redis-cluster-sentinel/redis3/redis.conf:/etc/redis.conf
    command:
      redis-server /etc/redis.conf
    depends_on:
      - redis1

  sentinel1:
    image: redis
    container_name: sentinel1
    restart: always
    privileged: true
    network_mode: "host"
    volumes:
      - /app/redis-cluster-sentinel/sentinel1/sentinel.conf:/etc/sentinel.conf
    command:
      redis-sentinel /etc/sentinel.conf
    depends_on:
      - redis1

  sentinel2:
    image: redis
    container_name: sentinel2
    restart: always
    privileged: true
    network_mode: "host"
    volumes:
      - /app/redis-cluster-sentinel/sentinel2/sentinel.conf:/etc/sentinel.conf
    command:
      redis-sentinel /etc/sentinel.conf
    depends_on:
      - redis1

  sentinel3:
    image: redis
    container_name: sentinel3
    restart: always
    privileged: true
    network_mode: "host"
    volumes:
      - /app/redis-cluster-sentinel/sentinel3/sentinel.conf:/etc/sentinel.conf
    command:
      redis-sentinel /etc/sentinel.conf
    depends_on:
      - redis1

然后在 docker-compose.yml 所在目录运行 docker-compose up -d 命令启动服务即可。

然后使用 docker-compose ps 命令查看服务启动状况:

image

由于在 docker-compose.yml 文件中,各个服务采用的是 docker 的 host 模式部署,因为看不到启动的端口

可以使用 netstat -tulnp | grep redis 命令查看启动的服务端口

image

由于使用 RDM 无法运行 sentinel 命令查看哨兵集群的状态,因此我们需要使用 redis 自带的 redis-cli 客户端工具连接 sentinel 查看集群的状态,可以到 GitHub 下载微软提供的 windows 版本的 redis 文件,使用其内部提供的 redis-cli.exe 工具

微软提供的 windows 版本的 redis 下载地址为:https://github.com/MicrosoftArchive/redis/releases

image

下载好 zip 包,解压缩就可以看到 redis-cli.exe 工具,打开 cmd 命令行,进入到 redis 的目录,运行 redis-cli.exe -h 192.168.136.128 -p 26379 连接其中任意一个 sentinel 节点,然后输入 auth root (我们设置的访问 sentinel 的连接密码是 root)

image

运行 sentinel master jobs 可以查看 reids 的 master 节点信息

image

运行 sentinel slaves jobs 可以查看 reids 的 slave 节点信息

image

运行 sentinel sentinels jobs 可以如果能够查看到除了本 sentinel 节点外其它 sentinel 节点信息,说明 sentinel 集群搭建成功

image

从上面命令的运行结果可以看出,sentinel 任意节点,运行以下命令可以获取到所监控的 redis 节点的主从信息:

# 本篇博客的搭建的 sentinel 集群,配置的名称是 jobs
sentinel master [sentinel集群名称]
sentinel slaves [sentinel集群名称]
# 查看除自己之外,sentinel 集群中的其它 sentinel 节点信息
sentinel sentinels [sentinel集群名称]

二、测试 master 节点宕机

从上面的 sentinel 命令运行结果可以看出,当前 redis 节点的主从信息如下:

  • master 节点为 redis1(192.168.136.128:6379)
  • slaves 节点为 redis2 (192.168.136.128:6479)和 redis3(192.168.136.128:6579)

此时我们运行 Linux 命令停掉 redis1 节点:docker-compose stop redis1

过一小会儿,再启动 redis1 节点:docker-compose start redis1

此时我们再运行 sentinel master jobssentinel slaves jobs 查看 redis 集群主从信息:

image

可以发现,现在 master 节点和 slave 节点信息发生的变化:

  • master 节点变成了 redis2(192.168.136.128:6479)
  • slave 节点变成了 redis1(192.168.136.128:6379)和 redis3(192.168.136.128:6579)

可以看出:redis1 这个 master 节点宕机后,sentinel 集群从 slaves 中选举出了 redis2 作为新的 master 节点,之前的 redis1 即使恢复后,也会作为 redis2 的 slave 节点。这说明 Redis 哨兵集群已经搭建成功,实现了 master 和 slave 的角色切换功能。

我们可以重启集群,恢复以前的 master 和 slave 的状态:docker-compose restart


三、RedisTemplate 操作哨兵集群实现读写分离

本篇博客仍然使用上篇博客的 Demo 代码,一行代码都不需要更改,只需要更改配置文件即可。

这里仅仅列出 application.yml 配置文件的具体内容:

spring:
  redis:
    # 这里配置的是连接具体的 redis 节点的密码
    password: root
    sentinel:
      # 在 sentinel 中配置的集群名称
      master: jobs
      # 这里配置的是连接 sentinel 节点的密码
      password: root
      # 这里只配置 sentinel 集群的节点地址和端口
      # 不需要配置具体的 redis 节点
      nodes:
        - 192.168.136.128:26379
        - 192.168.136.128:26479
        - 192.168.136.128:26579
    jedis:
      pool:
        # 最大连接数
        max-active: 10
        # 最大空闲连接数
        max-idle: 5
        # 最小空闲
        min-idle: 1
        # 连接超时时间(毫秒)
        max-wait: 3000

需要注意的是:

  • spring.redis.password 配置的是连接 redis 实例的密码
  • spring.redis.sentinel.password 配置的是连接 sentinel 的密码
  • spring.reidis.sentinel.nodes 只需要配置 sentinel 集群的节点即可,不需要配置 redis 集群中的节点,因为 RedisTemplate 能够从 sentinel 中获取到 redis 的主从节点的信息。当 redis 主节点变化时,sentinel 能够进行 redis 的 master 和 slave 自动切换,并通知 RedisTemplate 有关 Redis 的 master 和 slave 变化后的信息,从而实现程序自动识别并继续实现读写分离。

跟上篇博客的 Demo 运行情况相似,从控制台日志中,无法看出 RedisTemplate 是从哪个节点读取的数据

验证 RedisTemplate 操作哨兵集群实现读写分离的方法跟上篇博客的方案一样,这里就不再赘述。


本篇博客的源代码下载地址为:https://files.cnblogs.com/files/blogs/699532/springboot_redis_cluster2.zip