在分布式系统应用中,高可用性和一致性经常面临问题,针对不同的应用场景,我们会选择不同的架构方式,比如 master-sl**e 和基于 ZooKeeper 的 master 选择。 随着时间的流逝,有一种方法可以基于筏算法自动选择主节点,它基于 paxos,并且做了一些简化和限制,例如增加了日志必须是连续的,并且只支持 leader、follower 和 candidate 三种状态,这使得算法相对容易理解和实现。
1)Dledger 是基于 OpenMessaging 发布的 Raft 实现的 J**A 类库,可以很容易地引用到系统中,满足其高可用、高可靠、强一致性的要求。
2)Raft将系统中的角色分为领导者、追随者和候选人
leader:接受客户端请求,定时发送心跳报文,将请求日志同步到跟随者,当日志同步到大多数节点时,告诉跟随者提交日志。 follower:接受并持久化 leader 同步的日志,待 leader 告知 leader 已准备好提交日志后再提交。 candidate:领导者选举过程中的临时角色,处于此状态的节点会发起投票并尝试选择自身作为主节点,选举成功后,处于此状态的节点将不存在
dledger 的实现大致可以分为两部分:
复制选举领导日志的整体架构,如下图所示
注:图为官网。
从上面的架构图中可以看出,有两个核心类:dledgerleaderelector 和 dledgerstore、election 和 file storage。 选择 leader 后,leader 接收到数据写入,并同步给其他 follower,这样就完成了整个 raft 写入过程。
从gitgub***idea引入后,我们发现整个**量很小,比较容易分析**。
选择 raft 的 master 的过程其实就是一个状态机的流程,当集群启动时每个节点的等待超时时间是随机的,当第一个节点的超时时间到达时,它会主动向其他节点发起投票,在收到超过一半的投票后, 会被提升为leader节点(投票过程是一个循环过程),同时发送心跳请求,其他候选节点在收到主节点的请求后,会自行变为follower节点。
term: term,每轮投票为一个 term,默认从 0 开始 仲裁机制:简单来说,就是不到一半,比如 3 个节点,2 个同意超时: 在选举时,每个节点的超时时间在一定范围内是随机的,可以保证整个状态机的驱动能够成功当选, 并且 DledgerLeaderElector 每 10 毫秒由线程重复执行一次maintainstate() 方法。 让我们关注他们状态的驱动因素:
转到核心方法 maintainascandidate()。
term : 投票轮次。 ledgerendtermleader:节点的当前投票轮次。 LedgerEndIndex:当前日志的最大序列,即下一个日志的开始 indexNextTimeToRequestVote:下一次投票的时间(随机) needincreasetermimmediately:是否立即投票,后面会解释 dledger 中每个节点的初始状态,所以第一轮刚刚初始化。 只有成员国nextterm()** 更改投票轮次。
进入核心方法handlevote(),主要用于根据自己的术语和请求者来判断是否为其他节点投票。
ledgerendindex因为在日志复制过程中每个节点的进度可能不同,所以在新一轮选举中,不能投票的被选举人的任期小于选举人的任期,被拒绝的候选人的任期大于选举人的任期,则选举人进行如下操作: 成为候选项(或保留候选项) 将 needincreaseimmediately 设置为 true。返回拒绝项未就绪,稍后会提到。 以下是附加说明:
选举者的下一个状态循环进入 maintainascandidate() 函数,然后由于 needincreaseimmediately 为 true,因此将更新项并重置计时器。 但是,投票不会立即发布(选举人的 currvotefor 仍然为空,因此可以对下一个投票的候选人投赞成票)。
在获得所有节点投票结果后,将对投票进行统计
在收到所有节点的票数后,进行仲裁,下图主要说明了这种情况。
acceptnum:agreesNotReadyTermnum:未准备好的金额(即结果是拒绝项未准备好) 没有时间重置 NextTimeToRequestVote,并立即发起另一次投票。 结合上述说明,这确保了被选中的人能够尽快获得这些未读节点的赞成票。
最后,经过多次投票后,当一个节点获得超过一半的票数时,它会更新其非 leader 角色并向其他节点发送心跳,其他节点在收到心跳信息后从候选者变为关注者。
dledger 作为 rocketmq ( version>=4.5.0)消息存储已经发布,基于dledger实现多节点缓存同步更新,基于日志复制副本容错处理,这里仅简单分析一下主要选择过程,在读取源码的过程中会涉及到很多j**a基础和netty的使用,比如aqs, completablefuture等,以帮助提高我们的编码能力。初始化时,dledger 将节点角色设置为候选者而不是追随者,这与原来的 raft 不同,节点角色转换过程也略有不同。 /wikihttps://www.usenix.org/system/files/conference/atc14/atc14-**ongaro.pdf
作者:京东物流郭青海.*:京东云开发者社区自猿说技术**请注明**。