基于Raft算法的DLedger-Library分析 | 京东物流技术团队

京东云开发者
• 阅读 213

1 背景

在分布式系统应用中,高可用、一致性是经常面临的问题,针对不同的应用场景,我们会选择不同的架构方式,比如master-slave、基于ZooKeeper选主。随着时间的推移,出现了基于Raft算法自动选主的方式,Raft是在Paxos的基础上,做了一些简化和限制,比如增加了日志必须是连续的,只支持领导者、跟随者和候选人三种状态,在理解和算法实现上都相对容易许多。

1)DLedger 是openMessaging发布的一个基于 Raft 实现的JAVA类库,可以方便引用到系统中,满足其高可用、高可靠、强一致的需求,其中在RocketMQ中作为消息Broker存储高可用实现的一种解决方案。

2)Raft将系统中的角色分为领导者(Leader)、跟从者(Follower)和候选人(Candidate):

  • Leader:接受客户端请求,定时发送心跳包,并向Follower同步请求日志,当日志同步到大多数节点上后告诉Follower提交日志。
  • Follower:接受并持久化Leader同步的日志,在Leader告之日志可以提交之后,提交日志。
  • Candidate:Leader选举过程中的临时角色,该状态下的节点会发起投票,尝试选择自己为主节点,选举成功后,不会存在该状态下的节点

基于Raft算法的DLedger-Library分析 | 京东物流技术团队

2 DLedger架构设计

DLedger 的实现大体可以分为以下两个部分:

  • 选举 Leader
  • 日志复制
  • 其整体架构如下图

基于Raft算法的DLedger-Library分析 | 京东物流技术团队 注:图引用官网

从上面的架构图中,有两个核心类:DLedgerLeaderElector 和 DLedgerStore,选举和文件存储。选出 leader 后,再由 leader 去接收数据的写入,同时同步到其他的 follower,这样就完成了整个 Raft 的写入过程

3 DLedger选主源码分析

3.1 下载源码

