实现 Raft 协议
简介
Raft 是一个分布式共识算法,用于保证所有机器对一件事达成一个看法。本文用于记录实现 Raft 选举和日志复制的代码细节。
选举
节点启动时首先是跟随者状态,如果到达选举超时时间就尝试选举,为了预防对称网络分区带来的任期不断增加问题,需要使用预投票机制。
选举超时时间:跟随者在这段时间内没有搜到领导者的消息,就触发选举超时,转变为候选者开始竞选
对称网络分区:以 3 台机器为例,其中一台机器与另外两台机器(这两台中有一个领导者)的网络隔离开了,此时跟随者会触发选举超时,导致其不断增加任期,在网络恢复正常时,领导者会因任期小而下线,集群因此触发重新选举
预投票机制:触发选举超时先询问其他节点是否同意当前节点进行投票,当多数节点同意时再进行投票,即正式投票
上面介绍了选举需要注意的问题,下面说具体流程。
- 节点启动时开启一个选举超时时间检测定时任务,用于在当前节点是跟随者时不断检测是否发生了选举超时,发生超时就开始竞选。每一次定时任务的触发时间都是变化的,以防止所有节点一起选举,所有人都不投票后死循环。
- 任务内容:如果当前节点不是跟随者或选举超时时间内收到了来自领导者的消息就跳过这轮检测,否则就代表领导者可能下线了,开始竞选。
- 竞选的第一阶段是预投票:发起预投票 RPC,内容有想竞选的任期以及当前的最新日志的任期和索引,如果只有少数节点同意投票就结束这轮任务,否则开始正式投票,RPC 内容与上次相同,但这次需要改变当前节点任期号为想竞选的任期号了,同时也要更改状态为候选者,如果只有少数节点同意投票就结束这轮任务(同时回滚状态为跟随者),否则就更改状态成为领导者,开始发送心跳等等(成为领导者的一些事后面再说)。
到这里跟随者如何选举成为候选者以及领导者就大致完成了,还缺少其他节点如何处理投票请求:
- 请求的任期比当前节点小就拒绝。
- 领导者有效(选举超时时间内有收到了心跳)就拒接。
- 该任期已经投过票就拒绝。
- 最新的本地日志任期大就拒绝,日志任期相同但本地最新日志的索引更大也拒绝。
如果上面 4 个条件全通过就投票。投票过程中还有一些节点状态的变更处理,比如收到正式投票的任期比当前节点任期大需要转变为跟随者等等,当前这些也不是重点。
日志复制
日志复制是 Raft 的核心,这里涉及到状态机的执行,也就是共识的关键,比较复杂。
在完成选举后集群有了领导者,由领导者负责与客户端沟通,在领导者收到客户端请求时,领导者将这条待状态机执行的命令和当前任期组合成一条日志写入本地磁盘,并向其他节点发送该条日志,如果多数节点都表示收到了,也就表明达成共识了,那么领导者就会将这个命令放到状态机中执行,那么什么时候集群中的其他跟随者节点的状态机执行该条日志的命令呢?答案是由定时的心跳负责,每次心跳都会携带领导者状态机最后执行的日志索引,当跟随者收到后就会将当前节点状态机最后执行的日志索引和心跳中领导者的日志索引之间的日志放到状态机中执行,也就是说日志中命令的执行是一个二阶段的过程。
选举中我们忽略了一个地方,就是成为领导者后需要询问集群的节点日志复制情况,以此来将当前领导者多的日志复制到其他跟随者,大概过程如下:领导者拿着最新日志的任期和索引和跟随者对比,如果相同,等着领导者新的日志复制就行了,如果不同,说明这个日志是脏的(日志没被复制给大多数),此时领导者拿着该条日志的前一条日志继续对比,直到相同,然后领导者将相同的日志之后的所有日志复制给跟随者,跟随者将相同日志后的日志都删掉,再追加上领导者发来的日志,这样跟随者的日志就正确了。跟随者与领导者日志的对齐后就可以等待领导者发心跳了(即通知跟随者将哪些日志放到状态机中执行)。
关于状态机执行日志还有很重要的一点,就是节点需不需要保存当前状态机执行过的最后一条日志的索引,比如机器重启了,从头执行所有日志对状态机有没有影响。可以思考下,如果是一个 KV 数据库状态机,不保存也没问题,因为日志不管从哪里执行,数据库中的数据也不会变,但如果是 id 生成器,就会出现多执行一次 id 就会变化,多执行很多次甚至可能出现 id 分配完无法继续分配的问题,所以命令执行多次有问题就需要保存,并且需要满足保存执行过的索引和执行状态机命令是一个原子性的操作。
读请求优化(读索引读)
日志复制是需要刷盘的,这个操作非常耗时,写请求只能通过领导者进行日志复制处理,但读请求不同,可以像 ReentrantReadWriteLock 读写锁一样,将读请求负载到跟随者上,也就是实现跨机器的 volatile 语义(和跨进程类似),即读跟随者时确保跟随者的状态机已经和领导者的状态机一样,具体过程如下:跟随者收到读请求,跟随者请求领导者同步日志以及状态机应该执行到那条日志,领导者收到请求后向所有的节点发一个 RPC 确认领导者地位(防止领导者所在的少部分节点分区后还能正常读),确认后同步日志并回复该跟随者,收到回复后的跟随者的状态机再执行读请求。
对于领导者的读请求同样也不需要走日志复制,只需要和其他跟随者确认自己的领导者地位就可以执行读命令了。
最后
coding 时要注意节点任期的变化,刚开始可以先用一个全局锁来回避这个问题,等后面到一定的复杂程度再细化锁。完整的 Raft 还需要考虑很多,比如快照、批量、pipeline、删减节点等等。最后贴上我的实现 raft/README.md 以及相关学习资料:
为什么 Raft 的 ApplyIndex 和 CommitIndex 不需要持久化? - 知乎 (zhihu.com)
stateIs0/lu-raft-kv: this is raft java project. raft-kv-storage (github.com)
wenweihu86/raft-java: Raft Java implementation which is simple and easy to understand. (github.com)
实现 Raft 协议的更多相关文章
- Raft协议实战之Redis Sentinel的选举Leader源码解析
这可能是我看过的写的最详细的关于redis 选举的文章了, 原文链接 Raft协议是用来解决分布式系统一致性问题的协议,在很长一段时间,Paxos被认为是解决分布式系统一致性的代名词.但是Paxos难 ...
- MIT-6.824 Raft协议
摘要 raft是一种比paxos容易理解的一致性算法,实现起来比paxos简单许多.本文前部分描述算法的细节,后部分尝试探讨下该算法的原理. 算法描述 raft算法之所以简单的原因之一是它将问题分解成 ...
- Raft协议学习笔记
目录 目录 1 1. 前言 1 2. 名词 1 3. 什么是分布式一致性? 3 4. Raft选举 3 4.1. 什么是Leader选举? 3 4.2. 选举的实现 4 4.3. Term和Lease ...
- [搜狐科技]由浅入深理解Raft协议
由浅入深理解Raft协议 2017-10-16 12:12操作系统/设计 0 - Raft协议和Paxos的因缘 读过Raft论文<In Search of an Understandable ...
- Paxos、ZAB、RAFT协议
这三个都是分布式一致性协议,ZAB基于Paxos修改后用于ZOOKEEPER协议,RAFT协议出现在ZAB协议之后,与ZAB差不多,也有很大区别. 1. Paxos 分布式节点分为3种角色, Prop ...
- Paxos算法与Zookeeper分析,zab (zk)raft协议(etcd) 8. 与Galera及MySQL Group replication的比较
mit 分布式论文集 https://github.com/feixiao/Distributed-Systems wiki上描述的几种都明白了就出师了 raft 和 zab 是类似的,都是1.先选举 ...
- RocketMQ 多副本前置篇:初探raft协议
目录 1.Leader选举 1.1 一轮投票中,只有一个节点发起投票的情况 1.2 一轮投票中,超过一个节点发起投票的情况 1.3 思考如何实现Raft选主 2.日志复制 Raft协议是分布式领域解决 ...
- 基于 raft 协议的 RocketMQ DLedger 多副本日志复制设计原理
目录 1.RocketMQ DLedger 多副本日志复制流程图 1.1 RocketMQ DLedger 日志转发(append) 请求流程图 1.2 RocketMQ DLedger 日志仲裁流程 ...
- raft协议-分布式环境下的数据一致性问题
阅读了一个有意思的ppt,是Standford大学发表的raft协议 网址:http://thesecretlivesofdata.com/raft/ 下面自己总结下咯: 1.raft是一个实现了解决 ...
- DLedger —基于 raft 协议的 commitlog 存储库
“点击获取上云帮助文档” 尊敬的阿里云用户: 您好!为方便您试用开源 RocketMQ 客户端访问阿里云MQ,我们申请了专门的优惠券,优惠券可以直接抵扣金额.请填写下您公司账号信息,点击上图,了解更多 ...
随机推荐
- wget 命令的使用:HTTP文件下载、FTP文件下载--九五小庞
1. wget 命令简介与安装wget是用于在命令行终端下载网络文件的开源免费的命令工具,支持 HTTP/HTTPS.FTP/FTPS 协议的下载.wget 与 curl 相似,curl 可以理解为是 ...
- 《Python魔法大冒险》007 被困的精灵:数据类型的解救
小鱼和魔法师深入魔法森林,树木之间流淌着神秘的光芒,每一片叶子都似乎在低语着古老的咒语.不久,他们来到了一个小湖旁,湖中央有一个小岛,岛上困着一个透明的泡泡,里面有一个悲伤的精灵. 小鱼看着那个精灵, ...
- C#开发的基础工具类集合 - 开源研究系列文章
今天发布一个基础工具类代码集合. 以前有发布过一个类似的类库(见博文: Magical平台类库代码分享 ),不过那个版本有点久了,也没有这次这个全面,这次发布的是一个很多地方用到的基础类库代码. 1. ...
- SpingCloud:Gateway+Nginx+Stomp+Minio构建聊天室并进行文件传输
注:本人使用阿里云服务器(安装mino)+本地虚拟机(安装nginx)进行,理论上完全在本地进行也可以. 1.前期准备: 1.将本地虚拟机设置为静态ip且能ping通外网,参考网址:https://w ...
- Python ChatGPT Telegram Bot
注册 这里如何注册我就不说明了,大家自行去注册,主要是现在GPT的基本上已经备用很多了,导致了接码的价格也上涨了,而且使用token的话,其实还是很快可以用完免费的18美金: 接码:https://s ...
- 位图(bitmap)原理以及实现
大家好,我是蓝胖子,我一直相信编程是一门实践性的技术,其中算法也不例外,初学者可能往往对它可望而不可及,觉得很难,学了又忘,忘其实是由于没有真正搞懂算法的应用场景,所以我准备出一个系列,囊括我们在日常 ...
- 聊一聊 TLS/SSL
哈喽大家好,我是咸鱼 当我们在上网冲浪的时候,会在浏览器界面顶部看到一个小锁标志,或者网址以 "https://" 开头 这意味着我们正在使用 TLS/SSL 协议进行安全通信.虽 ...
- CopyOnWriteArrayList 写时复制思想
写时复制 conpyOnWrite容器即写时复制容器.往一个容器添加元素的时候,不直接往当前容器Object[]添加,而是先将当前容器Object[]进行Copy,复制出一个新的容器Object[] ...
- Vue源码学习(八):生命周期调用
好家伙, Vue源码学习(七):合并生命周期(混入Vue.Mixin) 书接上回,在上一篇中,我们已经实现了合并生命周期 现在,我们要在我们的初始化过程中,注册生命周期 1.项目目录 红框为本篇 ...
- Host-Only模式下虚拟机无法联网问题
环境: 镜像:Linux CentOS7 \-----------------------------------------\ 问题描述: 虚拟设置了Host-Only模式的虚拟网卡VMNet4,并 ...