共计 3346 个字符,预计需要花费 9 分钟才能阅读完成。
更多内容,欢迎关注微信公众号:全菜工程师小辉。公众号回复关键词,领取免费学习资料。
当主服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费事费力,还会造成一段时间内服务不可用。所以更多时候,我们优先考虑哨兵(sentinel) 模式。
Redis sentinel 是 Redis 高可用实现方案:故障发现、故障自动转移、配置中心、客户端通知。从 Redis 的 2.6 版本开始提供的,但是当时这个版本的模式是不稳定的,直到 Redis 的 2.8 版本以后,这个哨兵模式才稳定下来,在生产环境中,如果想要使用 Redis 的哨兵模式,也会尽量使用 Redis 的 2.8 版本之后的版本。
哨兵虽然有一个单独的可执行文件 Redis-sentinel,但实际上它只是一个运行在特殊模式下的 Redis 服务器,你可以在启动一个普通 Redis 服务器时通过给定 --sentinel
选项来启动哨兵,哨兵的一些设计思路和 zookeeper 非常类似。
sentinel 的定时任务
sentinel 机制中有三种重要的定时任务。
- 每 10 秒每个 sentinel 对 master 和 slave 执行 info
作用:
- 发现 slave 节点。
- 确认主从关系。
- 每 2 秒每个 sentinel 通过 master 节点的 channel 交换信息(pub/sub)
作用:
- 互相通信掌握节点的信息和自身信息,可以感知新加入的 sentinel
通过 master 节点的__sentinel__:hello 频道进行交互,所有 sentinel 订阅这个频道并每 2 秒向该频道发布信息
- 每 1 秒每个 sentinel 对其他 sentinel 和 master,slave 进行 ping
作用:
- 心跳检测
主观下线和客观下线
主观下线
主观下线:单个 sentinel 节点对 Redis 节点通信失败的“偏见”。
这是一种主观下线。因为在复杂的网络环境下,这个 sentinel 与这个 master 不通,但是如果 master 与其他的 sentinel 都是通的呢?所以是一种“偏见”。
这是依靠的第三种定时:每秒去 ping 一下周围的 sentinel 和 Redis。对于 slave Redis, 可以使用这个主观下线,因为他不需要进行故障转移;但是对于 master Redis,必须使用客观下线。
客观下线
客观下线:所有 sentinel 节点对 master Redis 节点失败“达成共识”(超过 quorum 个则统一,quorum 可配置)。
这是依靠的第二种定时:每两秒,sentinel 之间进行“商量”(一个 sentinel 可以通过向另一个 sentinel 发送 SENTINEL is-master-down-by-addr 命令来询问对方是否认为给定的服务器已下线。)
对于 master redis 的下线,必须要达成共识才可以,因为涉及故障转移,仅仅依靠一个 sentinel 判断是不够的
领导者选举
当 sentinel 集群需要故障转移的时候会在集群中选出 Leader 执行故障转移操作。sentinel 采用了 Raft 协议实现了 sentinel 间选举 Leader 的算法,不过也不完全跟论文描述的步骤一致。sentinel 集群运行过程中故障转移完成,所有 sentinel 又会恢复平等。Leader 仅仅是故障转移操作出现的角色。
选举流程
- 某个 sentinel 认定 master 客观下线的节点后,该 sentinel 会先看看自己有没有投过票,如果自己已经投过票给其他 sentinel 了,在 2 倍故障转移的超时时间自己就不会成为 Leader。相当于它是一个 Follower。
- 如果该 sentinel 还没投过票,那么它就成为 Candidate。
- 和 Raft 协议描述的一样,成为 Candidate,sentinel 需要完成几件事情
3.1 更新故障转移状态为 start
3.2 当前 epoch 加 1,相当于进入一个新 term,在 sentinel 中 epoch 就是 Raft 协议中的 term。
3.3 更新自己的超时时间为当前时间随机加上一段时间,随机时间为 1s 内的随机毫秒数。
3.4 向其他节点发送 is-master-down-by-addr 命令请求投票。命令会带上自己的 epoch。
3.5 给自己投一票,在 sentinel 中,投票的方式是把自己 master 结构体里的 leader 和 leader_epoch 改成投给的 sentinel 和它的 epoch。
- 其他 sentinel 会收到 Candidate 的 is-master-down-by-addr 命令。如果 sentinel 当前 epoch 和 Candidate 传给他的 epoch 一样,说明他已经把自己 master 结构体里的 leader 和 leader_epoch 改成其他 Candidate,相当于把票投给了其他 Candidate。投过票给别的 sentinel 后,在当前 epoch 内自己就只能成为 Follower。
- Candidate 会不断的统计自己的票数,直到他发现认同他成为 Leader 的票数超过一半而且超过它配置的 quorum(quorum 可以参考《redis sentinel 设计与实现》)。sentinel 比 Raft 协议增加了 quorum,这样一个 sentinel 能否当选 Leader 还取决于它配置的 quorum。
- 如果在一个选举时间内,Candidate 没有获得超过一半且超过它配置的 quorum 的票数,自己的这次选举就失败了。
- 如果在一个 epoch 内,没有一个 Candidate 获得更多的票数。那么等待超过 2 倍故障转移的超时时间后,Candidate 增加 epoch 重新投票。
- 如果某个 Candidate 获得超过一半且超过它配置的 quorum 的票数,那么它就成为了 Leader。
- 与 Raft 协议不同,Leader 并不会把自己成为 Leader 的消息发给其他 sentinel。其他 sentinel 等待 Leader 从 slave 选出 master 后,检测到新的 master 正常工作后,就会去掉客观下线的标识,从而不需要进入故障转移流程。
故障转移过程
- 当多个 sentinel 发现并确认了 master 有问题
- 接着会选举出一个 sentinel 作为领导
- 再选举出一个 slave 作为 master
- 通知其余的 slave,新的 master 是谁
- 通知客户端一个主从的变化
- 最后,sentinel 会等待旧的 master 复活,然后将新 master 成为 slave
那么,如何选择“合适”的 slave 节点呢?
- 选择 slave-priority(slave 节点优先级,人为配置)最高的 slave 节点,如果存在则返回,不存在则继续。
- 其次会选择复制偏移量最大的 slave 节点(复制得最完整),如果存在则返回,不存在则继续
- 最后会选择 run_id 最小的 slave 节点(启动最早的节点)
客户端实现高可用的基本原理
故障转移后客户端无法感知将无法保证正常的使用。所以,实现客户端高可用的步骤如下:
- 客户端获取 sentinel 节点集合
- 客户端通过 sentinel get-master-addr-by-name master-name 这个 api 来获取对应主节点信息
- 客户端验证当前获取的“主节点”是真正的主节点,这样的目的是为了防止故障转移期间主节点的变化
- 客户端保持和 sentinel 节点集合的联系,即订阅 sentinel 节点相关频道,时刻获取关于主节点的相关信息
从上面的模型可以看出,Redis sentinel 客户端只有在初始化和切换主节点时需要和 sentinel 进行通信来获取主节点信息,所以在设计客户端时需要将 sentinel 节点集合考虑成配置(相关节点信息和变化)发现服务。
需要说明的问题
- 尽可能在不同物理机上和同一个网络部署 Redis sentinel 的所有节点
- Redis sentinel 中的 sentinel 节点个数应该大于等于 3 且最好是奇数。(节点数多可以保证高可用)
- Redis sentinel 中的数据节点和普通数据节点没有区别。每个 sentinel 节点在本质上还是一个 Redis 实例,只不过和 Redis 数据节点不同的是,其主要作用是监控 Redis 数据节点
- 客户端初始化时连接的是 sentinel 节点集合,不再是具体的 Redis 节点,但 sentinel 只是配置中心不是代理。
更多内容,欢迎关注微信公众号:全菜工程师小辉。公众号回复关键词,领取免费学习资料。