SOFAJRaft 选举机制剖析 | SOFAJRaft 实现原理

简介: SOFAStack (Scalable Open Financial Architecture Stack)是蚂蚁金服自主研发的金融级分布式架构,包含了构建金融级云原生架构所需的各个组件,是在金融场景里锤炼出来的最佳实践。

SOFAStack (Scalable Open Financial Architecture Stack)是蚂蚁金服自主研发的金融级分布式架构,包含了构建金融级云原生架构所需的各个组件,是在金融场景里锤炼出来的最佳实践。

image.png

本文为《剖析 | SOFAJRaft 实现原理》第四篇,本篇作者力鲲,来自蚂蚁金服《剖析 | SOFAJRaft 实现原理》系列由 SOFA 团队和源码爱好者们出品,项目代号:,目前领取已经完成,感谢大家的参与。

SOFAJRaft 是一个基于 Raft 一致性算法的生产级高性能 Java 实现,支持 MULTI-RAFT-GROUP,适用于高负载低延迟的场景。

SOFAJRaft :

https://github.com/sofastack/sofa-jraft

前言

在 Raft 算法中,选举是很重要的一部分,所谓选举也就是在多个节点中选出一个 Leader 节点,由他来对外提供写服务 (以及默认情况下的读服务)。

在剖析源码时,对选举机制的理解经常会遇到两极分化的情况,对于了解 Raft 算法基本原理的同学,阅读源码就是品味实现之巧妙的过程,而对初体验的同学,却会陷入丈二和尚的窘境,仿佛坠入云里雾里。

为了提升文章的可读性,我还是希望花一部分篇幅讲清楚选举机制的基本原理,以便后面集中注意力于代码实现。下面是一段图文比喻,帮助理解的同时也让整篇文章不至于过早陷入细节的讨论。

问题1:选举要解决什么

一个分布式集群可以看成是由多条战船组成的一支舰队,各船之间通过旗语来保持信息交流。这样的一支舰队中,各船既不会互相完全隔离,但也没法像陆地上那样保持非常密切的联系,天气、海况、船距、船只战损情况导致船舰之间的联系存在但不可靠。

舰队作为一个统一的作战集群,需要有统一的共识、步调一致的命令,这些都要依赖于旗舰指挥。各舰船要服从于旗舰发出的指令,当旗舰不能继续工作后,需要有别的战舰接替旗舰的角色。

image.png

图1 - 所有船有责任随时准备接替旗舰

如何在舰队中,选出一艘得到大家认可的旗舰,这就是 SOFAJRaft 中选举要解决的问题。

问题2:何时可以发起选举

何时可以发起选举?换句话说,触发选举的标准是什么?这个标准必须对所有战舰一致,这样就能够在标准得到满足时,所有舰船公平的参与竞选。在 SOFAJRaft 中,触发标准就是通信超时,当旗舰在规定的一段时间内没有与 Follower 舰船进行通信时,Follower 就可以认为旗舰已经不能正常担任旗舰的职责,则 Follower 可以去尝试接替旗舰的角色。这段通信超时被称为 Election Timeout (简称 ET), Follower 接替旗舰的尝试也就是发起选举请求。

image.png

图2 - ET 触发其他船竞选旗舰

问题3:何时真正发起选举

在选举中,只有当舰队中超过一半的船都同意,发起选举的船才能够成为旗舰,否则就只能开始一轮新的选举。所以如果 Follower 采取尽快发起选举的策略,试图尽早为舰队选出可用的旗舰,就可能引发一个潜在的风险:可能多艘船几乎同时发起选举,结果其中任何一支船都没能获得超过半数选票,导致这一轮选举无果,然后失败的 Follower 们再一次几乎同时发起选举,又一次失败,再选举 again,再失败 again ···
image.png

图3 - 同时发起选举,选票被瓜分

为避免这种情况,我们采用随机的选举触发时间,当 Follower 发现旗舰失联之后,会选择等待一段随机的时间 Random(0, ET) ,如果等待期间没有选出旗舰,则 Follower 再发起选举。

image.png

图4 - 随机等待时间

问题4:哪些候选者值得选票

SOFAJRaft 的选举中包含了对两个属性的判断:LogIndex 和 Term,这是整个选举算法的核心部分,也是容易让人产生困惑的地方,因此我们做一下解释:

Term
我们会对舰队中旗舰的历史进行编号,比如舰队的第1任旗舰、第2任旗舰,这个数字我们就用 Term 来表示。由于舰队中同时最多只能有一艘舰船担任旗舰,所以每一个 Term 只归属于一艘舰船,显然 Term 是单调递增的。

LogIndex
每任旗舰在职期间都会发布一些指令(称其为“旗舰令”,类比“总统令”),这些旗舰令当然也是要编号归档的,这个编号我们用 Term 和 LogIndex 两个维度来标识,表示“第 Term 任旗舰发布的第 LogIndex 号旗舰令”。不同于现实中的总统令,我们的旗舰令中的 LogIndex 是一直递增的,不会因为旗舰的更迭而从头开始计算。
image.png

