Redis原理之哨兵机制(Sentinel)
上篇文章:
Redis原理之主从复制https://blog.csdn.net/sniper_fandc/article/details/149141103?fromshare=blogdetail&sharetype=blogdetail&sharerId=149141103&sharerefer=PC&sharesource=sniper_fandc&sharefrom=from_link
目录
1 哨兵机制恢复主节点故障流程
1.1 整体流程
1.2 sentinel节点作用
2 搭建主从结构+哨兵
2.1 docker拉取redis镜像
2.2 编排redis数据节点
2.3 编排redis的哨兵节点(sentinel)
2.4 演示故障转移
注意:从节点如何变为主节点?从节点主动执行slaveof no one命令,此时从节点变为主节点。但是如果是主节点挂了,从节点是不会自动变为主节点的。此时只有人工干预来选择哪个从节点变为主节点,需要改变主从模式的拓扑结构,比较麻烦。而哨兵机制正式解决该问题的手段。
哨兵机制:Redis提供的高可用性的实现方案,它是独立的redis-sentinel进程,不属于redis提供的redis-server进程。
1 哨兵机制恢复主节点故障流程
1.1 整体流程
哨兵节点会和数据节点建立TCP长连接,并采用心跳包机制,定期向数据节点发送ping,数据节点需要回复pong。如果哨兵节点没有收到pong响应,就会认为该节点不可达(发生故障)。如果是slave节点故障,对读写没有太大影响;如果是master节点故障,则哨兵节点开始工作:
1.master节点故障,主从复制终止。
2.sentinel节点发现master故障(主观下线),与其他sentinel进行“协商”,需要大多数sentinel都认为master故障(客观下线),此时才会确认真正发生master故障(只有一个sentinel认为master故障不一定正确,因为可能是因为该sentinel节点发生网络抖动,从而导致没有收到pong,而实际可能master正常工作)。
3.sentinel节点之间通过Raft算法选举一位作为领导(leader),leader负责故障转移过程。
注意:Raft算法要求每个哨兵节点只有一票,率先发现故障的先发起自身成为leader的投票,其他哨兵节点先收到谁的投票就先投给谁,达到法定投票的节点成为leader。按照Raft算法规则,谁先发现故障,谁就更可能成为leader。因为偶数节点数有较大可能出现平票,因此最好选择奇数节点个数的哨兵。
4.故障转移:leader远程控制某个slave执行slaveof no one命令,则该slave晋升为master节点,然后再控制其他slave进行slaveof到新的master节点,维护主从拓扑关系。当故障转移后,leader会将新的master(ip和端口号)和拓扑关系通知其他客户端。
注意:leader选择slave的三个指标。先看优先级(配置文件的slave-priority或replica-priority),数值越小越优先;优先级一样再看offset偏移量,数值越大越优先(说明数据量越接近原来的master);前两个指标都一样最后看run id,值越小越优先(由于run id是节点运行时redis自动随机生成,因此这个指标实际上也是随机选slave)。
1.2 sentinel节点作用
1.监控:监控所有的数据节点和其他哨兵节点。
2.故障转移:master发生故障后负责从slave选择新的master节点并维护主从拓扑结构。
3.通知:将故障转移后的新master节点和拓扑关系通知给客户端。
注意:sentinel节点数量通常设为奇数比较好,一般是3个。
2 搭建主从结构+哨兵
由于搭建真实的多物理机的redis节点成本较高,因此这里使用容器化技术来进行搭建。需要提前安装docker和docker-compose,docker简单来讲是一个类似虚拟机的容器,它小巧轻量,提供了容器来统一配置环境信息,尤其是在多服务的环境中,配置很容易出现冲突,使用docker创建容器就可以实现多个服务的环境配置的冲突隔离。
docker-compose是容器编排工具,在docker中如果容器较多就容易出现多个容器之间的依赖关系(比如先后启动顺序),使用docker-compose就可以帮助我们进行管理。
2.1 docker拉取redis镜像
使用命令来拉取redis的镜像(拉取之间需要停掉所有的redis服务):
docker pull redis:5.0.9
2.2 编排redis数据节点
编排就是用yml文件来配置多个容器的信息,包括但不限于创建什么容器、容器参数信息(名称、端口号等等),通过一个命令就能批量启动这些容器。
这里搭建一主两从的主从结构,需要定义docker-compose.yml文件(文件名不能变)。
version: '3.7'services:master:image: 'redis:5.0.9'container_name: redis-masterrestart: alwayscommand: redis-server --appendonly yesports:- 6379:6379slave1:image: 'redis:5.0.9'container_name: redis-slave1restart: alwayscommand: redis-server --appendonly yes --slaveof redis-master 6379ports:- 6380:6379slave2:image: 'redis:5.0.9'container_name: redis-slave2restart: alwayscommand: redis-server --appendonly yes --slaveof redis-master 6379ports:- 6381:6379
其中,services下的master、slave1和slave2是服务的名字(自己起),image则是容器基于的镜像,container_name是容器名称,restart是如果节点挂了是否重启,command则是启动节点的方式,ports是端口映射(把物理机的端口映射到容器的端口,容器端口和物理机端口独立,容器端口和容器端口独立,映射了后就可以让物理机访问容器)。
使用命令docker-compose up -d(-d表示后台方式启动)即可启动这三个容器:
这里可以使用redis-cli -p 6379来连接启动的节点:
注意:为什么不把3个数据节点和3个哨兵节点放到一个yml文件?因为如果是哨兵节点先启动,就会认为master节点故障,从而故障转移,但是此时数据节点还未启动,因此这样操作无效。虽然不会对全局容器产生影响,但是对于执行日志的观察不利。
2.3 编排redis的哨兵节点(sentinel)
这里操作和上述一样:
version: '3.7'services:sentinel1:image: 'redis:5.0.9'container_name: redis-sentinel-1restart: alwayscommand: redis-sentinel /etc/redis/sentinel.confvolumes:- ./sentinel1.conf:/etc/redis/sentinel.confports:- 26379:26379sentinel2:image: 'redis:5.0.9'container_name: redis-sentinel-2restart: alwayscommand: redis-sentinel /etc/redis/sentinel.confvolumes:- ./sentinel2.conf:/etc/redis/sentinel.confports:- 26380:26379sentinel3:image: 'redis:5.0.9'container_name: redis-sentinel-3restart: alwayscommand: redis-sentinel /etc/redis/sentinel.confvolumes:- ./sentinel3.conf:/etc/redis/sentinel.confports:- 26381:26379networks:default:external: truename: redis-data_default
由于sentinel启动依赖配置文件sentinel.conf,而在运行过程中sentinel会对配置文件进行修改来适应具体节点的情况(配置重写rewrite),因此不能使用同一个配置文件。volumes参数则描述了配置文件分别映射为三个文件,初始状态一样,但是一旦启动文件内容就会发生变化。这三个配置文件(sentinel1.conf、sentinel2.conf和sentinel3.conf)内容如下:
bind 0.0.0.0port 26379sentinel monitor redis-master redis-master 6379 2sentinel down-after-milliseconds redis-master 1000
bing描述哨兵可被所有ip访问,port描述哨兵的端口号,
sentinel monitor依次描述监控哪个主节点、主节点ip(这里使用主节点名称类似域名,docker可自动解析为ip)、主节点端口号、法定选票(少数服从多数,达到选票数量才会一致认为master出现故障)。
sentinel down-after-milliseconds描述了心跳包超时时间,超过这个时间没有接收到pong就认为master出现故障。
同时,由于使用docker-compose启动一组容器,这相当于创建一个局域网(网络名是docker-compose.yml所在的目录+_default)。如果没有networks字段的配置,这组sentinel节点就又创建了一个局域网,两个局域网无法直接互相访问。因此需要把这组节点加入到数据节点所在的局域网中,配置networks字段的原因正是上述阐述。
使用命令:docker network ls可以查看创建的局域网:
具体操作结果如下:
注意:上述操作中如果启动容器失败,就需要检查配置文件,然后使用命令docker-compose down来关闭容器,再重新启动。使用命令docker-compose logs可以查看docker容器的日志。上述和docker容器相关的命令必须在yml文件同级的目录下进行。
使用命令docker ps -a查看目前启动的容器:
2.4 演示故障转移
使用docker stop redis-master命令关闭master节点:
观察日志发现,sdown表示主观下线,即sentinel-3哨兵节点首先发现master故障。odown表示客观下线,sentinel-3获得的票数达到法定票数(2票):
下图表示投票记录,其他两个哨兵节点均给sentinel-3进行投票,因此sentinel-3成为leader:
sentinel-3作为leader选择ip为172.18.0.4的slave节点(端口号为6380)为新的master,而172.18.0.2的节点是故障的master节点:
观察6380的节点,role已经变成master,同时只有一个从节点连接,从节点的ip是182.18.0.3(端口号为6381):
如果使用docker start redis-master命令让原来的master恢复,则它不会变为master,而是成为从节点:
由此可见,哨兵节点自动的完成了从监控=>发现故障=>选举leader=>leader故障转移=>转移完成通知的过程,极大的保证了主从复制的高可用性。
下篇文章: