Redis 值 Sentinel(哨兵)详述,图文并茂才能浅显易懂
1、简介
主从复制奠定了 Redis 分布式的基础,但是普通的主从复制并不能达到高可用的状态。在普通的主从复制模式下,如果主服务器宕机,就只能通过运维人员手动切换主服务器,很显然这种方案并不可取。针对上述情况,Redis 官方推出了可抵抗节点故障的高可用方案——Redis Sentinel(哨兵)。Redis Sentinel(哨兵):由一个或多个 Sentinel 实例组成的 Sentinel 系统,它可以监视任意多个主从服务器,当监视的主服务器宕机时,自动下线主服务器,并且择优选取从服务器升级为新的主服务器。
如下示例:当旧 Master 下线时长超过用户设定的下线时长上限,Sentinel 系统就会对旧 Master 执行故障转移操作,故障转移操作包含三个步骤:
在 Slave 中选择数据最新的作为新的 Master
向其他 Slave 发送新的复制指令,让其他从服务器成为新的 Master 的 Slave
继续监视旧 Master,如果其上线则将旧 Master 设置为新 Master 的 Slave
本文基于如下资源清单进行开展:
2、Sentinel 初始化与网络连接
Sentinel 并没有什么特别神奇的地方,它就是一个更加简单的 Redis 服务器,在 Sentinel 启动的时候它会加载不同的命令表和配置文件,因此从本质上来讲 Sentinel 就是一个拥有较少命令和部分特殊功能的 Redis 服务。当一个 Sentinel 启动时它需要经历如下步骤:
初始化 Sentinel 服务器
替换普通 Redis 代码为 Sentinel 的专用代码
初始化 Sentinel 状态
根据用户给定的 Sentinel 配置文件,初始化 Sentinel 监视的主服务器列表
创建连接主服务器的网络连接
根据主服务获取从服务器信息,创建连接从服务器的网络连接
根据发布/订阅获取 Sentinel 信息,创建 Sentinel 之间的网络连接
2.1 初始化 Sentinel 服务器
Sentinel 本质上就是一个 Redis 服务器,因此启动 Sentinel 需要启动一个 Redis 服务器,但是 Sentinel 并不需要读取 RDB/AOF 文件来还原数据状态。
2.2 替换普通 Redis 代码为 Sentinel 的专用代码
Sentinel 用于较少的 Redis 命令,大部分命令在 Sentinel 客户端都不支持,并且 Sentinel 拥有一些特殊的功能,这些需要 Sentinel 在启动时将 Redis 服务器使用的代码替换为 Sentinel 的专用代码。在此期间 Sentinel 会载入与普通 Redis 服务器不同的命令表。Sentinel 不支持 SET、DBSIZE 等命令;保留支持 PING、PSUBSCRIBE、SUBSCRIBE、UNSUBSCRIBE、INFO 等指令;这些指令在 Sentinel 工作中提供了保障。
2.3 初始化 Sentinel 状态
装载 Sentinel 的特有代码之后,Sentinel 会初始化 sentinelState 结构,该结构用于存储 Sentinel 相关的状态信息,其中最重要的就是 masters 字典。
2.4 初始化 Sentinel 监视的主服务器列表
Sentinel 监视的主服务器列表保存在 sentinelState 的 masters 字典中,当 sentinelState 创建之后,开始对 Sentinel 监视的主服务器列表进行初始化。
masters 的 key 是主服务的名字
masters 的 value 是一个指向 sentinelRedisInstance 指针
主服务器的名字由我们 sentinel.conf 配置文件指定,如下主服务器名字为 redis-master(我这里是一主二从的配置):
sentinelRedisInstance 实例保存了 Redis 服务器的信息(主服务器、从服务器、Sentinel 信息都保存在这个实例中)。
根据上面的一主二从配置将会得到如下实例结构:
2.5 创建连接主服务器的网络连接
当实例结构初始化完成之后,Sentinel 将会开始创建连接 Master 的网络连接,这一步 Sentinel 将成为 Master 的客户端。Sentinel 和 Master 之间会创建一个命令连接和一个订阅连接:
命令连接用于获取主从信息
订阅连接用于 Sentinel 之间进行信息广播,每个 Sentinel 和自己监视的主从服务器之间会订阅_sentinel_:hello 频道(注意 Sentinel 之间不会创建订阅连接,它们通过订阅_sentinel_:hello 频道来获取其他 Sentinel 的初始信息)
Sentinel 在创建命令连接完成之后,每隔 10 秒钟向 Master 发送一次 INFO 指令,通过 Master 的回复信息可以获得两方面的知识:
Master 本身的信息
Master 下的 Slave 信息
2.6 创建连接从服务器的网络连接
根据主服务获取从服务器信息,Sentinel 可以创建到 Slave 的网络连接,Sentinel 和 Slave 之间也会创建命令连接和订阅连接。
当 Sentinel 和 Slave 之间创建网络连接之后,Sentinel 成为了 Slave 的客户端,Sentinel 也会每隔 10 秒钟通过 INFO 指令请求 Slave 获取服务器信息。到这一步 Sentinel 获取到了 Master 和 Slave 的相关服务器数据。这其中比较重要的信息如下:
服务器 ip 和 port
服务器运行 id run id
服务器角色 role
服务器连接状态 mater_link_status
Slave 复制偏移量 slave_repl_offset(故障转移中选举新的 Master 需要使用)
Slave 优先级 slave_priority
此时实例结构信息如下所示:
2.7 创建 Sentinel 之间的网络连接
此时是不是还有疑问,Sentinel 之间是怎么互相发现对方并且相互通信的,这个就和上面 Sentinel 与自己监视的主从之间订阅_sentinel_:hello 频道有关了。Sentinel 会与自己监视的所有 Master 和 Slave 之间订阅_sentinel_:hello 频道,并且 Sentinel 每隔 2 秒钟向_sentinel_:hello 频道发送一条消息,消息内容如下:
PUBLISH sentinel:hello "<s_ip>,<s_port>,<s_runid>,<s_epoch>,<m_ip>,<m_port>,<m_runid>,<m_epoch>"
其中 s 代码 Sentinel,m 代表 Master;ip 表示 IP 地址,port 表示端口、runid 表示运行 id、epoch 表示配置纪元。
多个 Sentinel 在配置文件中会配置相同的主服务器 ip 和端口信息,因此多个 Sentinel 均会订阅_sentinel_:hello 频道,通过频道接收到的信息就可获取到其他 Sentinel 的 ip 和 port,其中有如下两点需要注意:
如果获取到的 runid 与 Sentinel 自己的 runid 相同,说明消息是自己发布的,直接丢弃
如果不相同,则说明接收到的消息是其他 Sentinel 发布的,此时需要根据 ip 和 port 去更新或新增 Sentinel 实例数据
Sentinel 之间不会创建订阅连接,它们只会创建命令连接:
此时实例结构信息如下所示:
3、Sentinel 工作
Sentinel 最主要的工作就是监视 Redis 服务器,当 Master 实例超出预设的时限后切换新的 Master 实例。这其中有很多细节工作,大致分为检测 Master 是否主观下线、检测 Master 是否客观下线、选举领头 Sentinel、故障转移四个步骤。
3.1 检测 Master 是否主观下线
Sentinel 每隔 1 秒钟,向 sentinelRedisInstance 实例中的所有 Master、Slave、Sentinel 发送 PING 命令,通过其他服务器的回复来判断其是否仍然在线。
在 Sentinel 的配置文件中,当 Sentinel PING 的实例在连续 down-after-milliseconds 配置的时间内返回无效命令,则当前 Sentinel 认为其主观下线。Sentinel 的配置文件中配置的 down-after-milliseconds 将会对其 sentinelRedisInstance 实例中的所有 Master、Slave、Sentinel 都适应。
无效指令指的是+PONG、-LOADING、-MASTERDOWN 之外的其他指令,包括无响应
如果当前 Sentinel 检测到 Master 处于主观下线状态,那么它将会修改其 sentinelRedisInstance 的 flags 为 SRI_S_DOWN
3.2 检测 Master 是否客观下线
当前 Sentinel 认为其下线只能处于主观下线状态,要想判断当前 Master 是否客观下线,还需要询问其他 Sentinel,并且所有认为 Master 主观下线或者客观下线的总和需要达到 quorum 配置的值,当前 Sentinel 才会将 Master 标志为客观下线。
当前 Sentinel 向 sentinelRedisInstance 实例中的其他 Sentinel 发送如下命令:
ip:被判断为主观下线的 Master 的 IP 地址
port:被判断为主观下线的 Master 的端口
current_epoch:当前 sentinel 的配置纪元
runid:当前 sentinel 的运行 id,runid
current_epoch 和 runid 均用于 Sentinel 的选举,Master 下线之后,需要选举一个领头 Sentinel 来选举一个新的 Master,current_epoch 和 runid 在其中发挥着重要作用,这个后续讲解。
接收到命令的 Sentinel,会根据命令中的参数检查主服务器是否下线,检查完成后会返回如下三个参数:
down_state:检查结果 1 代表已下线、0 代表未下线
leader_runid:返回*代表判断是否下线,返回 runid 代表选举领头 Sentinel
leader_epoch:当 leader_runid 返回 runid 时,配置纪元会有值,否则一直返回 0
当 Sentinel 检测到 Master 处于主观下线时,询问其他 Sentinel 时会发送 current_epoch 和 runid,此时 current_epoch=0,runid=*
接收到命令的 Sentinel 返回其判断 Master 是否下线时 down_state = 1/0,leader_runid = *,leader_epoch=0
3.3 选举领头 Sentinel
down_state 返回 1,证明接收 is-master-down-by-addr 命令的 Sentinel 认为该 Master 也主观下线了,如果 down_state 返回 1 的数量(包括本身)大于等于 quorum(配置文件中配置的值),那么 Master 正式被当前 Sentinel 标记为客观下线。此时,Sentinel 会再次发送如下指令:
此时的 runid 将不再是 0,而是 Sentinel 自己的运行 id(runid)的值,表示当前 Sentinel 希望接收到 is-master-down-by-addr 命令的其他 Sentinel 将其设置为领头 Sentinel。这个设置是先到先得的,Sentinel 先接收到谁的设置请求,就将谁设置为领头 Sentinel。发送命令的 Sentinel 会根据其他 Sentinel 回复的结果来判断自己是否被该 Sentinel 设置为领头 Sentinel,如果 Sentinel 被其他 Sentinel 设置为领头 Sentinel 的数量超过半数 Sentinel(这个数量在 sentinelRedisInstance 的 sentinel 字典中可以获取),那么 Sentinel 会认为自己已经成为领头 Sentinel,并开始后续故障转移工作(由于需要半数,且每个 Sentinel 只会设置一个领头 Sentinel,那么只会出现一个领头 Sentinel,如果没有一个达到领头 Sentinel 的要求,Sentinel 将会重新选举直到领头 Sentinel 产生为止)。
3.4 故障转移
故障转移将会交给领头 sentinel 全权负责,领头 sentinel 需要做如下事情:
从原先 master 的 slave 中,选择最佳的 slave 作为新的 master
让其他 slave 成为新的 master 的 slave
继续监听旧 master,如果其上线,则将其设置为新的 master 的 slave
这其中最难的一步是如果选择最佳的新 Master,领头 Sentinel 会做如下清洗和排序工作:
判断 slave 是否有下线的,如果有从 slave 列表中移除
删除 5 秒内未响应 sentinel 的 INFO 命令的 slave
删除与下线主服务器断线时间超过 down_after_milliseconds * 10 的所有从服务器
根据 slave 优先级 slave_priority,选择优先级最高的 slave 作为新 master
如果优先级相同,根据 slave 复制偏移量 slave_repl_offset,选择偏移量最大的 slave 作为新 master
如果偏移量相同,根据 slave 服务器运行 id run id 排序,选择 run id 最小的 slave 作为新 master
新的 Master 产生后,领头 sentinel 会向已下线主服务器的其他从服务器(不包括新 Master)发送 SLAVEOF ip port 命令,使其成为新 master 的 slave。
到这里 Sentinel 的的工作流程就算是结束了,如果新 master 下线,则循环流程即可!
版权声明: 本文为 InfoQ 作者【李子捌】的原创文章。
原文链接:【http://xie.infoq.cn/article/398af5133422888258d4d0d65】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论