Redis设计与实现 第 16 章 Sentinel

第 16 章 Sentinel

Sentinel 哨兵组成了一个 Redis 高可用性的方案:

​ 由一个或多个 Sentinel 实例组成的 Sentinel 系统可以监视任意多个主服务器及其所有从服务器,并在主服务器下线时将其从服务器升级为新的主服务器,继续执行要求,当先前的主服务器再次上线时则成为新的从服务器

1

2

16.1 启动并初始化 Sentinel

redis-sentinel /path/to/yourt/setinel.conf
# or
redis-server /path/to/yourt/setinel.conf -- sentinel

启动时执行以下:

  1. 初始化服务器
  2. 将普通 Redis 服务器使用的代码替换 Sentinel 专用代码
  3. 初始化 Sentinel 代码
  4. 根据给定的配置文件,初始化 Sentinel 的监视主服务器列表
  5. 创建连向主服务器的网络连接

16.1.1 初始化服务器

Sentinel 本质是一个运行在特殊模式下的 Redis 服务器,所以启动第一步也是初始化一个 Redis 服务器,过程与第 14 章的相似,但不完全相同

Sentinel模式下的Redis服务器

16.1.2 使用 Sentinel 专用代码

例如使用不同端口和不同的命令列表

Sentinel专用代码

16.1.3 初始化 Sentinel 状态

服务器会初始化一个 sentinel.c/sentinelState 结构,保存了服务器中 Sentinel 功能有关的状态,一般状态是

SentinelState

16.1.4 初始化 Sentinel 状态的 master 属性

  • 字典的键:被监视的主服务器的名字
  • 字典的值:主服务器对应的 sentinel.c/sentinelRedisInstance 结构
    • 代表一个被 Sentinel 监视的 Redis 服务器实例
    • 可以是主服务器、从服务器、或者另外的 Sentinel

sentinelRedisInstance

sentinelInstance.addr 属性指向 sentinel.c/sentinelAddr 结构的指针,sentinelAddr 保存实例的 IP 地址和端口号

sentinelAddr

对 Sentinel 状态的初始化将引发对 masters 字典的初始化,而 master 字典的初始化根据被载入的 Senti 配置文件来进行的

16.1.5 创建连向主服务器的网络连接

最后一步即是创建网络连接,Sentinel 将成为主服务器的客户端,可以向主服务器发送命令,并从命令回复中获取相关的信息

会创建两个连向主服务器的异步网络连接

  • 命令连接:专用发送命令接收回复

  • 订阅连接:专门订阅主服务器的 _sentinel_:hello 频道

Redis 发布与订阅功能中,被发送的信息不会保存在 Redis 服务器中,如果信息发送时想要接收信息的客户端不在线,则会丢失信息,所以 Sentinel 有专门的连接接收信息

因为 Sentinel 与多个实例创建连接,所以使用的是异步连接

Sentinel与主服务器的网络连接

16.2 获取主服务器信息

Sentinel 会默认每 10 秒一次频率向监视的主服务器通过命令连接发送 INFO 命令,通过分析回复获取当前主服务器的信息

  • 主服务器本身的信息:
    • run_id:服务器运行 ID
    • role域:记录的服务器角色
  • 主服务器从属的所有从服务器信息
    • 每一个从服务器都以一个 "slave" 字符串开头的行记录
    • ip:记录了从服务器的 IP 地址
    • port:从服务器的端口号

根据 run_id 和 role 域,Sentinel 将对主服务器的实例结构进行更新,例如重启后的主服务器 run_id 将不同,Sentinel 检测后将会对实例结构的 run_id 进行更新

主服务器返回的从服务器信息被用于更新主服务器实例的 slave 字典

  • 主服务器实例结构的 flags 属性的值是 SRI_MASTER,从服务器实例结构的 flags 属性的值是 SRT_SLAVE

  • 主服务器的 name 属性是 Sentinel 配置文件设置的,从服务器实例结构的 name 属性的值是 Sentinel 根据从服务器的 IP 地址、端口号设置的

16.3 获取从服务器信息

如果监视的主服务器有新的从服务器出现时,Sentinel 会为这个新的从服务器创建相应的实例结构外,还会创建连接到从服务器的命令连接和订阅连接

创建命令连接后,Sentinel 会以每十秒一次的频率通过命令连接向从服务器发送 INFO 命令

Sentinel与各个从服务器的命令连接和订阅连接

根据 INFO 命令的回复,Sentinel 会提取以下信息:

  • 从服务器的 run_id
  • 从服务器的角色 role
  • 主服务器的 IP 地址 master_host,主服务器的端口号 master_port
  • 主从服务器的连接状态 master_link_status
  • 从服务器的优先级 slave_priority
  • 从服务器的复制偏移量 slave_repl_offset

再对从服务器的实例结构进行更新

16.4 向主服务器和从服务器发送信息

默认情况下:Sentinel 会以每两秒一次的频率,通过命令连接向所有被监视的主服务器和从服务器发送以下格式的命令:

发送信息

Sentinel有关参数

根据监视的是主服务器还是从服务器判断

服务器有关参数

16.5 接收来自主服务器和从服务器的频道信息

建立订阅连接后,Sentinel 则会向服务器发送以下命令:

SUBCRIBE _sentinel_:hello

此订阅会持续到与服务器的连接断开,Sentinel 通过此连接接收和发送消息

对于监视同一个服务器的多个 Sentinel,一个 Sentinel 发送的消息也会被其他 Sentinel 接收到,然后被用于更新对发送消息的 Sentinel 的认知,也会更新其监控的服务器的认知

16.5.2 更新 sentinels 字典

Sentinel 的 sentinels 字典 同样监视这个主服务器的其他 Sentinel:

  • 键:其他 Sentinel 的名字,格式为 ip:port
  • 值:对应的 Sentinel 的实例结构

当一个 Sentinel 接收到其他 Sentinel 发来的信息时,目标 Sentinel 会提取出以下参数:

  • 与 Sentinel 有关的参数:源 Sentinel 的 IP、端口、run_id、配置纪元
  • 与主服务器有关的参数:源 Sentinel 正在监视的主服务器的名字、IP、端口、配置纪元

根据主服务器参数,目标 Sentinel 会在自己的 Sentinel 状态的 masters 字典查找对应的主服务器实例的结构,根据参数,检查主服务器实例结构的 Sentinels 字典中 Sentinel 实例结构是否存在:

  • 存在则更新
  • 不存在则源 Sentinel 刚开始监视主服务器,目标 Sentinel 创建新的实例结构,并添加到 sentinels 字典中

16.5.2 创建连向其他 Sentinel 的命令连接

当 Sentinel 通过频道信息发现新的 Sentinel 后不仅创建对应的实例结构,也会创建连向此的命令连接,最终监视同一主服务器的多个 Sentinel 将形成相互连接的网络

各个Sentinel间的网络连接

16.6 检测主观下线状态

默认情况下,Sentinel 会默认每秒一次的频率向所有与它创建了命令连接的实例(主服务器、从服务器、其他 Sentinel ) 发送 PING 命令,通过回复判断实例是否在线

实例对 PING 命令的回复有两种:

  • 有效回复:+PONG、-LOADING、-MASTERDOWN 三种
  • 无效回复:返回有效回复外的其他回复,或在指定时间内没有任何回复

Sentinel 配置文件中的 down-after-millisecond 的值为 50000 毫秒,当主服务器 master 连续 50000 毫秒内都向 Sentinel 返回无效回复,则会被标记为主观下线,对应的实例结构的 flags 被标识为 SRI_S_DOWN

主服务器被标记为主观下线

down-after-millisecond 的值不仅会被 Sentinel 用来判断主服务器的主观下线状态,还会被用于判断主服务器属下的从服务器,以及所有同样监视这个主服务器的其他 Sentinel 的主观下线状态

且监视同一个主服务器的不同 Sentinel 来说,此值可能不同

16.7 检查客观下线状态

