基于Raft算法的DLedger-Library分析 | 京东物流技术团队
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选举过程中的临时角色,该状态下的节点会发起投票,尝试选择自己为主节点,选举成功后,不会存在该状态下的节点
2 DLedger架构设计
DLedger 的实现大体可以分为以下两个部分:
- 选举 Leader
- 日志复制
- 其整体架构如下图
从上面的架构图中,有两个核心类:DLedgerLeaderElector 和 DLedgerStore,选举和文件存储。选出 leader 后,再由 leader 去接收数据的写入,同时同步到其他的 follower,这样就完成了整个 Raft 的写入过程
3 DLedger选主源码分析
3.1 下载源码
从gitGub下载代码(https://github.com/openmessaging/dledger ),idea引入后,我们发现整个代码量很小,在分析代码时比较容易.
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()这个代码会更改投票轮次
2.step2 投票
进入到核心方法handleVote(),这个方法主要是判断其他节点请求来后,根据自己的term和请求者的等判断是否投赞成票
- ledgerEndIndex因为在日志复制过程中,每个节点的进度有可能是不一样的,所以在新的一轮选举时,这时不能投赞成票的
- 被选举者 term 小于 选举者的term,返回拒绝
- 被选举者 term 大于 选举者的term,则选举者进行下面操作:
- 变成candidate(或者保持candidate)
- 把needIncreaseImmediately设置为true。
- 返回 REJECT_TERM_NOT_READY,这个在后面提到。
这里补充说明:
选举者 的下一次状态循环会进入到maintainAsCandidate()函数,然后因为needIncreaseImmediately为true,所以把term更新,同时重置计时器。 但是并没有立刻发出投票(此时选举者 的CurrVoteFor还是null,使得接下来给之前的voting candidate 投赞成票可能)
获取所有node投票结果后开始计算票数:
3.step3 仲裁
在收到所有节点的投票结果计数后,进行仲裁,这里主要说明下图中这个条件
- acceptNum:同意的数量
- notReadyTermNum:未准备好的数量(即结果为REJECT_TERM_NOT_READY)
这里没有重置nextTimeToRequestVote的时间,即刻再发起一次投票。结合上面的说明,这样保证了被选者能尽快去拿到这些notRead的节点的赞成票。
最终经过多次投票后,当一个node节点获取到半数以上投票后,更新自己未leader角色,同时向其他node节点发送heartBeat,其他节点在收到心跳信息后,将自己从candidate 变为follower。
3.3 单元测试验证
3.3.1 编写单元测试
3.3.2 日志分析
3.4 应用场景
- DLedger 作为 RocketMQ ( version>=4.5.0)的消息存储已经发布
- 基于DLedger 实现多节点的缓存同步更新
- 基于日志复制的副本容错处理
4 总结
- 这里只简单分析了选主过程,在阅读源码过程中会涉及很多java的基础及netty的使用,比如AQS、CompletableFuture等,有助于提高我们的编码能力。
- DLedger在初始化时是将节点角色设置为candidate而不是follower 这个和原Raft是不同的地方,在节点角色转换过程中也稍有差别。
参考文献
- https://github.com/openmessaging/dledger/wiki
- https://www.usenix.org/system/files/conference/atc14/atc14-paper-ongaro.pdf
作者:京东物流 郭庆海
来源:京东云开发者社区 自猿其说Tech 转载请注明来源
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
火眼金睛破局ES伪慢查询 | 京东物流技术团队
一、问题现象 服务现象 服务接口的TP99性能降低 ES现象 YGC:耗时极其不正常, 峰值200+次,耗时7s+ FULL GC:不正常,次数为1但是频繁,STW 5s 慢查询:存在慢查询5+ 二 解决过程 1、去除干扰因素 从现象上看应用是由于某种原因导致JVM内存使用率不断增长,触发了频繁的YGC进而触发FGC(此时只是大胆的猜测)。 此时ES的JVM配置是JVM内存40G,使用CMS垃圾回收器。40G的内存使用CMS垃圾回收器性能显然不如G1更合适 找ES运维同学垃圾回收器由CMS修改为G1 (tips:不是所有的ES都适合G1,针对很多大查询的G1的Full GC会导致GC模式退化为串行扫描整个堆,导致几十秒甚至是分钟级别的暂停。这种长时间的暂停不仅影响用户的查询,还容易造成节点间的通信超时,导致master、dataNode脱离集群,影响集群稳定性。) 修改为G1后的GC变化: YGC:耗时极正常, 峰值35+次,耗时800ms FULL GC:正常,次数为0 慢查询:存在慢查询10+ 2、查找问题 ES的JVM垃圾回收器调整后,杰夫接口的服务接口的性能并没...
- 下一篇
大模型那么火,教你一键Modelarts玩转开源LlaMA(羊驼)大模型
本文分享自华为云社区《大模型那么火,教你一键Modelarts玩转开源LlaMA(羊驼)大模型》,作者:码上开花_Lancer 。 近日,LlaMA(羊驼)这个大模型再次冲上热搜! LLaMA(Large Language Model Meta AI),由 Meta AI 发布的一个开放且高效的大型基础语言模型,共有 7B、13B、33B、65B(650 亿)四种版本。其数据集来源都是公开数据集,无任何定制数据集,保证了其工作与开源兼容和可复现,整个训练数据集在 token 化之后大约包含 1.4T 的 token。关于模型性能,LLaMA 的性能非常优异:具有 130 亿参数的 LLaMA 模型「在大多数基准上」可以胜过 GPT-3( 参数量达 1750 亿),而且可以在单块 V100 GPU 上运行;而最大的 650 亿参数的 LLaMA 模型可以媲美谷歌的 Chinchilla-70B 和 PaLM-540B。 上篇文章有介绍了LLaMA 所采用的Transformer 结构和细节,与之前所介绍的Transformer架构不同的地方包括采用了前置层归一化(Pre-normaliz...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2整合Redis,开启缓存,提高访问速度
- CentOS7设置SWAP分区,小内存服务器的救世主
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- SpringBoot2全家桶,快速入门学习开发网站教程
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- CentOS7安装Docker,走上虚拟化容器引擎之路
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- CentOS8编译安装MySQL8.0.19