Redis的分布式部署方案-哨兵

Redis 的主从复制模式下,⼀旦主节点由于故障不能提供服务,需要⼈⼯进⾏主从切换,同时⼤量 的客⼾端需要被通知切换到新的主节点上,对于上了⼀定规模的应⽤来说,这种⽅案是⽆法接受的, 于是 Redis 从 2.8 开始提供了 Redis Sentinel(哨兵)加个来解决这个问题。

哨兵机制的介绍

由于对 Redis 的许多概念都有不同的名词解释,所以在介绍 Redis Sentinel 之前,先对⼏个名词 概念进⾏必要的说明,如表所⽰。

名词逻辑结构物理结构
主节点Redis 主服务⼀个独⽴的 redis-server 进程
从节点Redis 从服务⼀个独⽴的 redis-server 进程
Redis 数据节点主从节点主节点和从节点的进程
哨兵节点监控 Redis 数据节点的节点 ⼀⼀个独⽴的 redis-sentinel 进程
哨兵节点集合若⼲哨兵节点的抽象组合若⼲ redis-sentinel 进程
Redis 哨兵(Sentinel)Redis 提供的⾼可⽤⽅案哨兵节点集合 和 Redis 主从节点
应⽤⽅泛指⼀个多多个客⼾端⼀个或多个连接 Redis 的进程

手动恢复redis主从复制的流程

哨兵机制,是通过独立的进程来体现的,和之前的redis-server是不同的进程

redis-sentinel不负责存储数据,只是对其他的redis-server进程起到监控的作用

手动恢复redis主从复制的流程

服务器,要求要有比较高的可用性,7*24运行;服务器长期运行,总会有一些”意外",具体啥时候出意外,咱们也不知道,同时也不能全靠人工来盯着服务器运行;我们可以写一个程序,用程序来盯着服务器的运行状态(监控程序,发现服务器的运行出现状态异常了;往往还需要搭配“报警程序”)

  1. 运维⼈员通过监控系统,发现 Redis 主节点故障宕机
  2. 运维⼈员从所有节点中,选择⼀个(此处选择了 slave 1)执⾏ slaveof no one,使其作为新的主 节点
  3. 运维⼈员让剩余从节点(此处为 slave 2)执⾏ slaveof {newMasterIp} {newMasterPort} 从新主节 点开始数据同步
  4. 更新应⽤⽅连接的主节点信息到 {newMasterIp} {newMasterPort}
  5. 如果原来的主节点恢复,执⾏ slaveof {newMasterIp} {newMasterPort} 让其成为⼀个从节点。 上述过程可以看到基本需要⼈⼯介⼊,⽆法被认为架构是⾼可⽤的。⽽这就是 Redis Sentinel 所要做 的。

如果操作过程出错了咋办,并且恢复的过程也得半个小时以上,这显然并不合适。

自动恢复redis主从复制的流程

监控:这些进程之间,会建立tcp长连接,通过这样的长连接,定期发送心跳包;借助上图的监控机制,就可以及时发现某个主机是否是挂了,如果是从节点挂了,其实没关系;如果是主节点挂了,哨兵就会发挥作用

Redis Sentinel 相⽐于主从复制模式是多了若⼲(建议保持奇数)Sentinel 节点⽤于实现监控数据节 点,哨兵节点会定期监控所有节点(包含数据节点和其他哨兵节点)。针对主节点故障的情况,故障转移流程⼤致如下:

  1. 主节点故障,从节点同步连接中断,主从复制停⽌
  2. 哨兵节点通过定期监控发现主节点出现故障。哨兵节点与其他哨兵节点进⾏协商,达成多数认同主 节点故障的共识。这步主要是防⽌该情况:出故障的不是主节点,⽽是发现故障的哨兵节点,该情况经常发⽣于哨兵节点的⽹络被孤⽴的场景下
  3. 哨兵节点之间使⽤ Raft 算法选举出⼀个领导⻆⾊,由该节点负责后续的故障转移⼯作
  4. 哨兵领导者开始执⾏故障转移:从节点中选择⼀个作为新主节点;让其他从节点同步新主节点;通知应⽤层(客户端)转移到新主节点。

redis哨兵核心功能:

  • 监控
  • 自动的故障转移
  • 通知

