Redis 哨兵

Redis 哨兵

Redis 2.8 版本,新增了哨兵模式,以支持“自动故障转移”,它是 Redis 的 HA 方案。

Redis 哨兵模式由一个或多个 Sentinel 实例组成 Sentinel 集群,可以监控任意多个主服务器,以及这些主服务器的所有从服务器;并在被监视的主服务器进入下线状态时,自动将下线主服务器的某个从服务器升级为新的主服务器,然后由新的主服务器代替已下线的主服务器继续处理命令请求。

关键词:高可用监控选主故障转移Raft

哨兵简介

Redis 的主从复制模式,虽然提供了一定程度的 高可用性(High Availability)。但是,当主节点出现故障时,只能通过手动操作将从节点晋升为主节点,这显然是比较低效的。为了解决这个问题,Redis 2.8 版本提供了哨兵模式(Sentinel)来支持“自动故障转移”。

Redis 哨兵模式由一个或多个 Sentinel 实例组成 Sentinel 集群,可以监控任意多个主服务器,以及这些主服务器的所有从服务器;并在被监视的主服务器进入下线状态时,自动将下线主服务器的某个从服务器升级为新的主服务器,然后由新的主服务器代替已下线的主服务器继续处理命令请求。

Sentinel 的主要功能如下:

  • 监控(Monitoring) - Sentinel 不断检查主从服务器是否正常在工作。
  • 通知(Notification) - Sentinel 可以通过一个 api 来通知系统管理员或者另外的应用程序,被监控的 Redis 实例有一些问题。
  • 自动故障转移(Automatic Failover) - 如果一个主服务器下线,Sentinel 会开始自动故障转移:把一个从节点提升为主节点,并重新配置其他的从节点使用新的主节点,使用 Redis 服务的应用程序在连接的时候也被通知新的地址。
  • 配置提供者(Configuration provider) - Sentinel 给客户端的服务发现提供来源:对于一个给定的服务,客户端连接到 Sentinels 来寻找当前主节点的地址。当故障转移发生的时候,Sentinel 将报告新的地址。

启动哨兵

启动一个 Sentinel 可以使用下面任意一条命令,两条命令效果完全相同。

1
2
redis-sentinel /path/to/sentinel.conf
redis-server /path/to/sentinel.conf --sentinel

当一个 Sentinel 启动时,它需要执行以下步骤:

  1. 初始化服务器。
  2. 使用 Sentinel 专用代码。
  3. 初始化 Sentinel 状态。
  4. 初始化 Sentinel 的主服务器列表。
  5. 创建连向被监视的主服务器的网络连接。

Sentinel 本质上是一个运行在“特殊模式”下的 Redis 服务器。Sentinel 模式下 Redis 服务器只支持 PINGSENTINELINFOSUBSCRIBEUNSUBSCRIBEPSUBSCRIBEPUNSUBSCRIBE 七个命令。

创建连向被监视的主服务器的网络连接,Sentinel 将成为主服务器的客户端,它可以向主服务器发送命令,并从命令回复中获取相关的信息。Sentinel 会读入用户指定的配置文件, 为每个要被监视的主服务器创建相应的实例结构, 并创建连向主服务器的命令连接和订阅连接:

  • 命令连接 - 专门用于向主服务器发送命令,并接受命令回复。
  • 订阅连接 - 专门用于订阅主服务器的 __sentinel__:hello 频道。

监控

获取服务器信息

默认情况下, Sentinel 以“每十秒一次”的频率向被监视的主服务器和从服务器发送 INFO 命令,并通过分析 INFO 命令的回复来获取服务器的当前信息。

  • 主服务器 - 可以获取主服务器自身信息,以及其所属从服务器的地址信息。
  • 从服务器 - 从服务器自身信息,以及其主服务器的了解状态和地址。

Sentinel 通过向主服务器发送 INFO 命令来获得主服务器属下所有从服务器的地址信息, 并为这些从服务器创建相应的实例结构, 以及连向这些从服务器的“命令连接”和“订阅连接”

对于监视同一个主服务器和从服务器的多个 Sentinel 来说, 它们会以“每两秒一次”的频率, 通过向被监视服务器的 __sentinel__:hello 频道发送消息来向其他 Sentinel 宣告自己的存在。Sentinel 只会与主服务器和从服务器创建命令连接和订阅连接, Sentinel 与 Sentinel 之间则只创建命令连接。

判断下线

主观下线

默认,每个 Sentinel 以“每秒一次”的频率,向它所知的“所有实例”发送一个 PING 命令

  • “所知”是指,与 Sentinel 创建了命令连接的实例。
  • “所有实例”包括了主服务器、从服务器以及其他 Sentinel 实例。

如果,某实例在指定的时长( down-after-milliseconds 设置的值,单位毫秒)中,未向 Sentinel 发送有效回复, Sentinel 会将该实例判定为“主观下线”

  • 一个有效的 PING 回复可以是:+PONG-LOADING 或者 -MASTERDOWN。如果服务器返回除以上三种回复之外的其他回复,又或者在 指定时间 内没有回复 PING 命令, 那么 Sentinel 认为服务器返回的回复无效。
  • “主观下线”适用于所有主节点和从节点。

客观下线

当一个“主服务器”被 Sentinel 标记为“主观下线”后,为了确认其是否真的下线,Sentinel 会向同样监听该主服务器的其他 Sentinel 发起询问。如果有“足够数量”的 Sentinel 在指定的时间范围内认为主服务器已下线,那么这个“主服务器”被标记为“客观下线”

  • Sentinel 节点通过 sentinel is-master-down-by-addr 命令,向其它 Sentinel 节点询问对某主服务器的 状态判断
  • “足够数量”是指 Sentinel 配置中 quorum 参数所设的值。
  • 客观下线只适用于主节点。

注:默认情况下, Sentinel 以“每十秒一次”的频率向被监视的主服务器和从服务器发送 INFO 命令。当一个主服务器被 Sentinel 标记为“客观下线”时,Sentinel 向该主服务器的所有从服务器发送 INFO 命令的频率,会从“每十秒一次”改为“每秒一次”

选主

Redis Sentinel 采用 Raft 协议 实现了其 Sentinel 选主流程。Raft 是一种共识性算法,想了解其原理,可以参考 深入剖析共识性算法 Raft

当一个“主服务器”被判断为“客观下线”时,监视该主服务器的各个 Sentinel 会进行“协商”,选举出一个领头的 Sentinel(Leader),并由领头 Sentinel 对下线主服务器执行“故障转移”操作

所有在线 Sentinel 都有资格被选为 Leader。

  1. 当一个 Sentinel 认定某主服务器是“客观下线”后,该 Sentinel 会先看看自己是否投过票。
    • 如果已投票给其他 Sentinel 了,在 2 倍故障转移的超时时间内,都不能竞选 Leader——相当于它是一个 Follower
    • 如果未投票,那么该 Sentinel 可以竞选 Leader,转为 Candidate
  2. 如 Raft 协议所描述的,Candidate 需要完成几件事情:
    1. 更新故障转移状态为 start
    2. 将当前纪元(epoch) 加 1,表明开始新一轮的选举——这里的 epoch 相当于 Raft 协议中的 term
    3. 将自身的超时时间设为当前时间加上一个随机值,随机值为 1s 内的随机毫秒数。
    4. 向其他节点发送 is-master-down-by-addr 命令,请求其他节点投票支持自己,命令会携带自己的 epoch
    5. Candidate 会投票给自己。在 Sentinel 中,投票的方式是把自己 master 结构体里的 leaderleader_epoch 改成投给的 Sentinel 和它的 epoch
  3. 其他 Sentinel 收到 Candidateis-master-down-by-addr 命令后,如果 Sentinel 当前 epochCandidate 传给他的 epoch 一样,说明他已经把自己 master 结构体里的 leaderleader_epoch 改成其他 Candidate,相当于把票投给了其他 Candidate。投票给其他 Sentinel 后,在当前 epoch 内,该 Sentinel 就只能成为 Follower
  4. Candidate 会不断的统计自己的票数,如果满足“当选投票条件”,则该 Candidate 当选 Leader
    1. 票数超过一半(监控主服务器的 Sentinel 的节点数的一半 + 1)
    2. 票数超过 Sentinel 配置的 quorum 参数——注:Raft 协议中没有这个限制,这是 Redis Sentinel 所独有的
  5. 如果在一个选举周期内(epoch),Candidate 没有满足“当选投票条件”(第 4 点描述的),则竞选失败。
  6. 如果在一个选举周期内(epoch),没有一个 Candidate 满足“当选投票条件”,说明所有 Candidate 都竞选失败,本轮选举作废。在等待超过 2 倍故障转移的超时时间后,开始新一轮的选举。
  7. 与 Raft 协议不同的是,Leader 并不会把自己成为 Leader 的消息发给其他 Sentinel。当 Leader 完成故障转移后,其他 Sentinel 检测到新的主服务器正常工作后,就会去掉“客观下线”的标识,从而不需要再发起选举。

故障转移

在选举产生出 Sentinel Leader 后,Sentinel Leader 将对已下线的主服务器执行故障转移操作。操作含以下三个步骤:

(1)选出新的主服务器

故障转移第一步,是 Sentinel Leader 在已下线主服务属下的所有从服务器中,挑选一个状态良好、数据完整的从服务器。然后,向这个从服务器发送 SLAVEOF no one 命令,将其转换为主服务器。

Sentinel Leader 如何选出新的主服务器:

  • 删除列表中所有处于下线或断线状态的从服务器。
  • 删除列表中所有最近五秒没有回复过 Sentinel Leader 的 INFO 命令的从服务器。
  • 删除所有与已下线主服务器连接断开超过 down-after-milliseconds * 10 毫秒的从服务器(down-after-milliseconds 指定了判断主服务器下线所需的时间)。
  • 之后, Sentinel Leader 先选出优先级最高的从服务器;如果优先级一样高,再选择复制偏移量最大的从服务器;如果结果还不唯一,则选出运行 ID 最小的从服务器。

(2)修改从服务器的复制目标

选出新的主服务器后,Sentinel Leader 会向所有从服务器发送 SLAVEOF 命令,让它们去复制新的主服务器。

(3)将旧的主服务器变为从服务器

Sentinel Leader 将旧的主服务器标记为从服务器。当旧的主服务器重新上线,Sentinel 会向它发送 SLAVEOF 命令,让其成为从服务器。

参考资料