当一个主服务器被认为主观下线后,Sentinel 会询问其他监视此主服务器的 Sentinel,当接收到足够多的下线判断(主观或者客观下线)后,才会将主服务器判断为客观下线,并对主服务器执行故障转移操作

16.7.1 发送 SENTINEL is-master-down-by-addr 命令

Sentinel 使用

SENTINEL IS-master-down-by-addr <current_epoch> <run_id> 来询问其他 Sentinel 是否同意主服务器已下线

SENTINEL IS-master-down-by-addr参数

16.7.2 接收 SENTIENL is-master-down-by-addr 命令

目标 Sentinel 接收到源 Sentinel 发来的命令后,目标会分析提取参数,根据主服务器 IP 和端口检查主服务器是否已下线,然后向源返回一个包含三个参数的 Multi Bulk 作为回复

  1. <down_state>
  2. <leader_runid>
  3. <leader_epoch>

Multi Bulk参数

16.7.3 接收 SENTIENL is-master-down-by-addr 命令的回复

根据其他 Sentinel 发回的命令回复,统计同意已下线的数量,达到配置的数量则判断为客观下线,flags 标志为 SRI_O_DOWN

主服务器被标记为客观下线

Sentinel 配置的 quorum 参数判断的标志,大于等于则被认为是客观下线

不同的 Sentinel 此参数的配置不同,即对主服务器的客观下线判断不同

16.8 选举领头 Sentinel

领头 Sentinel 对下线主服务器进行故障转移操作

选举 Sentinel :

  • 每个监视同一主服务器的 Sentinel 都有资格成为领头
  • 每次选举后不论成功失败,每个 Sentinel 的配置纪元增 1
  • 一个配置纪元里,所有 Sentinel 都有一次机会将某个 Sentinel 设置为局部领头,且设置后在此纪元不会更改
  • 每个发现主服务器进入客观下线状态的 Sentinel 都会要求其他将自己设置为局部领头
  • 源 Sentinel 向目标 Sentinel 发送 SENTIENL is-master-down-by-addr 命令且 runid 参数不是 * 而是自己的 runid 时表示要目标设置源为它的局部领头 Sentinel
  • 设置局部领头的规则是先到先得,后来的都被拒绝
  • 目标接收后将向源回复,其中的 leader_runid 和 leader_epoch 记录目标的局部领头的 runid 和配置纪元
  • 源接收命令的回复后取出参数,如果和自己相同则表示自己成为目标的局部领头
  • 如果某个 Sentinel 被半数以上设置为局部领头,则他成为领头 Sentinel
  • 因为需要半数以上支持且只能设置一次局部领头,则领头 Sentinel 只有一个
  • 在指定时间内没有选出领头 Sentinel,则在一段时间后再进行选择,直到选出

16.9 故障转移

三个步骤:

  1. 在已下线的主服务器的从服务器中选出一个从服务器,成为新的主服务器
  2. 让其他从服务器改为复制新的主服务器
  3. 将已下线的主服务器设置为新的主服务器的从服务器,当重新上线时自动成为主服务器的从服务器

16.9.1 选出新的主服务器

挑选一个状态良好、数据完整的从服务器,发送 SLAVEOF no one 成为主服务器

挑选

领头 Sentinel 将所有属于的从服务器保存在列表中,然后按照以下规则过滤

  1. 删除下线或断线服务器
  2. 删除最近 5 秒没有回复领头 Sentinel 的 INFO 命令的服务器
  3. 删除与已下线主服务器连接断开超过 down-after-milliseconds * 10 毫秒的服务器
  4. 根据服务器的优先级排序,选出
  5. 如果优先级一致,选出复制偏移量最大的
  6. 再一致选出运行 ID 最小的

16.9.2 修改从服务器的复制目标

让其他从服务器复制新的主服务器,领头 Sentinel 发送 SLAVEOF 命令

16.9.3 将旧主服务器变为从服务器

领头 Sentinel 发送命令

posted @ 2021-07-13 19:59  zephxu  阅读(78)  评论(0)    收藏  举报