redis哨兵节点,有一个也是可以的,但是:

  1. 如果哨兵节点只有一个,它自身也是容易出现问题的,万一这个哨兵节点挂了,后续redis节点也挂了,就无法进行自动的恢复过程了
  2. 出现误判的概率也比较高,网络传输数据是容易出现抖动或者延迟或者丢包的,如果只有一个哨兵节点,出现上述问题之后,影响就比较大
  • 基本原则:在分布式系统中,应该避免使用“单点”
  • 哨兵节点,最好要搞奇数个,最少也应该是3个(和选举机制有关)

使用docker搭建环境

类似下图的结构,按理说这6个节点要在6个不同的服务器主机上的,此时只有一个云服务器,就在一个云服务器上,来完成这里的环境搭建(在工作中,把上述节点放到一个服务器上,是没有意义的)

由于这些节点有点多,相互之间依赖的端口号/配置文件/数据文件可能会冲突,如果直接部署,就需要小心翼翼的取避开这些冲突-使用docker就可以有效避免上述问题

虚拟机,通过软件,在一个电脑上模拟出另外的一些硬件(构造了另一个虚拟的电脑),虚拟机这样的软件,就可以使用一个电脑,来模拟出多个电脑的情况;但是虚拟机比较吃配置

docker可以认为是一个轻量级“虚拟机”,起到了虚拟机这样的隔离环境的效果,但是又没有吃很多的硬件资源,即使是配置比较拉跨的云服务器,也能构造出好几个这样的虚拟环境

  • 安装docker和docker-compose

docker中关键概念“容器”,每一个容器都可以看做一个轻量级的虚拟机

docker-compose 的安装

# ubuntu
apt install docker-compose
# centos
yum install docker-compose
  • 停⽌之前的 redis-server
# 停⽌ redis-server
service redis-server stop
# 停⽌ redis-sentinel 如果已经有的话.
service redis-sentinel stop

  • 使⽤ docker 获取 redis 镜像
 docker pull redis:5.0.9
#类似于git clone从远程仓库拉取代码

docker中的“镜像”和“容器”类似于“可执行程序“和”进程“的关系;镜像可以自己创建,也可以直接拿别人已经构建好的,docker hub(类似于Github)包含了很多其他大佬构建好的镜像

拉取到的镜像,里面包含一个精简的Linux操作系统,并且上面会安装redis,只要直接基于这个镜像创建一个容器跑起来,此时redis服务器就搭建好了

docker-compose来进行容器编排,此处涉及到多个redis-server也有多个redis哨兵节点,每一个redis-server或者每一个redis哨兵节点都是作为一个单独的容器了(6个容器)

通过一个配置文件,把具体要创建哪些容器,每个容器运行的各种参数,描述清楚,后续通过一个简单的命令,就能够批量的启动/停止这些容器了

使用yml这样的格式来作为配置文件,spring也是使用yml来作为配置文件的

  • 创建三个容器,作为redis的数据节点(一主两从)
  • 创建三个容器,作为redis的哨兵节点

其实也是可以用一个yml文件,直接启动6个容器;如果把这6个容器同时启动,可能是哨兵先启动完成,数据节点后启动完成,哨兵可能就会先认为是数据节点挂了,虽然对于大局不影响,但是会影响到观察执行日志的过程

version: '3.7'
services:
#名字是自己起的
 master:
#基于哪个镜像创建的
 image: 'redis:5.0.9'
 container_name: redis-master
#是否自动重启
 restart: always
 command: redis-server --appendonly yes
 ports:
#端口映射,也就是隧道
#也就是XShell中的隧道命令
#宿主机的端口:容器内部的端口
 - 6379:6379
#名字是自己起的,容器名就相当于域名
 slave1:
 image: 'redis:5.0.9'
 container_name: redis-slave1
 restart: always
 command: redis-server --appendonly yes --slaveof redis-master 6379
 ports:
 - 6380:6379
#名字是自己起的
 slave2:
 image: 'redis:5.0.9'
 container_name: redis-slave2
 restart: always
 command: redis-server --appendonly yes --slaveof redis-master 6379
 ports:
 - 6381:6379
#这里格式似乎不太正确 请大家自行抄下面的


