前言

Paxos、Zab、Raft都属于在分布式环境保持数据一致性的相关算法。

对于这三个算法,初次接触的时候有很多疑惑的地方:

1. 这3个算法的实现是什么,复杂么

2. 为什么要存在这么多算法,一个不能解决么,都用在什么场景。

算法本身不太复杂,但是应用在实际场景中解决问题,开发起来还是比较复杂的。

下面尽可能简单易懂的进行描述。

Paxos

paxos算法是在不会出现拜占庭错误的环境下达成一致性协议的解决方案。

p.s. 分布式环境都是通过网络通讯,系统中的成员可能出错而发送错误的信息,用于传递信息的通讯网络也可能导致信息损坏,传输或响应的信息有误算是拜占庭错误,而非拜占庭错误就是说信息不会在传输过程中遭到篡改。

详细了解,查看:拜占庭将军问题

角色

paxos在分布式环境中存在多个节点,节点的角色如下:

  • Client    在分布式系统中发送一个请求并等待响应,比如写一条数据的请求
  • Proposer  发送prepare和accept请求,对于client发送的请求希望Acceptor能同意
  • Acceptor   处理prepare和accept请求,就是对发送的请求进行投票
  • Learner     对于上面的请求,获取paxos算法的结果
  • Leader    Paxos集群中选出唯一一个节点作为leader处理提议

Acceptor是对请求进行投票的,那在分布式环境中作为Acceptor的大部分节点都应当是存活的,来保证多票当选(同zab或raft的投票选举来理解)。

并不是一个节点只能作为一个角色,paxos实现的集群,每一个节点应该包含Proposer、Acceptor、Learner三种角色,可以处理Client请求并进行投票最终响应。

Paxos的实现也是不同的,比如Basic Paxos和Multi-Paxos,本文主要以基本实现进行说明。

算法

paxos算法实现分为两个阶段,通信过程中数据结构可以简化为(n, v)表示。

n表示一个提案号,v是与该提案号对应的值。

e.g. client要写入一条数据,Proposer提出一个提案号给集群中其它的Acceptor,如果有Quorum(可以认为是半数以上)的Acceptor同意这个提案,那么这个提案号对应的数据v就可以被写入。

这两个阶段如下:

阶段1a:Proposer发出一条“Prepare"消息,带着提案号n,发给Acceptor(包括它自身)

阶段1b:Acceptor收到Proposer的Prapare消息后,看一下这个提案号和之前收到的提案号相比,如果比之前的都大就同意(发一条Propose消息给Proposer),不是就忽略或表示拒绝

阶段2a:Proposer收到Qururm数量的Acceptor的Propose消息,说明都同意这个提案,就发送一个Accept(n,v)消息给这些Acceptor(带着提案号和该提案号对应的数据)

阶段2b:收到Proposer的Accept消息的Acceptor就把这个数据写入。

这样就算达成一个共识,如果上面提案最终失败,其实会重新开始新一轮提案。

下面这个流程图来自Paxos的wiki,可以看一下帮助理解下这个过程:

图片来源

注意,上面这个基本的paxos实现包括两个阶段会涉及很多消息交换,Multi-Paxos 实现会选举一个leader,只需要第2阶段即可确定一个值。

Paxos的实现案例 chubby:https://courses.cs.washington.edu/courses/csep552/13sp/lectures/5/chubby.pdf

Raft

目前使用相当广泛的一个一致性算法,比如ETCD,Consul,kafka,rocketmq的dledger都有用到。

p.s.  我在wiki搜索的搜索的搜到的是一款游戏

目前我实际接触到的都是leader选举、日志复制的解决方案。

角色

