基本概念
主从复制的机制是其他高可用方式的基础,下面介绍的哨兵方式和集群方式都依赖于主从复制机制
- 若干 Sentinel 节点组成的分布式集群,可以实现故障发现、故障自动转移、配置中心和客户端通知
- 节点数量要满足 2n+1(n>=1)的奇数个
- 哨兵节点: 哨兵系统由一个或多个哨兵节点组成,哨兵节点是特殊的 Redis 节点,不存储数据
- 数据节点: 主节点和从节点都是数据节点
哨兵方式在主从复制的基础上,实现了故障自动切换的功能
:
监控
: 哨兵会不断地检查主节点和从节点是否运作正常(发布-订阅
)自动故障转移
:- 当主节点不能正常工作时,哨兵会开始 自动故障转移操作
- 它会将失效主节点的其中一个从节点升级为新的主节点,并让其他从节点改为复制新的主节点
配置提供者
: 客户端在初始化时,通过连接哨兵来获得当前 Redis 服务的主节点地址通知
:哨兵可以将故障转移的结果发送给客户端
配置及使用
以
一主二从三哨兵
的架构来搭建一个哨兵系统
数据节点
找到
redis.conf
文件复制三份: redis-master.conf、redis-slave1.conf、redis-slave2.conf
# redis-master.conf master
port 6380
daemonize yes
logfile "6380.log"
dbfilename "dump-6380.rdb"
# redis-slave1.conf slave-1
port 6381
daemonize yes
logfile "6381.log"
dbfilename "dump-6381.rdb"
slaveof 127.0.0.1 6380
# redis-slave2.conf slave-2
port 6382
daemonize yes
logfile "6382.log"
dbfilename "dump-6382.rdb"
slaveof 127.0.0.1 6380
####################################################################################
# 启动三个 redis 实例
/usr/local/redis/bin/redis-server redis-master.conf
/usr/local/redis/bin/redis-server redis-slave1.conf
/usr/local/redis/bin/redis-server redis-slave2.conf
####################################################################################
# 查看redis进程
ps -aux | grep redis
####################################################################################
# 连接主节点,查看状态(info Replication)
redis -h 127.0.0.1 -p 6380
哨兵节点
哨兵节点本质上是特殊的 Redis 节点,所以配置几乎没什么差别,只是在端口上做区分
# redis-sentinel-1.conf
port 16380
daemonize yes
logfile "16380.log"
sentinel monitor redis-master 127.0.0.1 6380 2
# redis-sentinel-2.conf
port 16381
daemonize yes
logfile "16381.log"
sentinel monitor redis-master 127.0.0.1 6380 2
# redis-sentinel-3.conf
port 16382
daemonize yes
logfile "16382.log"
sentinel monitor redismaster 127.0.0.1 6380 2
####################################################################################
# 启动哨兵节点
/usr/local/redis/bin/redis-server redis-sentinel-1.conf --sentinel
/usr/local/redis/bin/redis-server redis-sentinel-2.conf --sentinel
/usr/local/redis/bin/redis-server redis-sentinel-3.conf --sentinel
####################################################################################
# 登录哨兵节点,查看状态(info Sentinel)
redis -h 127.0.0.1 -p 16380
sentinel monitor redis-master 127.0.0.1 6380 2
- 该哨兵节点监控
127.0.0.1:63780
这个主节点,该主节点的名称是redis-master
-
`2` :主节点的故障判定有关,至少需要 `2` 个哨兵节点同意,才能判定主节点故障并进行故障转移
Python连接
from redis import Sentinel
sentinel = Sentinel(
[('127.0.0.1', 16380), ('127.0.0.1', 16381), ('127.0.0.1', 16382)],
socket_timeout=0.5
)
# 获取主服务器地址,discover_master 参数为自己的mastername
master = sentinel.discover_master('redis-master')
print(master)
Sentinel命令
# 展示所有被监控的主节点状态以及相关的统计信息
sentinel masters
# 展示指定<master name>的主节点状态以及相关的统计信息
sentinel master <master name>
# 展示指定<master name>的从节点状态以及相关的统计信息
sentinel slaves <master name>
# 展示指定<master name>的Sentinel节点集合(不包含当前Sentinel节点)
sentinel sentinels <master name>
# 返回指定<master name>主节点的IP地址和端口
sentinel get-master-addr-by-name <master name>
# 对指定<master name>主节点进行强制故障转移
sentinel failover <master name>
# 检测当前可达的Sentinel节点总数是否达到<quorum>的个数
sentinel ckquorum <master name>
# 取消当前Sentinel节点对于指定<master name>主节点的监控
sentinel remove <master name>
# 通过命令的形式来完成Sentinel节点对主节点的监控
sentinel monitor <master name> <ip> <port> <quorum>
# 动态修改Sentinel节点配置选项
sentinel set <master name>
# Sentinel节点之间用来交换对主节点是否下线的判断
# 根据参数的不同,还可以作为Sentinel领导者选举的通信方式
sentinel is-master-down-by-addr
流程及原理
定时监控
- 每隔 10 秒,每个 Sentinel 节点会向主节点和从节点发送 info 命令获取 Redis 数据节点的信息
- 这也是为什么 Sentinel 节点不需要显式配置监控从节点
- 当有新的从节点加入时都可以立刻感知出来
- 节点不可达或者故障转移后,可以通过 info 命令实时更新节点拓扑信息
- 每隔 2 秒,每个 Sentinel 节点会向 Redis 数据节点的
__sentinel__:hello
频道上发送该 Sentinel 节点对于主节点的判断以及当前 Sentinel 节点的信息,同时每个 Sentinel 节点也会订阅
该频道,来了解其他Sentinel 节点以及它们对主节点的判断- 消息格式
- s_ip:sentinel的ip
- s_port:sentinel的端口
- s_runid:sentinel云心id
- s_epoch:sentinel当前的配置纪元
- m_name:主服务器名字
- m_ip:主服务器ip
- m_port:主服务器端口
- m_epoch:主服务器纪元
- 发现新的 Sentinel 节点
- 通过订阅主节点的
__sentinel__:hello
了解其他的 Sentinel 节点信息 - 如果是新加入的 Sentinel 节点,将该 Sentinel 节点信息保存起来(sentinelRedisInstance)
- 并与该 Sentinel 节点创建连接
- 通过订阅主节点的
- 消息格式
主观/客观下线
主观下线
单个 sentinel 认为某个服务下线(有可能是接收不到订阅,之间的网络不通等等原因)
sentinel 会以每秒一次的频率向所有与其建立了命令连接的实例(master、slave、其他 sentinel)发 ping 命令
通过判断 ping 回复是有效回复还是无效回复,来判断实例是否在线(对该 sentinel 来说是主观在线
)
- sentinel 配置文件中的
down-after-milliseconds
设置了判断主观下线的时间长度
- 如果实例在 down-after-milliseconds 毫秒内返回的都是无效回复,那么 sentinel 会认为该实例已下线(主观)
- 并修改其 flags 状态为 SRI_S_DOWN
在实际的工作当中,多个 sentinel 配置的 down-after-milliseconds 超时时间推荐一致
客观下线
只针对主节点
,从节点和哨兵节点不需要这一步
当 Sentinel 主观下线的节点是主节点时:
- 该 Sentinel 节点会通过 sentinel ismaster-down-by-addr 命令向其他 Sentinel 节点询问对主节点的判断
- 当超过配置中的
<quorum>
个数,Sentinel 节点认为主节点确实有问题 - 这时该 Sentinel 节点会做出客观下线的决定,并后续对其做故障转移操作
如果每个 Sentinel 配置的 down-after-milliseconds 时间不一致,会很难超过<quorum>
配置的个数
sentinel is-master-down-by-addr <ip> <port> <current_epoch> <runid>
eg:
sentinel is-master-down-by-addr 127.0.0.1 6379 0 *
- ip:主节点 IP
- port:主节点端口
- current_epoch:当前配置纪元
- runid:此参数有两种类型,当 runid为
*
:- Sentinel 节点直接交换对主节点下线的判定
- 在领导者Sentinel 节点选举时,runid 等于当前 Sentinel 节点的 runid
- 作用是当前 Sentinel 节点,希望目标 Sentinel 节点同意自己成为领导者的请求
一个 sentinel 接收另一个 sentinel 发来的 is-master-down-by-addr 后
提取参数,根据 ip 和端口,检测该服务时候在该 sentinel 主观下线,并且回复 is-master-down-by-addr
- down_state:目标 Sentinel 节点对于主节点的下线判断,1 是下线,0 是在线
- leader_runid:
*
:代表返回结果是用来做主节点是否不可达具体的 runid
:代表目标节点同意 runid 成为领导者
- leader_epoch:领导者纪元
领导者 Sentinel 选举
Redis 使用了 Raft 算法实现领导者选举,大体思路:
- 每个在线的 Sentinel 节点都有资格成为领导者,每个 Sentinel 节点发现当它确认主节点客观下线检查时候,会向其他 Sentinel 节点发送 sentinel is-master-down-by-addr 命令,要求将自己设置为领导者
- 每个节点在每个选举轮次中只有一次投票权,收到命令的 Sentinel 节点,如果没有同意过其他 Sentinel 节点的 sentinelis-master-down-by-addr 命令,将同意该请求,否则拒绝
- 如果该 Sentinel 节点发现自己的票数已经大于等于 max(quorum,num(sentinels)/2+1),那么它将成为领导者。其他的投票就会终止,即使后续还有其他的哨兵节点到达配置,也没有作用
- 如果此过程没有选举出领导者, 暂定一段时间,再进行下一轮选举,current_epoch 加 1
选举流程
- s1 (哨兵节点) 节点首先完成客观下线的检查,然后向 s2 和 s3 发送成为领导者的请求
- s2 节点完成客观下线的检查,然后向 s1 和 s3 发送成为领导者的请求
- s3节点完成客观下线的检查,然后向 s1和 s2发送成为领导者的请求
故障转移
- 在从节点列表中选出一个作为新的主节点
- 过滤
- “不健康”节点(主观下线、断线)
- 5 秒内没有回复过 Sentinel 节点 ping 响应
- 与主节点失联超过 down-after-milliseconds*10s(断线时间越长主从数据不一致问题越严重)
- 选择 slave-priority(从节点优先级)最高的从节点列表,如果存在则返回,不存在则继续
- 如果优先级一样,选择
复制偏移量最大的从节点
(复制的最完整),如果存在则返回,不存在则继续 - 如果偏移量一样,选择 runid 最小的从节点
- 挑选从节点的重要原则:
在从节点列表中挑选与主节点数据最一致的节点
- 过滤
- Sentinel 领导者节点会对第一步选出来的从节点执行 slaveof no one 命令让其成为主节点
- Sentinel 领导者节点会向剩余的从节点发送命令,让它们成为新主节点的从节点,复制规则和 parallel-syncs 参数有关:slaveof ip port
- Sentinel 节点集合会将原来的主节点更新为从节点,并保持着对其关注,当其恢复后命令它去复制新的主节点
优点
- 在Redis主从复制的基础上,
增加了故障自动转移
- 方便实现 Redis 数据节点的线形扩展
- 突破 Redis 自身单线程瓶颈,可较大程度上满足Redis 大容量或高性能的业务需求
- 可以实现一套 Sentinel 监控一组 Redis 数据节点或多组数据节点
缺点
- 部署相对 Redis 主从模式要复杂一些,原理理解更繁琐
- 资源浪费,Redis 数据节点中
slave 节点作为备份节点不提供服务
- Redis Sentinel 主要是
针对主节点的高可用切换
:- 主观下线(并不执行故障转移)
- 客观下线
- 不能解决读写分离问题,实现起来相对复杂
全部配置
# Example sentinel.conf
# 哨兵sentinel实例运行的端口 默认26379
port 26379
# 哨兵sentinel的工作目录
dir /tmp
# 哨兵sentinel监控的redis主节点的 ip port
# master-name 可以自己命名的主节点名字 只能由字母A-z、数字0-9 、这三个字符".-_"组成。
# quorum 配置多少个sentinel哨兵统一认为master主节点失联 那么这时客观上认为主节点失联了
# sentinel monitor <master-name> <ip> <redis-port> <quorum>
sentinel monitor mymaster 127.0.0.1 6379 2
# 当在Redis实例中开启了requirepass foobared 授权密码 这样所有连接Redis实例的客户端都要提供密码
# 设置哨兵sentinel 连接主从的密码 注意必须为主从设置一样的验证密码
# sentinel auth-pass <master-name> <password>
sentinel auth-pass mymaster MySUPER--secret-0123passw0rd
# 指定多少毫秒之后 主节点没有应答哨兵sentinel 此时 哨兵主观上认为主节点下线 默认30秒
# sentinel down-after-milliseconds <master-name> <milliseconds>
sentinel down-after-milliseconds mymaster 30000
# 这个配置项指定了在发生failover主备切换时最多可以有多少个slave同时对新的master进行 同步,这个数字越小,完成failover所需的时间就越长,但是如果这个数字越大,就意味着越 多的slave因为replication而不可用。可以通过将这个值设为 1 来保证每次只有一个slave 处于不能处理命令请求的状态。
# sentinel parallel-syncs <master-name> <numslaves>
sentinel parallel-syncs mymaster 1
# 故障转移的超时时间 failover-timeout 可以用在以下这些方面:
#1. 同一个sentinel对同一个master两次failover之间的间隔时间。
#2. 当一个slave从一个错误的master那里同步数据开始计算时间。直到slave被纠正为向正确的master那里同步数据时。
#3.当想要取消一个正在进行的failover所需要的时间。
#4.当进行failover时,配置所有slaves指向新的master所需的最大时间。不过,即使过了这个超时,slaves依然会被正确配置为指向master,但是就不按parallel-syncs所配置的规则来了
# 默认三分钟
# sentinel failover-timeout <master-name> <milliseconds>
sentinel failover-timeout mymaster 180000
# SCRIPTS EXECUTION
# 配置当某一事件发生时所需要执行的脚本,可以通过脚本来通知管理员,例如当系统运行不正常时发邮件通知相关人员。
# 对于脚本的运行结果有以下规则:
# 若脚本执行后返回1,那么该脚本稍后将会被再次执行,重复次数目前默认为10
# 若脚本执行后返回2,或者比2更高的一个返回值,脚本将不会重复执行。
# 如果脚本在执行过程中由于收到系统中断信号被终止了,则同返回值为1时的行为相同。
# 一个脚本的最大执行时间为60s,如果超过这个时间,脚本将会被一个SIGKILL信号终止,之后重新执行。
# 通知型脚本: 当sentinel有任何警告级别的事件发生时(比如说redis实例的主观失效和客观失效等等),将会去调用这个脚本,这时这个脚本应该通过邮件,SMS等方式去通知系统管理员关于系统不正常运行的信息。调用该脚本时,将传给脚本两个参数,一个是事件的类型,一个是事件的描述。如果sentinel.conf配置文件中配置了这个脚本路径,那么必须保证这个脚本存在于这个路径,并且是可执行的,否则sentinel无法正常启动成功。
# 通知脚本
# shell编程
# sentinel notification-script <master-name> <script-path>
sentinel notification-script mymaster /var/redis/notify.sh
# 客户端重新配置主节点参数脚本
# 当一个master由于failover而发生改变时,这个脚本将会被调用,通知相关的客户端关于master地址已经发生改变的信息。
# 以下参数将会在调用脚本时传给脚本:
# <master-name> <role> <state> <from-ip> <from-port> <to-ip> <to-port>
# 目前<state>总是“failover”,
# <role>是“leader”或者“observer”中的一个。
# 参数 from-ip, from-port, to-ip, to-port是用来和旧的master和新的master(即旧的slave)通信的
# 这个脚本应该是通用的,能被多次调用,不是针对性的。
# sentinel client-reconfig-script <master-name> <script-path>
sentinel client-reconfig-script mymaster /var/redis/reconfig.sh
- 参考:
评论区