Raft 协议
Paxos 存在的问题
Paxos 算法的描述偏学术化,缺失了很多细节,无法直接应用于工程领域。实际工程应用中的分布式算法大多是 Paxos 的变种,验证这些算法的正确性也成为了一个难题。
举个例子:上一篇文章的 最后 介绍了一个应用 Paxos 算法的工程模型,这个模型存在明显的写性能瓶颈:
- 使用多主架构,写入冲突的概率高
- 每次更新操作都需要至少 2 轮以上的网络通信,通信开销大
如果要提高该模型的性能,仍需要在很多细节上做进一步调整,最终实现出来的算法已经和原始的版本的 Paxos 相去甚远。
为了解决以上问题,另一个高性能且易于理解的一致性算法横空出世:Raft
基本概念
Raft 算法基于 复制状态机Replicated State Machine
模型,本质上就是一个管理 日志复制 的算法。
Raft 集群采用 Single Leader 架构,集群中有唯一的 Leader 进程负责管理日志复制,其职责有:
- 接受 Client 发送的请求
- 将日志记录同步到其他进程
- 告知其他进程的何时能够提交日志
复制状态机
复制状态机的本质就是:Paxos + WAL
State Machine
,并且使用一个日志存储其所要执行指令。如果两个状态机执行按照相同的顺序,执行相同的指令,则这两个进程最终能够收敛到同个状态。如果能保证所有进程的日志一致,则每个进程的状态必定也是一致的。
任期
为了减少不必要的网络通信,日志追加顺序由集群唯一的 Leader 决定,无须与其他节点协商。通信开销从最低 2 次降为固定的 1 次,从而大幅提高了算法的性能。
出于可用性考虑,当前 Leader 下线后,集群需要从存活的节点中挑选一个新的 Leader,这个过程被称为选举 election
。
每次选举都会产生一个新的任期号 term
(单调递增),如果选举中产生了一个新的 Leader,那么这个任期号会伴随这个 Leader 直到其下线。
每个 参与者 进程都会维护一个 current_term
用于表示已知的最新任期,进程之间通过彼此交换该值来感知 Leader 变化。
/**
* 基础信息
*/
public abstract class RaftMember implements RaftParticipant {
// 响应 RPC 前需要持久化以下两个属性
protected final long currentTerm; // 已知的最新的 term(初始为 0)
protected final ID lastCandidate; // 最近一次赞成投票的 candidate
protected RaftMember(long term, ID candidate) {
this.currentTerm = term;
this.lastCandidate = candidate;
stableStorage().persist(currentTerm, lastCandidate);
}
/**
* @see RaftParticipant#currentTerm()
*/
@Override
public long currentTerm() {
return currentTerm;
}
/**
* @see RaftParticipant#votedFor()
*/
@Override
public ID votedFor() {
return lastCandidate;
}
}
日志
日志是 Raft 的核心概念。Raft 保证日志是连续且一致的,并且最终能够被所有进程按照日志索引的顺序提交。
每条日志记录包含:
- 任期
term
:生成该条记录的 Leader 对应的任期 - 索引
index
:其在日志中的顺序 - 命令
command
:可执行的状态机指令
一旦某条日志中的命令被状态机执行了,那么我们称这条记录为已提交committed
,Raft 保证已提交的记录不会丢失。
角色
Raft集群中每个进程只能担任其中一个角色:
- Leader:发送心跳、管理日志复制与提交
- Follower:被动响应其他节点发送过来的请求
- Candidate:主动发起并参与选举
Raft 进程间使用 RPC 的方式进行通信,实现最基础的共识算法只需 两种RPC:
- RequestVote:用于选举产生 Leader
- AppendEntries:复制日志与发送心跳
/**
* RPC 接口
* */
public interface RaftService {
/**
* 复制日志+发送心跳(由 leader 调用)
* @param term leader 任期
* @param leaderId leader 在集群中的唯一标识
* @param prevLogIndex 紧接在新的之前的日志条目索引
* @param prevLogTerm prevLogIndex 对应的任期
* @param entries 日志条目(发送心跳时为空)
* @param leaderCommit leader 已经提交的日志条目索引
* @return 当 follower 中的日志包含 prevLogIndex 与 prevLogTerm 匹配的日志条目返回 true
* */
Async<RaftResponse> appendEntries(
long term, ID leaderId,
long prevLogIndex, long prevLogTerm,
Entry[] entries, long leaderCommit) throws Exception;
/**
* 选主(由 candidate 调用)
* @param term candidate 任期
* @param candidateId candidate 在集群中的唯一标识
* @param lastLogIndex candidate 最后一条日志条目的索引
* @param lastLogTerm candidate 最后一条日志条目的任期
* @return 当收到赞成票时返回 true
* */
Async<RaftResponse> requestVote(
long term, ID candidateId,
long lastLogIndex, long lastLogTerm) throws Exception;
}
算法流程
基于 Single Leader 模型,Raft 将一致性问题分解为 3 个独立的子问题:
- Leader 选举
Election
:Leader 进程失效后能够自动选举出一个新的 Leader - 日志复制
Replication
:Leader 保证其他节点的日志与其保持一致 - 状态安全
Safety
:Leader 保证状态机执行指令的顺序与内容完全一致
为了方便理解,下面结合 动画 进行介绍。
选举
使用 心跳超时heartbeat timeout
机制来触发 Leader 选举:
- 节点启动时默认处于 Follower 状态,如果 Follower 超时未收到 Leader 心跳信息,会转换为 Candidate 并向其他节点发起 RequestVote 请求。
- 当 Candidate 收到半数以上的选票之后成为 Leader,开始定时向其他节点发起 AppendEntries 请求以维持其 Leader 的地位。
- Leader 失效之后停止发送心跳,Follower 的心跳超时机制又会被触发,开始新一轮的选举。
复制
未提交日志
已提交日志
集群中只有 Leader 对外提供服务:
当 Leader 在接收到命令之后,首先会将命令转换为一条对应的 日志记录log entry
,并追加到本地的日志中。然后调用 AppendEntries 将这条日志复制到其他节点的日志中。
当日志被复制到过半数节点上时,Leader 会将这条日志中包含的命令 提交commit
状态机执行,最后将执行结果告知客户端。
网络分区
使用 过半数majority
机制来处理脑裂:
如果日志无法复制到多数节点,Leader 会拒绝提交这些日志,当网络分区消失后,集群会自动恢复到一致的状态。
安全性保证
选举时…
保证新的 Leader 拥有所有已经提交的日志
- 每个 Follower 节点在投票时会检查 Candidate 的日志索引,并拒绝为日志不完整的 Candidate 投赞成票
- 半数以上的 Follower 节点都投了赞成票,意味着 Candidate 中包含了所有可能已经被提交的日志
提交日志时…
Leader 只主动提交自己任期内产生的日志
- 如果记录是当前 Leader 所创建的,那么当这条记录被复制到大多数节点上时,Leader 就可以提交这条记录以及之前的记录
- 如果记录是之前 Leader 所创建的,则只有当前 Leader 创建的记录被提交后,才能提交这些由之前 Leader 创建的日志
总结
一致性算法的本质:一致性与可用性之间的权衡。
Raft 的优点:Single Leader 的架构简化日志管理
Raft 的缺点:对日志的连续性有较高要求
Multi-Raft
的模式对无关的业务进行解耦,从而提高系统的并发度。
限于篇幅限制,本文只展示了 Raft 算法大致的轮廓。
此前在学习算法的过程中,使用 Java 实现了一个简化版的 Raft 协议:rafting
代码忠实于论文原文,包含了其中的众多算法细节,希望对各位学习 Raft 的朋友有所帮助。
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,我们申请了专门的优惠券,优惠券可以直接抵扣金额.请填写下您公司账号信息,点击上图,了解更多 ...
随机推荐
- 详细了解JS Map,它和传统对象有什么区别?
转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者.原文出处:https://www.codeproject.com/Articles/5278387/Under ...
- 【学习中】Fitness Schedule
章节 内容 签到 第1周:步伐 √第1课(34分钟) 跑步1分钟 行走2分钟 共做8次 4月24日 √第2课(28分钟) 跑步1分钟 行走2分钟 共做6次 4月27日 √第3课(31分钟) 跑步1分钟 ...
- Git 实用操作:重写 Commit 历史
当我们修改完代码,提交了一个 commit,然后发现改错了,怎么修正?下面分两种情况来讨论:修正最近一次提交,和修正历史多个提交. 修正最近一次提交 如果发现刚刚提交的内容有错误,当场再修改一下再提交 ...
- C++11的decltype关键字
C++11的decltype关键字 概述 decltype关键字和auto有异曲同工之处 有时我们希望从表达式的类型推断出要定义的变量类型,但是不想用该表达式的值初始化变量(如果要初始化就用auto了 ...
- Java多线程类FutureTask源码阅读以及浅析
FutureTask是一个具体的实现类,实现了RunnableFuture接口,RunnableFuture分别继承了Runnable和Future接口,因此FutureTask类既可以被线程执行,又 ...
- composer 国内镜像
本文列举一些最常用的国内镜像,配置国内镜像后可以提高composer包的下载速度.使用阿里云镜像的开发者较多,我也一直在使用这个镜像. 1. composer 中文网提供的中国全量镜像 https:/ ...
- 【吴恩达课程使用】anaconda (python 3.7) win10安装 tensorflow 1.8 cpu版
[吴恩达课程使用]anaconda (python 3.7) win10安装 tensorflow 1.8 目前tensorflow是只支持到python3.6的,anaconda最新版本已经到pyt ...
- 三、spring boot开发web应用-使用传统的JDBC
上一节<spring boot第一个web服务>中我们只是简单的展示了spring mvc的功能,并没有涉及到具体的CRUD的操作,也没有涉及到数据持久化的方面.本节中我们将基于原始的JD ...
- Java面试知识点1
typora-root-url: ......\Software\Typora\Picture Bean的作用域 在Spring的元素的scope属性设置bean的作用域,用来决定bean是单实例还是 ...
- 2.1 Spring5源码编译
一. 准备工作 1. . 编译环境 maven jdk8 idea 2. 编译版本: SpringV5.2.7RELEASE+GradleWapper+jdk1.8.0_131编译 二. 源码下载 g ...