赞
踩
Redis 的主从复制模式下,一旦主节点由于故障不能提供服务,需要人工进行主从切换,同时大量的客户端需要被通知切换到新的主节点上,对于上了一定规模的应用来说,这种方案是无法接受的,于是 Redis 从 2.8 开始提供了 Redis Sentinel(哨兵)加个来解决这个问题。
由于对 Redis 的许多概念都有不同的名词解释,所以在介绍 Redis Sentinel 之前,先对几个名词概念进行必要的说明,如下表所示。
Redis Sentinel 相关名词解释:
Redis Sentinel 是 Redis 的高可用实现方案,在实际的生产环境中,对提高整个系统的高可用是非常有帮助的。
Redis 的主从复制模式可以将主节点的数据改变同步给从节点,这样从节点就可以起到两个作用:
第一,作为主节点的⼀个备份,⼀旦主节点出了故障不可达的情况,从节点可以作为后备 “顶” 上来,并且保证数据尽量不丢失(主从复制表现为最终一致性)。第二,从节点可以分担主节点上的读压力,让主节点只承担写请求的处理,将所有的读请求负载均衡到各个从节点上。
但是主从复制模式并不是万能的,它同样遗留下以下几个问题:
Redis 主从复制模式下,主节点故障后需要进行的人工工作是比较繁琐的,在图中大致展示了整体过程。
实际开发过程中,对于服务器后端开发,监控程序是非常重要的。服务器要求要有比较高的可用性,而服务器长期运行总会出现一些 “意外”,但也没法全靠人工来盯着这些服务器运行。所以,此时就可以写一个程序,用程序来盯着服务器的运行状态(监控程序,往往还需要搭配 “报警程序” 来发现服务器的运行出现状态异常)。
当主节点出现故障时,Redis Sentinel 能自动完成故障发现和故障转移,并通知应用方,从而实现真正的高可用。
Redis Sentinel 是一个分布式架构,其中包含若干个 Sentinel 节点和 Redis 数据节点,每个 Sentinel 节点会对数据节点和其余 Sentinel 节点进行监控,当它发现节点不可达时,会对节点做下线表示。如果下线的是主节点,它还会和其他的 Sentinel 节点进行 “协商”,当大多数 Sentinel 节点对主节点不可达这个结论达成共识之后,它们会在内部 “选举” 出一个领导节点来完成自动故障转移的工作,同时将这个变化实时通知给 Redis 应用方。整个过程是完全自动的,不需要人工介入。整体的架构如下图所示。
这里的分布式架构是指:Redis 数据节点、Sentinel 节点集合、客户端分布在多个物理节点上,不要与后面将要学习的 Redis Cluster 分布式混淆。
Redis Sentinel 相比于主从复制模式是多了若干单独的(建议保持奇数,最少应该是 3 个)Sentinel 节点用于实现监控数据节点,哨兵节点会定期监控(这些进程之间会建立 tcp 长连接,通过这样的长连接定期发送心跳包)所有节点(包含数据节点和其他哨兵节点)。
针对主节点故障的情况,故障转移流程大致如下:
通过上面的介绍,可以看出 Redis Sentinel 具有以下几个功能:
注意:只有一个 Redis 哨兵节点也是可以的。但是万一这个哨兵节点挂了,后续 Redis 节点也挂了的话,就无法进行自动回复的过程了。而且出现误判的概率也比较高,毕竟网络传输数据容易出现抖动、延迟或者丢包等情况,只有一个哨兵节点出现上述情况影响较大。
基本原则:在分布式系统中,应该避免使用 “单点”。
参考:【Docker】安装 Docker(Server-Centos、GUI-Windows11)—— 超详细教程-CSDN博客
- # 安装 docker-compose
- yum install docker-compose
-
- # 停⽌之前的 redis-server
- service redis-server stop
-
- # 停⽌ redis-sentinel (如果已经有的话)
- service redis-sentinel stop
-
- # 使⽤ docker 获取 redis 镜像
- docker pull redis:5.0.9
docker 中的 “镜像”(可以自己构建,也可以直接拿别人已经构建好的,docker hub 中提供了 Redis 官方提供的镜像,可以直接拖下来使用)和 “容器” 类似于 “可执行程序” 和 “进程” 的关系。
docker pull 使用 docker 从中央仓库(默认就是 docker hub)拉取镜像:
拉取到的镜像里面包含一个精简的 Linux 操作系统,并且上面会安装 Redis。只要直接基于这个镜像创建一个容器跑起来,此时 Redis 服务器就搭建好了。
此时就可以基于 Docker 来搭建 Redis 哨兵环境了。
此处涉及到多个 redis server,同时也有多个 redis 哨兵节点。 每一个 redis server 或者一个 redis 哨兵节点都是作为一个单独的容器,此时就可以通过一个配置文件把具体要创建每个容器运行的各种参数描述清楚,后续通过一个简单的命令就能够批量的启动 / 停止这些容器了。
创建 ~/redis/redis-data/docker-compose.yml(这个文件名是固定的,不能随意乱取),同时 cd 到 yml 所在目录中。(其实也可以用一个 yml 文件同时启动所有容器(包括数据节点和哨兵节点),但这样做可能是哨兵先启动完成,数据节点后启动完成,哨兵可能就会认为是数据节点挂了,虽然对于大局不影响,但是会影响到观察执行日志的过程)。
- 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
三个容器内部的端口号都是自成一派的,不同容器之间的 6379 是不会有冲突的,可以把它们当作是不同的主机。有的时候,我们希望在容器外访问容器内部的端口,此时需要进行端口映射,把容器里的端口映射到宿主机中,后续访问宿主机的这个端口就相当于在访问对应容器的对应端口了。
经典的配置文件格式:xml(和 html 很相似,区别:html 中的标签都是标准规定的,xml 中的标签都是自定义的)
由于 xml 写起来比较繁琐且占用空间,后来又有了 json 格式,而这里说的 yml 和 json 也有一些相似之处,和 json 这种比较直观的键值对结构相比,json 是使用 { } 来表示层级结构的,yml 则是使用缩进来表示。
yum 相对于 json 的优势:对于格式要求更加严格,可读性会更好,有助于其他人来理解。
注意:docker 中可以通过容器名字,作为 ip 地址,进行相互之间的访问。
也可以直接在 windows 上使用 vscode 编辑好 yml,然后在上传到 Linux 上。
新的写法是不带中间横杠,如下:
如果启动后发现前面的配置有误,需要重新操作,使用 docker-compose down 即可停止并删除刚才创建好的容器。
上述操作必须保证工作目录在 yml 的同级目录中,才能工作。
创建 ~/redis/redis-sentinel/docker-compose.yml,同时 cd 到 yml 所在目录中。
注意:每个目录中只能存在⼀个 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:
- - ./sentinel1.conf:/etc/redis/sentinel.conf
- ports:
- - 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
- networks:
- default:
- external:
- name: redis-data_default
也可以直接在 windows 上使用 vscode 编辑好 yml,然后在上传到 linux 上。
如果没有加上最后这一段:
那么 docker compose 一下子启动 N 个容器,此时这 N 个容器都处于同一个 “局域网” 中,可以让这 N 个容器之间相互访问。三个 redis-server 节点是一个局域网,三个哨兵节点是另一个局域网,默认情况下,这两网络不是互通的。解决方案就是使用 docker compose 把此处的两组服务给放到同一个局域网中(docker network ls 命令可以列出当前 docker 中的局域网),此时再启动这三个节点就直接让他们加入到同一个局域网中,而不是创建新的局域网。
创建 sentinel1.conf、sentinel2.conf、sentinel3.conf,三份文件的初始内容是完全相同的,都放到 /root/redis-sentinel/ 目录中。
- bind 0.0.0.0
- port 26379
- sentinel monitor redis-master redis-master 6379 2
- sentinel down-after-milliseconds redis-master 1000
sentinel monitor 主节点名 主节点ip 主节点端⼝ 法定票数
redis-sentinel 在运行中可能会对配置进行 rewrite,修改文件内容。如果用一份文件,就可能出现修改混乱的情况。
如果启动后发现前面的配置有误,需要重新操作,使用 docker-compose down 即可停止并删除刚才创建好的容器。
上述操作必须保证工作目录在 yml 的同级目录中才能工作。可以看到,哨兵节点已经通过主节点认识到了对应的从节点。
再次打开哨兵的配置文件,发现文件内容已经被自动修改了。
这里的内容就是自动修改的,对比这三份文件, 可以看到配置内容是存在差异的。
哨兵存在的意义:能够在 Redis 主从结构出现问题(比如主节点挂了)时,哨兵节点自动帮我们重新选出一个主节点来代替之前挂了的节点,保证整个 Redis 仍然是可用状态。
手动把 redis-master 干掉:
当主节点挂了之后,哨兵节点就开始工作了,具体如何工作的,来观察哨兵的日志:
可以看到哨兵发现了主节点 sdown,进一步的由于主节点宕机得票达到 3/2,达到法定得票,于是 master 被判定为 odown。
接下来,哨兵们挑选出了⼀个新的 master。在上图中,是 172.18.0.3:6379 这个节点。此时,对于 Redis 来说仍然是可以正常使用的。
手动把 redis-master 启动起来:
观察哨兵日志,可以看到刚才新启动的 redis-master 被当成了 slave:
使用 redis-cli 也可以进⼀步的验证这⼀点:
假定当前环境如上方介绍,三个哨兵(sentenal1、sentenal2、sentenal3),一个主节点(redis-master),两个从节点(redis-slave1、redis-slave2)。
当主节点出现故障,就会触发重新一系列过程。
哨兵节点通过心跳包来判断 Redis 服务器是否正常工作。当 redis-master 宕机,此时 redis-master 和三个哨兵之间的心跳包就没有了。此时,站在三个哨兵的角度来看,redis-master 出现严重故障。此时还不能排除网络波动的影响,因此三个哨兵均会把 redis-master 判定为主观这个 Redis 节点下线(SDown)。
此时,哨兵 sentenal1、sentenal2、sentenal3 均会对主节点故障这件事情进行投票。当故障得票数 >= 配置的法定票数之后
sentinel monitor redis-master 172.22.0.4 6379 2
在这个地方配置的 2,即为法定票数。此时意味着 redis-master 故障这个事情被做实了,此时触发客观下线(ODown)。
是有这个可能的。如果出现这个情况,怕是用户的客户端也连不上 Redis 主节点了,此时这个主节点基本也就无法正常工作了。
“挂了” 不一定是进程崩了,只要是无法访问,都可以被视为是挂了。
接下来需要哨兵把剩余的 slave 中挑选出一个新的 master,这个工作不需要所有的哨兵都参与,只需要选出个代表(称为 leader),由 leader 负责进行 slave 升级到 master 的提拔过程。这个选举的过程涉及到 Raft 算法。
假定一共三个哨兵节点:S1、S2、S3
简而言之,Raft 算法的核心就是 “先下手为强”。谁率先发出了拉票请求,谁就有更大的概率成为 leader。这里的决定因素成了 “网络延时”。网络延时本身就带有一定随机性。
具体选出的哪个节点是 leader,这个并不重要,重要的是能选出一个节点即可 。
挑选规则:
当某个 slave 节点被指定为 master 之后,
上述过程都是 “无人值守”,Redis 自动完成的。这样做就解决了主节点宕机之后需要人工干预的问题,提高了系统的稳定性和可用性。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。