一个raft集群有若干个节点,角色如下:

  • Leader 只有一个(比如kafka的分区、rocketmq的主节点(dledger),接受客户端的请求
  • Follower 接受leader的写请求(数据同步过来)
  • Candidate 在选举leader时的这些follower

比如我们说kafka消息主从复制就是说Leader和Follower。

对于Raft的快速理解,推荐一个网站(新手都容易看懂):Raft

算法

以实际项目来举例,其实很简单,以rocketmq dledger来说明。

日志复制

p.s. 不以kafka示例,有两个原因,一个是kafka的ack数量可以配置,写入一条消息的可以配置ack数量算是成功。rocketmq dledger如是1主2从,只要有一个从节点写入成功(集群中一主一从2个节点已经写入,超过半数节点)便可以认为成功写入,更容易理解。第二个原因,是我本地刚好有一版rocketmq的源码。

代码版本是4.9.1

DLedgerCommitLog:DLedgerCommitLog

            BatchAppendEntryRequest request = new BatchAppendEntryRequest();request.setGroup(dLedgerConfig.getGroup());request.setRemoteId(dLedgerServer.getMemberState().getSelfId());request.setBatchMsgs(encodeResult.batchData);// 写入消息dledgerFuture = (BatchAppendFuture<AppendEntryResponse>) dLedgerServer.handleAppend(request);

看handleAppend()方法:dledger/DLedgerServer.java at master · openmessaging/dledger · GitHub

看方法注释:

关键是第2步,等待Quorum节点的ack.

                    DLedgerEntry dLedgerEntry = new DLedgerEntry();dLedgerEntry.setBody(request.getBody());DLedgerEntry resEntry = dLedgerStore.appendAsLeader(dLedgerEntry);return dLedgerEntryPusher.waitAck(resEntry, false);

基本流程就是客户端生产者发送一条消息到主节点,主节点发送给从节点,等待其中部分从节点写入成功返回ack,主节点响应客户端消息提交成功。

选举

关于ledaer选举主要在:DLedgerLeaderElector

通过心跳维护leader和follower之间的关系:

/*** The core method of maintainer. Run the specified logic according to the current role: candidate => propose a* vote. leader => send heartbeats to followers, and step down to candidate when quorum followers do not respond.* follower => accept heartbeats, and change to candidate when no heartbeat from leader.** @throws Exception*/private void maintainState() throws Exception {if (memberState.isLeader()) {maintainAsLeader();} else if (memberState.isFollower()) {maintainAsFollower();} else {maintainAsCandidate();}}private void maintainAsLeader() throws Exception {if (DLedgerUtils.elapsed(lastSendHeartBeatTime) > heartBeatTimeIntervalMs) {long term;String leaderId;synchronized (memberState) {if (!memberState.isLeader()) {//stop sendingreturn;}term = memberState.currTerm();leaderId = memberState.getLeaderId();lastSendHeartBeatTime = System.currentTimeMillis();}sendHeartbeats(term, leaderId);}}private void maintainAsFollower() {if (DLedgerUtils.elapsed(lastLeaderHeartBeatTime) > 2 * heartBeatTimeIntervalMs) {synchronized (memberState) {if (memberState.isFollower() && (DLedgerUtils.elapsed(lastLeaderHeartBeatTime) > maxHeartBeatLeak * heartBeatTimeIntervalMs)) {logger.info("[{}][HeartBeatTimeOut] lastLeaderHeartBeatTime: {} heartBeatTimeIntervalMs: {} lastLeader={}", memberState.getSelfId(), new Timestamp(lastLeaderHeartBeatTime), heartBeatTimeIntervalMs, memberState.getLeaderId());changeRoleToCandidate(memberState.currTerm());}}}}

超过2个心跳的超时,follower就会进入candidate重新选举。

看下这段代码,作为不同的角色在实现会有不同的行为:

/*** The core method of maintainer. Run the specified logic according to the current role: candidate => propose a* vote. leader => send heartbeats to followers, and step down to candidate when quorum followers do not respond.* follower => accept heartbeats, and change to candidate when no heartbeat from leader.** @throws Exception*/private void maintainState() throws Exception {if (memberState.isLeader()) {maintainAsLeader();} else if (memberState.isFollower()) {maintainAsFollower();} else {maintainAsCandidate();}}

成为candidate 候选者的时候,就会投票选举leader,实现就在maintainAsCandidate()方法:

同意票数超过Quorum数量,就是投票通过,选举为leader。

raft的应用正如上面说的,比较多了:kafka, rocketmq dledger, etcd...

Zab

Zab的全称是Zookeeper Atomic Broadcast协议,听名字好像是专用于zookeeper的协议,目前我主要了解到的也是在zookeeper上的应用。

p.s. zab的读法,我目前听到过两个版本(zai bi)或者(za bi),至于哪个标准我也不知道。

zab是相对来说是属于非常强一致的协议了(zookeeper不算是最强一致性,业内好像是还有比zk更强一致性的实现)。CAP理论,如果有了解应该听说个,zookeeper是CP,集群中必须半数以上节点存在才可用,牺牲可用性来满足一致性。

下面以zookeeper来说明。

角色

  • Leader  集群通过投票选举出来的唯一节点,主要接受客户端对集群的写请求
  • Follower   参与投票选举leader,当客户端发起写请求的时候,处理leader的Proposal,leader在收到超过Quorum(大多数节点,zk是半数节点)的follower的accept的ack后,ledaer才会提交这条消息。follower其实跟leader保持数据同步。

在zookeeper中还个observer的节点角色,但是这个并参与选举和事务提交,所以在zab协议中不再提现。zookeeper的observer只是集群读性能的一种优化。

p.s. zookeeper集群的所有类型的节点都可以处理读请求,但是写请求都是转发给leader处理。

算法

在zab中事务提交时的数据同步术语好像不像是raft的复制而broadcast,我这里就叫做广播了。

在zab中有个zxid是一个自增值,叫做事务id,每次一条写请求可以看做是一个事务。

广播

  1. 客户端发一起提条数据更新的请求
  2. leader进行处理,发送 PROPOSE(zxid, data)(带着zxid和更新的数据)请求包给所有连接的follower
  3. follower收到请求会将数据同步到本地,然后返回一个ACK(zxid)数据包给leader
  4. leader收到quorum(超过半数)的节点的ack就发送一个提交请求COMMIT(zxid) 给所有的follower
  5. 响应客户端

选举

直接拿zookeeper的源码进行说明。源码在:zookeeper/FastLeaderElection.java at master · apache/zookeeper · GitHub

zookeeper投票的主要数据是(epoch,zxid,sid),epoch是zookeeper的选举轮次,这个数据会备份到数据目录下的一个文件(哪个目录下,具体我也记不清了);zxid就是上文提到的事务id,在数据目录下增量文件的名,sid是集群配置的server id,在配置文件中。

zookeeper的follower发起投票选举,如果谁收获大多数(Querum)的票就当选leader。

                        if (voteSet.hasAllQuorums()) {// Verify if there is any change in the proposed leader// 获得多数投票的时候还是等一会,看这期间是否会收到新的投票不// 因为可能有一些更符合leader条件的节点由于网络的原因,投票的请求传输慢了,其它节点收到的晚了while ((n = recvqueue.poll(finalizeWait, TimeUnit.MILLISECONDS)) != null) {if (totalOrderPredicate(n.leader, n.zxid, n.peerEpoch, proposedLeader, proposedZxid, proposedEpoch)) {recvqueue.put(n);break;}}/** This predicate is true once we don't read any new* relevant message from the reception queue*/if (n == null) {setPeerState(proposedLeader, voteSet);Vote endVote = new Vote(proposedLeader, proposedZxid, logicalclock.get(), proposedEpoch);leaveInstance(endVote);return endVote;}}

对当前收到的投票是否有效的判断逻辑,在totalOrderPredicate方法:

        return ((newEpoch > curEpoch)|| ((newEpoch == curEpoch)&& ((newZxid > curZxid)|| ((newZxid == curZxid)&& (newId > curId)))));

如果一个follower收到的票比自己的更有效(上面这个判断),设置这个票为自己的投票并重新投出去。

在选举出leader后,跟follower有一个数据同步的动作。具体说明感兴趣可以看一下:Zab1.0 - Apache ZooKeeper - Apache Software Foundation

总结

在raft之前,paxos使用的比较多。

基本的paxos,对于一次数据更新请求,集群各个节点可能要进行多轮消息交换,而raft因为必须要选择一个leader,通过leader只需要一轮消息交换。

raft论文描述了一个基于raft复制状态机的完整方案,paxos论文只给了一个一致性算法。

目前业内使用比较广泛的是raft。

zab协议是为zookeeper专门设计的支持崩溃恢复的原子广播协议,目前主要了解到的也是在zookeeper上的应用和实现。

快速理清Paxos、Zab、Raft协议相关推荐

  1. 2PC到3PC到Paxos到Raft到ISR

    序 本文主要讲述2PC及3PC,以及Paxos以及Raft协议. 两类一致性(操作原子性与副本一致性) 2PC协议用于保证属于多个数据分片上的操作的原子性.这些数据分片可能分布在不同的服务器上,2PC ...

  2. 超详细解析 | 一致性协议算法-2PC、3PC、Paxos、Raft、ZAB、NWR

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 来源:r6d.cn/VMW9 背景 在常见的分布式系统中, ...

  3. 一致性协议算法-2PC、3PC、Paxos、Raft、ZAB、NWR超详细解析

    背景 在常见的分布式系统中,总会发生诸如机器宕机或网络异常(包括消息的延迟.丢失.重复.乱序,还有网络分区)等情况. 一致性算法需要解决的问题就是如何在一个可能发生上述异常的分布式系统中,快速且正确地 ...

  4. 分布式系统协议Paxos、Raft和ZAB

    分布式系统协议Paxos.Raft和ZAB 文章转载自:https://zhuanlan.zhihu.com/p/147691282 首先得放在开头,分布式系统的一致性协议一直是分布式系统的难题,本人 ...

  5. Paxos、ZAB、RAFT协议

    这三个都是分布式一致性协议,ZAB基于Paxos修改后用于ZOOKEEPER协议,RAFT协议出现在ZAB协议之后,与ZAB差不多,也有很大区别. 1. Paxos 分布式节点分为3种角色, Prop ...

  6. 从paxos到raft zab,为何raft能够“独领风骚”

    文章目录 RAFT出现的缘由 RAFT 的实现 STATE MACHINE Log Replicated State Machine Leader Election 基本角色 关键变量 基本选举过程 ...

  7. 分布式系统概念 | 一致性协议:拜占庭将军问题、Paxos、Raft

    文章目录 拜占庭将军问题 Paxos 问题描述 执行过程 Prepare阶段 Accept阶段 Learner获取提案 活锁问题 Raft 状态机 执行流程 主节点选举 数据同步 拜占庭将军问题 拜占 ...

  8. ZAB、Raft协议简述

    CAP原则 1.一致性(C):在分布式系统中的所有数据备份,在同一时刻是否同样的值,即写操作之后的读操作,必须返回该值.(分为弱一致性.强一致性和最终一致性) 2.可用性(A):在集群中一部分节点故障 ...

  9. Paxos、Raft不是一致性算法/协议?

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作为互联网中的一员,我们时常沉浸在"分布式"的 ...

最新文章

  1. ITextHtml2canvas js截图 绘制 导出PDF
  2. 《机器学习》 第 5 章 神经网络
  3. JavaEE之使用DOM4J和XPath对xml文档的添加,删除,查询操作
  4. 我们都准备好进入数字货币+无现金世界了?
  5. 第四范式荣获“工业和信息化系统抗击新冠肺炎疫情先进集体”称号
  6. linux安装minikube(Ubuntu/deepin)
  7. torch.tensor().permute(2,1,0)
  8. vue(el-button的五种类型,三种css格式)
  9. spring专业术语了解
  10. suma在c语言中表示什么变量,C语言题
  11. 记一次ST-LINK维修及刷固件过程
  12. FOSRestBundle功能包:概述
  13. C++ 作用域与生命周期
  14. Zabbix(四):高级应用之--宏、网络发现测试实例
  15. 中国历代更改重复地名及其现实意义
  16. R730服务器内存扩展安装
  17. Android Studio editText去掉下划线的办法
  18. 积极的面对生活,勇敢的接受挑战,保持乐观的心态!
  19. 计算机控制课设串级回路,华北电力大学过程计算机控制课设DDC串级回路PID闭环.doc...
  20. iPhone更换字体教程,无需越狱,支持所有苹果设备!

热门文章

  1. Android 双卡双待支持检验SIM信息获取
  2. 小白学习爬虫的第三天之数据解析bs4与pyQuery的使用
  3. xin片设计的中的数学问题
  4. 计算机应用技术 快捷键,几个实用的电脑使用技巧和快捷键
  5. MATLAB真彩色图像转换为索引图
  6. 数学模型转化为计算机语言,程序设计语言类课程教学选题方法探讨
  7. 一分钟教会你黑白图片如何上色
  8. 两万字的Redis笔记!
  9. Xilinx Arch PCIE卡
  10. 【Python】创蓝253云通讯平台---国际短信API接口demo