前言
之前参加了字节青训营后端进阶班,我们选择的题目是搜索引擎,临近ddl,故有此文。
GoDance是一款用go语言编写的分布式搜索引擎,同时也是一款分布式文档数据库。支持分布式搜索以及分布式存储功能,对外提供restful Api接口来操作GoDance。
一、设计目标
分布式共识,保证元数据的一致性
节点间自动发现
保证集群的高可用,支持故障转移、服务降级
支持数据分布式存储
尽可能快的性能要求
二、设计方案
简述:集群模块整体采用了raft算法来保证分布式共识和集群高可用,借鉴了ES的思路来实现自动发现功能。
1.节点角色
MasterNode
可以被选举为Leader节点,集群中只有MasterNode才有选举权和被选举权,其他节点不参与选举
负责创建索引、删除索引、跟踪哪些节点是群集的一部分,并决定哪些分片分配给相关的节点、追踪集群中节点的状态等
DataNode
DataNode负责真实文档数据的存储
一个节点可以既是MasterNode,也可以是DataNode,但建议在配置时分开部署
2.节点状态
state状态
Leader(处于领导者状态)
Candidate(候选者,处于候选状态,正在进行选举)
Follower (跟随者状态)
Dead (节点宕机)
前三者是raft算法中的角色
term任期
节点每次选举都会把任期自增,一个节点在一个任期内只能投一票。任期也会影响到后面的选举规则。
任期的最大作用就是保证任何一个节点在一个任期内仅会投出一票,保证在“大多数”设置合理的情况下,一个任期最多只会有一个Leader被选出,从而避免脑裂现象
3.Leader选举机制
简述:raft算法通过“大多数"来保证集群中只有一个Leader存在,避免脑裂情况产生
3.1 设计方案
基本规则
Leader会间隔一段时间向Follower发送心跳RPC来告诉Follower自己还活着
Follower在收到心跳Rpc后会重新计时,之后一段时间不会发生选举
选举时机
当节点发现随机超时时间内没有收到心跳时,MasterNode会将自己term自增1,并开始竞选,投自己一票,并向其他MasterNode发送拉票请求
投票策略
只会投给任期比自己大的节点(意味着一个节点在一个任期term内只会投一票)
只会投给日志完整性(也就是最后一条日志项对应的任期编号值,索引号)和自己一样或者比自己高的节点
每次收到投票RPC时,无论是否投票,都会把自己的term更新到较大的值
选举成功条件
当一个候选者向其他节点发起拉票,收到“大多数”选票后晋升为新一任的Leader
其他规则
PS:以下一些规则是自己加的,并不完全是raft算法
当一个Leader收到比自己任期高的节点心跳时,由Leader退为Follower,避免脑裂
当其他候选者接受到来自任期大于等于自身的节点心跳时停止选举,变为Follwer
当收到其他节点的拉票请求时,重置超时时间,避免再次开始选举
选举失败后,应该检查自身是否收到了新的符合合规心跳(term>=自己),如果收到则退出选举状态为Follower;如果没有则等待随机超时时间再开始下一次选举
随机超时时间
跟随者等待领导者心跳信息超时的时间间隔是随机的
重新选举的等待时间是随机的
3.2 情境简述
为了方便理解上述的设计方案,这里举例几个常见的分布式场景
PS:以下示例默认所有节点都是Master节点(raft算法中没有这种概念,这个是为了后面自动发现而增加的)
情境一:正常情况
Leader节点定时向其他节点进行RPC调用(心跳)告诉各个从节点——“我还活着,不要造反!”
当Follower节点正常收到该心跳时,在接下来一段时间内不会进行选举操作,同时会返回当前的日志状态(最新提交和最新增加的日志Id)
情境二:Leader宕机
接下来一种情况就是Leader宕机
当Leader宕机时会,每个节点随机时间后开始选举,如果时间过近,可能会出现同时有多个节点参与竞选,由于同一任期内一个结点只能投一票的规则,此时会发生瓜分选票的情况,导致没有节点能够达到大多数选票。
为了避免这种情况,每个节点在选举失败后,会休息随机时间再尝试选举,同时term++,最终会有一个Leader节点成功竞选。
而这两个随机时间——跟随者等待领导者心跳信息超时的时间间隔和重新选举等待时间,都是为了避免这种冲突,加快Leader选举而设立的。
当然上述是一种比较复杂的情况,真实场景中可能第一次就能选举成功,不必等待。
情境三:网络时延情况
除了正常宕机外,还有一种情况会触发选举,那就是网络时延
因为网络时延而不能预期收到心跳的节点会开始选举,任期+1,当收到大多数选票时,成为新的Leader。
注意在这种情况下,可能会出现短暂的脑裂现象,但是并不会对集群状态产生影响,因为在这期间集群的状态修改,只有新Leader才可能达到大多数条件从而修改集群状态。
当然在这种情况下,还有一些比较复杂的情景,比如:
情境四:网络分区
这里又可以分为两种情况,Leader分在多数和Leader分在少数
要注意的是Leader可能并不是原来节点,这取决于分区恢复后日志完整性,以及当时的网络情况。具体情况可以参照情景三
可以看到,无论是否发生了网络分区,集群的状态都能达到一致。