实现 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,我们申请了专门的优惠券,优惠券可以直接抵扣金额.请填写下您公司账号信息,点击上图,了解更多 ...
随机推荐
- (2023.8.28)Hi铁布衫-CM Ver 0.001 - Cracked-writeup
Hi铁布衫-CM Ver 0.001 WriteUp 本文作者:XDbgPYG(小吧唧) 发布时间:2023年8月28日 内容概要:Hi铁布衫-CM Ver 0.001 WriteUp 收集信息 有一 ...
- Shell脚本中文英文多语言国际化和命令行批处理(bash sh cmd bat)中定义函数的简单写法
目录 命令行脚本参考 - bat 命令行脚本参考 - bash 值得学习的知识点 1. 识别终端使用的语言 2. 函数的编写 3. 获取用户的输入 4. bat文件老是乱码怎么办 有时候为了方便别人使 ...
- Mybatis-Plus 系列:简介和基本使用
目录 一.简介 二.特性 三.基本使用 1.初始化数据库 2.初始化工程 3.精简 SpringBoot 相关日志 一.简介 官网:https://www.baomidou.com MyBatis-P ...
- 专为小白打造—Kafka一篇文章从入门到入土
一.什么是Kafka MQ消息队列作为最常用的中间件之一,其主要特性有:解耦.异步.限流/削峰. Kafka 和传统的消息系统(也称作消息中间件)都具备系统解耦.冗余存储.流量削峰.缓冲.异步通信.扩 ...
- 一文教你理解Kafka offset
日常开发中,相信大家都对 Kafka 有所耳闻,Kafka 作为一个分布式的流处理平台,一般用来存储和传输大量的消息数据.在 Kafka 中有三个重要概念,分别是 topic.partition 和 ...
- 深入解析枚举(Enum):在程序设计中的应用与优势
深入解析枚举(Enum):在程序设计中的应用与优势 引言 在程序设计中,我们经常需要用到一组具名的常量,这些常量表示一些有限的离散状态或取值范围.例如,表示方向(上.下.左.右).星期几.性别等.为了 ...
- 圆角android
资源地址 <shape xmlns:android="http://schemas.android.com/apk/res/android"> <solid an ...
- oracle命令3 冷备份
用户管理的备份:备份脚本要自己写:备份哪些文件要自己选:恢复时要复制那些文件自己判断:恢复需要的日志,自己找: 备份,需要备份保存关键SCN信息的文件:一次完成的备份包括:控制文件,数据文件,日志文件 ...
- 是因为不同的浏览器内核吗--Could not register service workers到底是怎么回事
什么是浏览器内核 浏览器内核(Rendering Engine),是浏览器最核心的部分. 它负责处理网页的HTML.CSS.JavaScript等代码,并将其转化为可视化的网页内容.即我们常说的对网页 ...
- 想让你的代码简洁,试试这个SimpleDateFormat类高深用法
本文分享自华为云社区<从入门到精通:SimpleDateFormat类高深用法,让你的代码更简洁!>,作者:bug菌. 环境说明:Windows 10 + IntelliJ IDEA 20 ...