图5 - 总统令 Vs 旗舰令,LogIndex 稍有区别

所有的舰船都尽可能保存了过去从旗舰接收到的旗舰令,所以我们选举的标准就是哪艘船保存了最完整的旗舰令,那他就最有资格接任旗舰。具体来说,参与投票的船 V 不会对下面两种候选者 C 投票:一种是 lastTermC < lastTermV;另一种是 (lastTermV == lastTermC) && (lastLogIndexV > lastLogIndexC)。

稍作解释,第一种情况说明候选者 C 最后一次通信过的旗舰已经不是最新的旗舰了,至少比 V 更滞后,所以它所知道的旗舰令也不可能比 V 更完整。第二种情况说明,虽然 C 和 V 都与同一个旗舰有过通信,但是候选者 C 从旗舰处获得的旗舰令不如 V 完整 (lastLogIndexV > lastLogIndexC),所以 V 不会投票给它。

image.png

图6 - Follower 船 b 拒绝了船 c 而投票给船 a,船 a 旗舰令有一个空白框表示“第 Term 任旗舰”没有发布过任何旗舰令

问题5:如何避免不够格的候选者“捣乱”

如上一小节所说,SOFAJRaft 将 LogIndex 和 Term 作为选举的评选标准,所以当一艘船发起选举之前,会自增 Term 然后填到选举请求里发给其他船只 (可能是一段很复杂的旗语),表示自己竞选“第 Term + 1 任”旗舰。

这里要先说明一个机制,它被用来保证各船只的 Term 同步递增:当参与投票的 Follower 船收到这个投票请求后,如果发现自己的 Term 比投票请求里的小,就会自觉更新自己的 Term 向候选者看齐,这样能够很方便的将 Term 递增的信息同步到整个舰队中。

image.png

图7 - Follower 船根据投票请求更新自己的 Term

但是这种机制也带来一个麻烦,如果一艘船因为自己的原因没有看到旗舰发出的旗语,他就会自以为是的试图竞选成为新的旗舰,虽然不断发起选举且一直未能当选(因为旗舰和其他船都正常通信),但是它却通过自己的投票请求实际抬升了全局的 Term,这在 SOFAJRaft 算法中会迫使旗舰 stepdown (从旗舰的位置上退下来)。

image.png

图8 - 自以为是的捣乱者,迫使旗舰 stepdown

所以我们需要一种机制阻止这种“捣乱”,这就是预投票 (pre-vote) 环节。候选者在发起投票之前,先发起预投票,如果没有得到半数以上节点的反馈,则候选者就会识趣的放弃参选,也就不会抬升全局的 Term。

image.png

图9 - Pre-vote 预投票

选举剖析

在上面的比喻中,我们可以看到整个选举操作的主线任务就是:

Candidate 被 ET 触发

Candidate 开始尝试发起 pre-vote 预投票

Follower 判断是否认可该 pre-vote request

Candidate 根据 pre-vote response 来决定是否发起 RequestVoteRequest

Follower 判断是否认可该 RequestVoteRequest

Candidate 根据 response 来判断自己是否当选

这个过程可用下图表示:

image.png

图10 - 一次成功的选举

在代码层面,主要是由四个方法来处理这个流程:

com.alipay.sofa.jraft.core.
NodeImpl
#preVote 
//预投票

com.alipay.sofa.jraft.core.
NodeImpl
#electSelf 
//投票

com.alipay.sofa.jraft.core.
NodeImpl
#handlePreVoteRequest 
//处理预投票请求

com.alipay.sofa.jraft.core.
NodeImpl
#handleRequestVoteRequest 
//处理投票请求

代码逻辑比较直观,所以我们用流程图来简述各个方法中的处理。

预投票和投票
image.png

图11 - 预投票 Vs 投票

图中可见,预投票请求 preVote 和投票请求 electSelf 的流程基本类似,只是有几个细节不太一样:

preVote 是由超时触发;

preVote 在组装 Request 的时候将 term 赋值为 currTerm + 1,而 electSelf 是先将 term ++;

preVote 成功后,进入 electSelf,electSelf 成功后 become Leader。

处理请求
处理预投票和投票请求的逻辑也比较类似,同样用图来表示。

image.png

图12 - 处理预投票请求

image.png

图13 - 处理投票请求

图中可见,处理两种请求的流程也基本类似,只是处理投票请求的时候,会有 stepdown 机制,强制使 Leader 从其 Leader 的身份退到 Follower。在具体的实现中,Leader 会通过租约的机制来避免一些没有必要的 stepdown,关于租约机制,可以参见之前的系列文章《SOFAJRaft 线性一致读实现剖析 | SOFAJRaft 实现原理》。