从gitGub下载代码(https://github.com/openmessaging/dledger ),idea引入后,我们发现整个代码量很小,在分析代码时比较容易.

基于Raft算法的DLedger-Library分析 | 京东物流技术团队

3.2 选主流程分析

3.2.1 原理

raft的选主过程实际是一个状态机的流转,在集群启动时每个节点的等待超时时间时随机的,在第一个节点超时时间到来,则主动向其他节点发起投票,在收到半数以上的投票后晋升为leader(投票过程是个循环的过程),同时发送心跳请求,其他候选节点收到主节点的请求后,改变自己为follower节点。

  • term: 任期,每一轮投票都是一个任期,默认从0开始
  • Quorum机制:简单说就是少说半数以上,比如3个节点,2个同意即可
  • 超时时间: 在选举时,每个节点的超时时间在一定范围内是随机的,这样可以保证能够顺利选举

3.2.2 代码分析

整个状态机的驱动,由线程每个10ms反复执行DLedgerLeaderElector.maintainState()方法。下面重点来分析其状态的驱动:

进入到核心方法maintainAsCandidate() :

1.step1 初始化

  • term : 投票轮次。
  • ledgerEndTermLeader: 节点当前的投票轮次。
  • ledgerEndIndex: 当前日志的最大序列,即下一条日志的开始 index
  • nextTimeToRequestVote: 下次发起投票的时间(随机的)
  • needIncreaseTermImmediately:是否立即投票,在后面中会说明

在DLedger中每个节点的初始状态WAIT_TO_REVOTE,所以第一轮只是做了初始化。其中只有 memberState.nextTerm()这个代码会更改投票轮次

基于Raft算法的DLedger-Library分析 | 京东物流技术团队

2.step2 投票

进入到核心方法handleVote(),这个方法主要是判断其他节点请求来后,根据自己的term和请求者的等判断是否投赞成票

基于Raft算法的DLedger-Library分析 | 京东物流技术团队

  • ledgerEndIndex因为在日志复制过程中,每个节点的进度有可能是不一样的,所以在新的一轮选举时,这时不能投赞成票的
  • 被选举者 term 小于 选举者的term,返回拒绝
  • 被选举者 term 大于 选举者的term,则选举者进行下面操作:
    • 变成candidate(或者保持candidate)
    • 把needIncreaseImmediately设置为true。
    • 返回 REJECT_TERM_NOT_READY,这个在后面提到。

这里补充说明:

选举者 的下一次状态循环会进入到maintainAsCandidate()函数,然后因为needIncreaseImmediately为true,所以把term更新,同时重置计时器。 但是并没有立刻发出投票(此时选举者 的CurrVoteFor还是null,使得接下来给之前的voting candidate 投赞成票可能)

基于Raft算法的DLedger-Library分析 | 京东物流技术团队

获取所有node投票结果后开始计算票数:

基于Raft算法的DLedger-Library分析 | 京东物流技术团队

3.step3 仲裁

在收到所有节点的投票结果计数后,进行仲裁,这里主要说明下图中这个条件

基于Raft算法的DLedger-Library分析 | 京东物流技术团队

  • acceptNum:同意的数量
  • notReadyTermNum:未准备好的数量(即结果为REJECT_TERM_NOT_READY)

这里没有重置nextTimeToRequestVote的时间,即刻再发起一次投票。结合上面的说明,这样保证了被选者能尽快去拿到这些notRead的节点的赞成票。

基于Raft算法的DLedger-Library分析 | 京东物流技术团队

最终经过多次投票后,当一个node节点获取到半数以上投票后,更新自己未leader角色,同时向其他node节点发送heartBeat,其他节点在收到心跳信息后,将自己从candidate 变为follower。

3.3 单元测试验证

3.3.1 编写单元测试

基于Raft算法的DLedger-Library分析 | 京东物流技术团队

3.3.2 日志分析

基于Raft算法的DLedger-Library分析 | 京东物流技术团队

3.4 应用场景

  1. DLedger 作为 RocketMQ ( version>=4.5.0)的消息存储已经发布
  2. 基于DLedger 实现多节点的缓存同步更新
  3. 基于日志复制的副本容错处理

4 总结

  1. 这里只简单分析了选主过程,在阅读源码过程中会涉及很多java的基础及netty的使用,比如AQS、CompletableFuture等,有助于提高我们的编码能力。
  2. DLedger在初始化时是将节点角色设置为candidate而不是follower 这个和原Raft是不同的地方,在节点角色转换过程中也稍有差别。

参考文献

作者:京东物流 郭庆海

来源:京东云开发者社区 自猿其说Tech 转载请注明来源

点赞
收藏
评论区
推荐文章
架构师日记-为什么数据一致性那么难
在现代大型分布式软件系统中,有一个绕不过去的课题,那就是如何保证系统的数据一致性。著名的Paxos算法(Megastore、Spanner),Raft协议(ETCD、TiKV、Consul),ZAB协议(ZooKeeper)等分布式一致性解决方案,都是在此背景下而诞生的。
拜占庭将军问题和 Raft 共识算法讲解
在分布式系统中,什么是拜占庭将军问题?产生的场景和解决方案是什么?什么是Raft共识算法?Raft算法是如何解决拜占庭将军问题的?其核心原理和算法逻辑是什么?除了Raft,还有哪些共识算法?共识问题作为分布式系统的一大难点和痛点,本文主要介绍了其产生的背景、原因,以及通用的Raft算法解决方案。
Wesley13 Wesley13
2年前
BFT等5种主流区块链共识的开源实现
共识算法是实现自主产权区块链的必不可少的关键环节,本文列出社区中相对成熟的区块链共识算法开源实现,包括BFT共识、Raft共识、Paxos共识、PoW共识等,可供希望开发自主产权区块链的团队参考学习。相关推荐:区块链开发系列教程(https://www.oschina.net/action/GoToLink?urlhttp%3A%2F%2Fww
Stella981 Stella981
2年前
Raft 算法在分布式存储系统 Curve 中的实践
作为网易数帆开源的高性能、高可用、高可靠的新一代分布式存储系统,Curve对于多副本数据同步、负载均衡、容灾恢复方面都有较高的要求。网易数帆存储团队选用Raft算法作为Curve底层一致性协议,并基于Raft的特性,实现了异常情况下的数据迁移和自动恢复。本文首先简要介绍一下Raft算法的一些基本概念和术语,再详细介绍其在Curve中的实践。Raft一致性
Easter79 Easter79
2年前
TiKV 源码解析系列文章(十七)raftstore 概览
第一作者:李建俊,第二作者:杨哲轩,王聪TiKV作为一个分布式KV数据库,使用Raft算法来提供强一致性。Raft算法提供了单一group的一致性,但是单一group无法扩展和均衡。因此,TiKV采用了MultiRaft的方式基于Raft算法提供能兼顾一致性、扩展均衡的KV储存。下文以3.0版本代码为例,讲述raf
Wesley13 Wesley13
2年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Stella981 Stella981
2年前
RocketMQ 整合 DLedger(多副本)即主从切换实现平滑升级的设计技巧
源码分析RocketMQDLedger多副本系列已经进行到第8篇了,前面的章节主要是介绍了基于raft协议的选主与日志复制,从本篇开始将开始关注如何将DLedger应用到RocketMQ中。\摘要:详细分析了RocketMQDLedger多副本(主从切换)是如何整合到RocketMQ中,本文的行文思路首先结合已掌握的DLe
Stella981 Stella981
2年前
Raft 基础
目录1.三个状态2.什么是任期3.节点之间的通信1\.三个状态Raft设计了3个状态,用于表示节点的状态,分别是跟随者,候选者,领导者。1.领导者:通常只有一个领导人,并且其他节点都是跟随者。2.跟随者:跟随者不会发送任何请求,只是简单的响应领导者或者候选人的请求,由领导人处理所有的客户端请
Stella981 Stella981
2年前
Raft 与 Paxos的区别
RaftRaft概述        Raft一致性算法用于保证在分布式的条件下,所有的节点可以执行相同的命令序列,并达到一致的状态。这类的问题可以归结为“Replicatedstatemachines”问题。!关于Raft一致性协议的概要(http://static.oschina.net/uploads/img/
linbojue linbojue
1个月前
Etcd:分布式键值存储和配置系统
什么是Etcd?Etcd是一个开源的、分布式的键值存储和配置系统,由CoreOS团队开发并维护。它基于Raft一致性算法,用于存储和检索关键数据,并提供了高可用性、强一致性和高性能的特性。Etcd的设计目标是为分布式系统提供共享配置、服务发现、分布式锁和协