之前一篇文章介绍过redis主从复制及搭建过程,redis主从并不是一个高可用的方案,一但master宕机,redis将无法提供写服务,因此需要一种机制可以自动将slave升级为master来保证redis主从的高可用,那么就要用到redis-sentinel。
什么是redis-sentinel
哨兵(sentinel)是一个分布式系统, 用于对主从结构中的每台服务器进行 【监控】,当出现故障时通过投票机制 【选择】 新的master并将所有slave连接到新的master。
redis Sentinel为Redis提供高可用性。实际上,这意味着使用Sentinel可以创建Redis部署,该部署可以在没有人工干预的情况下抵抗某些类型的故障。同时Redis Sentinel是一个分布式系统,即使不是所有的Sentinel进程都在工作,Sentinel也能工作,从而使系统能够抵御故障。
redis-sentinel具备哪些功能
- 监控。Sentinel会不断检查master和slave是否按预期工作。
- 通知。Sentinel可以通过API通知系统管理员或其他计算机程序,其中一个受监视的Redis实例出了问题。
- 自动故障转移。如果某个主服务器没有按预期工作,Sentinel可以启动一个故障转移过程,其中一个副本被提升为主副本,其他附加副本被重新配置为使用新的主副本,并且使用Redis服务器的应用程序会被通知在连接时要使用的新地址。
- 配置提供程序。Sentinel充当客户端服务发现的授权来源:客户端连接到Sentinels,以询问负责给定服务的当前Redis主服务器的地址。如果发生故障转移,Sentinels将报告新地址。
部署哨兵
部署之前的工作
- 由于要考虑sentinel的选举问题,因此sentinel数量必须为大于等于3的奇数
- Sentinel实例应该被放置到被认为以独立方式发生故障的计算机或虚拟机中
- Sentinel Redis分布式系统不保证在失败期间保留已确认的写操作,因为Redis使用异步复制。然而,有一些方法可以部署Sentinel,使丢失写入的窗口限制在某些时刻,而部署它的其他不太安全的方法。
- 需要客户端支持Sentinel
1、启动redis主从服务
这里使用一主多从的结构
使用的docker启动了三个redis容器,容器信息如下:
- master: 172.18.0.9
- slave1: 172.18.0.10
- slave2: 172.18.0.11
详情过程见之前的文章redis主从复制原理及搭建
2、sentinel的配置与启动
配置如下:
port 26379
daemonize no
pidfile "/var/run/redis-sentinel.pid"
logfile "/usr/local/redis/logs/redis-sentinel.log"
dir "/tmp"
# sentinel monitor <master-group-name> <ip> <port> <quorum>
sentinel deny-scripts-reconfig yes
sentinel monitor mymaster 172.18.0.9 6379 2
sentinel down-after-milliseconds mymaster 10000
# Generated by CONFIG REWRITE
protected-mode no
sentinel failover-timeout mymaster 10000
sentinel auth-pass mymaster 123456
3、启动sentinel服务
启动的三个sentinel使用相同的配置,sentinel会通过配置自动连接到master获取主从信息
使用的docker启动了三个redis-sentinel容器,容器信息如下:
- sentinel1: 172.18.0.12
- sentinel2: 172.18.0.13
- sentinel3: 172.18.0.14
使用docker-compose 启动哨兵
# redis-sentinel 哨兵
redis-sentinel1:
image: gongxulei/redis:5.0
container_name: my-app-redis-sentinel1
stdin_open: true
tty: true
privileged: true
restart: always
depends_on:
- redis-master
- redis-slave1
- redis-slave2
environment:
TZ: "Asia/Shanghai"
LANG: "C.UTF-8"
links:
- "redis-master"
- "redis-slave1"
- "redis-slave2"
volumes:
- "/Users/gongxulei/docker/redis/sentinel1/conf:/usr/local/redis/conf"
- "/Users/gongxulei/docker/redis/sentinel1/logs:/usr/local/redis/logs"
command: ["/usr/local/bin/redis-sentinel", "/usr/local/redis/conf/sentinel.conf"]
networks:
default:
ipv4_address: 172.18.0.12
redis-sentinel2:
image: gongxulei/redis:5.0
container_name: my-app-redis-sentinel2
stdin_open: true
tty: true
privileged: true
restart: always
depends_on:
- redis-master
- redis-slave1
- redis-slave2
environment:
TZ: "Asia/Shanghai"
LANG: "C.UTF-8"
links:
- "redis-master"
- "redis-slave1"
- "redis-slave2"
volumes:
- "/Users/gongxulei/docker/redis/sentinel2/conf:/usr/local/redis/conf"
- "/Users/gongxulei/docker/redis/sentinel2/logs:/usr/local/redis/logs"
command: ["/usr/local/bin/redis-sentinel", "/usr/local/redis/conf/sentinel.conf"]
networks:
default:
ipv4_address: 172.18.0.13
redis-sentinel3:
image: gongxulei/redis:5.0
container_name: my-app-redis-sentinel3
stdin_open: true
tty: true
privileged: true
restart: always
depends_on:
- redis-master
- redis-slave1
- redis-slave2
environment:
TZ: "Asia/Shanghai"
LANG: "C.UTF-8"
links:
- "redis-master"
- "redis-slave1"
- "redis-slave2"
volumes:
- "/Users/gongxulei/docker/redis/sentinel3/conf:/usr/local/redis/conf"
- "/Users/gongxulei/docker/redis/sentinel3/logs:/usr/local/redis/logs"
command: ["/usr/local/bin/redis-sentinel", "/usr/local/redis/conf/sentinel.conf"]
networks:
default:
ipv4_address: 172.18.0.14
4、测试故障转移
#进入任意一个sentinel容器,并连接到redis-sentinel
docker exec -it my-app-redis-sentinel1 bash
redis-cli -p 26379
# 通过info命令查询sentinel信息
127.0.0.1:26379> info Sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=172.18.0.9:6379,slaves=2,sentinels=3
# 如上信息显示sentinel启动成功
# 在master节点执行测试故障转移
redis-cli -p 6379 DEBUG sleep 30
在测试故障转移后,master由172.18.0.9自动切换为 172.18.0.10
sentinel的工作原理
1、阶段一:监控阶段
- 获取各个sentinel的状态(是否在线)
- 获取master的状态
- master的属性 runid、role:master
- 各个slave的详细信息
- 获取所有slave的状态(根据master中的slave信息)
- slave属性:runid、role、master_host、slave_host、offset 等
- slave属性:runid、role、master_host、slave_host、offset 等
2、阶段二:通知阶段
sentinel通过阶段一建立的CMD通道发送 publish sentinel: hello 命令来获取各个redis节点的状态,并将信息发布到sentinel的发布订阅网络中,这样就把redis的master和slave的状态信息同步到其他sentinel中。
3、阶段三:故障转移
- 故障master主观下线阶段
当其中一台sentinel发现master无法连接时,会将master标记为SDWON(主观下线)状态,会通过发布订阅中心发布master通知。其他sentinel接收的通知后会向master发送hello,确定master是否无法连接,并将信息发布到订阅中心,如果超过半数的sentinel认为master无法连接,那么master会被标记为ODWON(客观下线)状态。然后进入下一阶段
- sentinel-leader选举阶段
当master被标记为ODOWN状态后,需要有一个sentinel去将master下线,并从slave中选出新的master,然后再通知其他slave将旧的master切换为新的master。而这个sentinel是需要通过内部的选举机制选举的的sentinel-leader。
- 故障转移阶段
- 通过上阶段选举出的sentinel-leader再负责去选出合适的一台slave,将其升级为master,选举条件如下:
- 是否在线
- 连接响应时间
- 与原master断开的时间长短
- 优先级原则:offset、runid等
- 发送指令切换新的master
- 向新的master发送slaveof no one
- 向其他slave发送slaveof 新的master的ip端口
- 通过上阶段选举出的sentinel-leader再负责去选出合适的一台slave,将其升级为master,选举条件如下:
至此master切换完毕,总结sentinel的功能:
- 监控
- 同步信息
- 通知
- 保持联通
- 故障转移
- 发现问题
- 竞选负责人
- 优选新master
- 新master.上任,其他slave切换master,原master作为slave故障回复后连接