version: '3.7'
services:
  master:
    image: 'redis:5.0.9'
    container_name: redis-master
    restart: always
    command: redis-server --appendonly yes
    ports:
      - 6379:6379
  slave1:
   image: 'redis:5.0.9'
   container_name: redis-slave1
   restart: always
   command: redis-server --appendonly yes --slaveof redis-master 6379
   ports:
     - 6380:6379
  slave2:
    image: 'redis:5.0.9'
    container_name: redis-slave2
    restart: always
    command: redis-server --appendonly yes --slaveof redis-master 6379
    ports:
      - 6381:6379


  •  启动所有容器
docker-compose up -d
#-d表示后台运行

此步骤一定要把redis三个服务器彻底关闭,不然无法运行成功 

 ​​​​​​

此处启动的redis就是容器中的redis 

  • 查看运⾏⽇志
docker-compose logs

redis哨兵节点是单独的redis服务器进程

  • 在redis-sentienl中写docker-compose.yml文件
version: '3.7'
services:
#搞三个哨兵节点,并且是奇数个,以利于选举机制
  sentinel1:
    image: 'redis:5.0.9'
    container_name: redis-sentinel-1
#当前节点能够自动启动
    restart: always
#使用后面的软件来启动
    command: redis-sentinel /etc/redis/sentinel.conf
    volumes:
#映射文件,将配置文件映射到/etc/redis/sentinel.conf
#哨兵节点,会在运行过程中,对配置文件进行自动的修改,因此就不能拿一个配置文件,给三个容器分别映射
      - ./sentinel1.conf:/etc/redis/sentinel.conf
    ports:
#端口映射:docker核心功能
      - 26379:26379
  sentinel2:
    image: 'redis:5.0.9'
    container_name: redis-sentinel-2
    restart: always
    command: redis-sentinel /etc/redis/sentinel.conf
    volumes:
      - ./sentinel2.conf:/etc/redis/sentinel.conf
    ports:
      - 26380:26379
  sentinel3:
    image: 'redis:5.0.9'
    container_name: redis-sentinel-3
    restart: always
    command: redis-sentinel /etc/redis/sentinel.conf
    volumes:
      - ./sentinel3.conf:/etc/redis/sentinel.conf
    ports:
      - 26381:26379
  • 创建配置⽂件

创建 sentinel1.conf sentinel2.conf sentinel3.conf . 三份⽂件的内容是完全相同 的,都放到 /root/redis-sentinel/ ⽬录中.

bind 0.0.0.0
#允许其他节点来访问
port 26379
#容器内部端口号
sentinel monitor redis-master redis-master 6379 2
#让当前的哨兵节点监控哪个redis服务器,redis-master本来是IP,但是docker会自动进行域名解析
#6379为端口号,2是法定票数(达到法定票数就可以判定redis主节点挂了)
sentinel down-after-milliseconds redis-master 1000
#down-after-milliseconds明确心跳包的超时时间,设定1000ms之内,如果不返回,就认为挂了,此时投票

报错:哨兵节点不认识redis-master,redis-master相当于一个域名,docker会进行域名解析;docker-compose一下启动了N个容器,此时N个容器处于同一个“局域网”中,可以使这N个容器之间可以相互访问,三个redis-server节点是一个局域网,三个哨兵节点是另一个局域网,默认情况下,这俩网络不是互通的

解决方案:使用docker-compose 把此处的两组服务放到同一个局域网中

docker network ls

  • 在docker-compose.yml中加入配置局域网
networks:
  default:
    external:
      name: redis-data_default

Question:是否可以把6个容器,都写到同一个yml配置中,一次全都启动,不就保证互通问题了吗?


Answer:如果使用这种方案,由于docker-compose启动容器的顺序不确定,就不能保证redis-server一定是在哨兵之前启动的,分成两组就可以保证上述顺序,观察到的日志是比较可控的

在sentinel1.conf文件中产生了新的内容,这些内容都是哨兵节点启动之后,自动进行修改的

哨兵节点的作用演示

哨兵存在的意义,能够在redis主从结构出现问题的时候(比如主节点挂了),此时哨兵节点就能够自动的帮我们重新选出一个主节点,来代替之前挂了的节点,保证整个redis仍然是可用状态