总结

我们在本文中采用了类比的方式来剖析源码,主要是为了让读者能更容易的理解如何在分布式环境中达成共识,其实这也是整个 SOFAJRaft 要实现的目标。

行文至此,作者感觉已经把选举说清楚了,如果还有没提到的地方,或者一些流程中的分支任务,欢迎从源码中进一步寻找答案。最后贴出上面提到的四个方法的源码。

image.png

图14 - preVote 预投票

image.png

图15 - electSelf 投票

image.png

图16 - handlePreVoteRequest 处理预投票

image.png

图17 - handleRequestVoteRequest 处理投票

目录
相关文章
|
存储 缓存 网络协议
dpdk课程学习之练习笔记二(arp, udp协议api测试)
dpdk课程学习之练习笔记二(arp, udp协议api测试)
470 0
|
4月前
|
JSON 负载均衡 监控
《服务治理》Thrift与gRPC深度对比与实践
在微服务架构中,服务间通信是系统设计的核心环节。RPC(Remote Procedure Call)框架通过抽象网络通信细节,让开发者能够像调用本地方法一样调用远程服务,极大地提升了开发效率。
|
数据可视化 Android开发
XMind 2021 v11.1.2破解版使用方法
XMind 2021 v11.1.2破解版使用方法
772 0
|
5月前
|
Java 测试技术 编译器
@GrpcService使用注解在 Spring Boot 中开始使用 gRPC
本文介绍了如何在Spring Boot应用中集成gRPC框架,使用`@GrpcService`注解实现高效、可扩展的服务间通信。内容涵盖gRPC与Protocol Buffers的原理、环境配置、服务定义与实现、测试方法等,帮助开发者快速构建高性能的微服务系统。
1106 0
|
Kubernetes Ubuntu Windows
【Azure K8S | AKS】分享从AKS集群的Node中查看日志的方法(/var/log)
【Azure K8S | AKS】分享从AKS集群的Node中查看日志的方法(/var/log)
386 3
|
IDE PHP 开发工具
「Python入门」python环境搭建及VScode使用python运行方式
**Python 概述与环境搭建摘要** Python是一种解释型、面向对象、交互式的脚本语言,以其简单易学和丰富库著称。安装Python时,推荐在Windows上选择.exe安装程序,记得勾选“Add Python to PATH”。安装完成后,通过环境变量配置确保Python可被系统识别。验证安装成功,可在CMD中输入`python --version`。Visual Studio Code (VScode)是流行的Python IDE,安装Python插件并选择解释器后,可直接在VScode内编写和运行Python代码。
895 0
「Python入门」python环境搭建及VScode使用python运行方式
|
存储 达摩院 供应链
排产排程问题【数学规划的应用(含代码)】阿里达摩院MindOpt
**文章摘要:** 本文探讨了使用阿里巴巴达摩院的MindOpt优化求解器解决制造业中的排产排程问题。排产排程涉及物料流动、工序安排、设备调度等多个方面,通常通过数学规划方法建模。MindOpt支持线性规划、整数规划等,能有效处理大规模数据。案例以香皂制造工厂为例,考虑了多种油脂的购买、存储和生产计划,以及价格变化和存储成本。问题通过数学建模转化为MindOpt APL代码,求解器自动寻找最优解,以最大化利润。文章还提供了代码解析,展示了解决方案的细节,包括目标函数(利润最大化)、约束条件(如生产效率、库存管理)以及结果分析。
|
机器学习/深度学习 算法 计算机视觉
基于深度学习网络的USB摄像头实时视频采集与人脸检测matlab仿真
**摘要 (Markdown格式):** ```markdown - 📹 使用USB摄像头(Tttttttttttttt666)实时视频检测,展示基于YOLOv2在MATLAB2022a的实施效果: ``` Tttttttttttttt1111111111------------5555555555 ``` - 📺 程序核心利用MATLAB视频采集配置及工具箱(Dddddddddddddd),实现图像采集与人脸定位。 - 🧠 YOLOv2算法概览:通过S×S网格预测边界框(B个/网格),含坐标、类别概率和置信度,高效检测人脸。
|
SQL 安全 算法
【惊险揭秘】Django高手的十大安全秘籍:如何从零构建坚不可摧的Web堡垒?
【8月更文挑战第31天】《Django安全性指南:构建安全Web应用的十大关键步骤》介绍了在使用Django框架开发Web应用时,如何通过十个关键步骤提升应用安全性。从使用HTTPS、设置CSRF保护到限制密码复杂度、防止SQL注入,文章详细阐述了每一步的具体实施方法及示例代码,帮助开发者构建更加安全可靠的Web应用。
380 0
|
存储 Kubernetes 调度
在K8S中,deployment的创建过程包括什么?
在K8S中,deployment的创建过程包括什么?