手动把主节点干掉

当主节点挂了之后,哨兵节点开始工作

  • sdown:主观下线,本哨兵节点,认为该节点挂了
  • odown:客观下线,好几个哨兵都认为该节点挂了,达成了一致(法定票数)

此时,主节点挂了这个事情就被实锤了

此时就需要哨兵节点选出一个从节点,作为新的主节点,此处就需要提拔出一个新的主节点

即使原主节点启动,也是作为从节点

主从切换的具体流程

哨兵重新选取主节点的流程

  • 主观下线:哨兵节点通过心跳包,判定redis服务器是否正常工作,如果心跳包没有如约而至,就说明redis服务器挂了,此时还不能排除网络波动的影响,因此就只能是单方面认为这个redis节点挂了
  • 客观下线:多个哨兵都认为主节点挂了(认为挂了的哨兵节点数目达到法定票数),哨兵们就认为这个主节点是客观下线

Question:是否有可能出现非常严重的网络波动,导致所有的哨兵都联系不上redis主节点,误判成挂了呢?

当然是有的,如果出现这个情况,怕是用户的客户端也连不上redis主节点了,此时这个主节点基本也就无法正常工作了

  • 要让多个哨兵节点,选出一个leader,由这个leader负责选一个从节点作为新的主节点

日志中可以看到

三个哨兵节点id

  • 一号哨兵立即给自己投了一票
  • 二号哨兵也立即给了一号哨兵投了一票
  • 三号哨兵也立即给了二号哨兵投了一票
redis-sentinel-1  | 1:X 15 Aug 2024 04:10:18.300 # +vote-for-leader 8727c0b46d6aaaee1d5f00ef3ef100e81e73de34 1
redis-sentinel-1  | 1:X 15 Aug 2024 04:10:18.301 # bbd99057b11998a9d52786d2164eaecfaae64441 voted for bbd99057b11998a9d52786d2164eaecfaae64441 1
redis-sentinel-1  | 1:X 15 Aug 2024 04:10:18.308 # 72f040cb2884e70c8a5faa3c7d0baad4da53fce5 voted for 8727c0b46d6aaaee1d5f00ef3ef100e81e73de34 1

每个哨兵手里只有一票,当哨兵1第一个发现客观下线之后,就立即给自己投了一票,并且告诉2,3,我来负责这个事情;2 3反应慢了半拍,才发现是客观下线,一看1乐意负责这个事情,立即投了赞成票(如果总的票数超过哨兵总数的一半,选举完成了->把哨兵个数设置为奇数个节点,就是为了方便投票)

leader选主节点

  • 此时leader选举完毕,leader就需要挑选一个从节点,作为新的主节点
  1. 优先级:每个redis数据节点,都会在配置文件中,有一个优先级的设置,slave-priority,优先级高的从节点就会胜出
  2. offset:offset最大,就胜出,offset从节点从主节点这边同步数据的进度,数值越大,说明从节点的数据和主节点就越接近
  3. run id:每个redis节点启动的时候随机生成的一串数字(大小全凭缘分)

把新的主节点指定好了之后,leader就会控制这个节点,执行slaveof no one,成为master,再控制其他节点,执行slaveof,让这些其他节点,以新的master作为主节点了

先选leader,再选主节点

小结

上述过程, 都是 "⽆⼈值守" , Redis ⾃动完成的,这样做就解决了主节点宕机之后需要⼈⼯⼲预的问题, 提⾼了系统的稳定性和可⽤性

⼀些注意事项

  • 哨兵节点不能只有⼀个. 否则哨兵节点挂了也会影响系统可⽤性
  • 哨兵节点最好是奇数个. ⽅便选举 leader, 得票更容易超过半数
  • 哨兵节点不负责存储数据. 仍然是 redis 主从节点负责存储
  • 哨兵 + 主从复制解决的问题是 "提⾼可⽤性", 不能解决 "数据极端情况下写丢失" 的问题
  • 哨兵 + 主从复制不能提⾼数据的存储容量. 当我们需要存的数据接近或者超过机器的物理内存,这样的结构就难以胜任了.
  • 为了能存储更多的数据, 就引⼊了集群.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值