Paxos算法

Paxos 算法是莱斯利·兰伯特(Leslie Lamport)1990 年提出的一种基于消息传递的、具有高容错性的一致性算法。Google Chubby 的作者 Mike Burrows 说过,世上只有一种一致性算法, 那就是 Paxos,所有其他一致性算法都是 Paxos 算法的不完整版。

Paxos 算法是一种公认的晦涩难懂的算法,并且工程实现上也具有很大难度。

所以 Paxos算法主要用来解决我们的分布式系统中如何根据表决达成一致。

算法前置理解

首先需要理解的是算法中的三种角色

  • Proposer(提议者)

  • Acceptor(决策者)

  • Learners(群众)

一个提案的决策者(Acceptor)会存在多个,但在一个集群中提议者(Proposer)也是可能存在多个的,不同的提议者(Proposer)会提出不同的提案。

paxos算法特点:

  • 没有提案被提出则不会有提案被选定。

  • 每个提议者在提出提案时都会首先获取到一个具有全局唯一性的、递增的提案编号 N, 即在整个集群中是唯一的编号N,然后将该编号赋予其要提出的提案。(在zookeeper中就是zxid,由epoch 和xid组成)

  • 每个表决者在 accept 某提案后,会将该提案的编号N 记录在本地,这样每个表决者中保存的已经被 accept 的提案中会存在一个编号最大的提案,其编号假设为 maxN。每个表决者仅会 accept 编号大于自己本地maxN 的提案。

  • 在众多提案中最终只能有一个提案被选定。

  • 一旦一个提案被选定,则其它服务器会主动同步(Learn)该提案到本地。

Paxos算法整个选举的过程可以分为两个阶段来理解。

阶段一

这个阶段主要是准备阶段发送提议

  • 提议者(Proposer)准备提交一个编号为 N 的提议,于是其首先向所有表决者(Acceptor)发送 prepare(N)请求,用于试探集群是否支持该编号的提议。

  • 每个决策者(Acceptor)中都保存着自己曾经 accept 过的提议中的最大编号 maxN。当一个表决者接收到其它主机发送来的 prepare(N)请求时,其会比较 N 与 maxN 的值。

    • 若 N 小于 maxN,则说明该提议已过时,当前表决者采取不回应来拒绝该 prepare 请求

    • 若N 大于maxN,则说明该提议是可以接受的,当前表决者会首先将该 N 记录下来, 并将其曾经已经 accept 的编号最大的提案 Proposal(myid,maxN,value)反馈给提议者, 以向提议者展示自己支持的提案意愿。其中第一个参数 myid 表示表决者 Acceptor 的标识 id,第二个参数表示其曾接受的提案的最大编号 maxN,第三个参数表示该提案的真正内容 value。

      若当前表决者还未曾 accept 过任何提议(第一次初始化的时候),则会将Proposal(myid,null,null)反馈给提议者。

    • 在当前阶段 N 不可能等于maxN。这是由 N 的生成机制决定的。要获得 N 的值, 其必定会在原来数值的基础上采用同步锁方式增一

阶段二

当前阶段要是真正的发送接收阶段又被称为Accept阶段

  • 当提议者(Proposer)发出 prepare(N)后,若收到了超过半数的决策者(Accepter)的反馈, 那么该提议者就会将其真正的提案 Proposal(N,value)发送给所有的表决者。

  • 当决策者(Acceptor)接收到提议者发送的 Proposal(N,value)提案后,会再次拿出自己曾经accept 过的提议中的最大编号 maxN,及曾经记录下的 prepare 的最大编号,让 N 与它们进行比较,若N 大等于于这两个编号,则当前表决者 accept 该提案,并反馈给提议者。若 N 小于这两个编号,则决策者采取不回应来拒绝该提议。

  • 若提议者没有接收到超过半数的表决者的 accept 反馈,则重新进入 prepare 阶段,递增提案号,重新提出 prepare 请求。若提议者接收到的反馈数量超过了半数,则其会向外广播两类信息:

    • 向曾 accept 其提案的表决者发送“可执行数据同步信号”,即让它们执行其曾接收到的提案

    • 向未曾向其发送 accept 反馈的表决者发送“提案 + 可执行数据同步信号”,即让它们接受到该提案后马上执行。

假设现在我们有三台主机服务器从中选取leader(也可以选择其他的更多的服务器,为了比较方便容易理解这里选少一点)。所以这三台主机它们就分别充当着提议者(Proposer)决策者(Acceptor)Learners(群众)三种角色。

所以假设现在开始模拟选举,三台服务分别开始获取N(具有全局唯一性的、递增的提案编号 N)的值,此时 serverOne(主机1) 就对应这个 ProposerOne(提议者1)、serverTwo(主机2)对应ProposerTwo(提议者2)、serverThree(主机3)对应ProposerThree(提议者3)。

为了整个流程比较简单清晰,过程中更好理解。他们的初始N值就特定的设置为 ServerOne(2)、ServerTwo(1)、ServerThree(3),所以他们都要发送给提议(Proposal)给决策者(Acceptor),让它们进行表决确定

名词解析

提议(Proposal):提议者向决策者发送的中间数据的包装简称提议。

同时每个 提议者(Proposer)向其中的两个决策者(Acceptor)发送提案消息。所以假设

ProposerOne(提议者1)向 AcceptorOne(决策者1)和AcceptorTwo(决策者2)、

ProposerTwo(提议者2)向AcceptorTwo(决策者2)和AcceptorThree(决策者3)、

ProposerThree(提议者3)向AcceptorTwo(决策者2)和AcceptorThree(决策者3)、

发送提案消息。为了流程结构简单就向其中的2台发送提案,但是也是已经超过半票了,当然也可以多选几个主机,多发送提案,只是流程就复杂了一点不好理解了。注意点就是一定要超过半票。

那么整个图可以如下所示:

图片

所以根据上面的图开始走第一阶段

按照上面我们假设的流程开始执行流程

ProposerOne(提议者1)向 AcceptorOne(决策者1)和AcceptorTwo(决策者2)

AcceptorOne(决策者1)和AcceptorTwo(决策者2)第一次收到ProposerOne(提议者1)的提议(Proposal),由于是第一次收到提议(Proposal),本地没有存储最大的N值,所以都会接受ProposerOne(提议者1)的提议。

所以AcceptorOne(决策者1)和AcceptorTwo(决策者2)都会提议返回给ProposerOne(提议者1)告知我赞同你的提议。

同时AcceptorOne(决策者1)和AcceptorTwo(决策者2)因为收到的当前的最大提议编号N为2,并且保存在本地,所以想接受到其他的N值小于2时则不会回复提议。

而ProposerOne(提议者1)已经收到超过半数返回,所以提议通过

此时 :

  • AcceptorOne(决策者1)本地 N值为2

  • AcceptorTwo(决策者2) 本地 N值为2

  • AcceptorThree(决策者3)本地 N值为null

ProposerTwo(提议者2)向AcceptorTwo(决策者2)和AcceptorThree(决策者3)

AcceptorTwo(决策者2)和AcceptorThree(决策者3)收到ProposerTwo(提议者2)的提议(Proposal)时。因为AcceptorTwo(决策者2)之前已经接受过ProposerOne(提议者1)的提议,所以本地的N值已经存储了2

当ProposerTwo(提议者2)的N值为1的时候,小于本地存的最大N值,所以不给予通过,也就不回复ProposerTwo(提议者2)

而AcceptorThree(决策者3)因为这是第一次收到提议,没有最大N值,所以同意提议(Proposal),返回当前提,更新本地N值。

最后ProposerTwo(提议者2)只收到AcceptorThree(决策者3)的同意反馈,没有超过半数选择,所以不给通过

此时 :

  • AcceptorOne(决策者1)本地 N值为2

  • AcceptorTwo(决策者2) 本地 N值为2

  • AcceptorThree(决策者3)本地 N值为1

ProposerThree(提议者3)向AcceptorTwo(决策者2)和AcceptorThree(决策者3)

AcceptorTwo(决策者2)和AcceptorThree(决策者3)收到ProposerThree(提议者3)的提议(Proposal)时。因为

AcceptorTwo(决策者2)和AcceptorThree(决策者3)都已经都到过提议(Proposal),所以AcceptorTwo(决策者2)收到ProposerThree(提议者3)的提议时,本地N值2小于ProposerThree(提议者3)的N值3,所以通过提议

AcceptorThree(决策者3)因为本地之前收到最大的值为1,所以本次通过也通过提议,更新本次的N值为3

最后ProposerThree(提议者3)收到超过半数的同意反馈,所以通过

此时 :

  • AcceptorOne(决策者1)本地 N值为2

  • AcceptorTwo(决策者2) 本地 N值为3

  • AcceptorThree(决策者3)本地 N值为3

由于之前ProposerTwo(提议者2)向AcceptorTwo(决策者2)和AcceptorThree(决策者3)发出提议时,没有超过半数投票。所以会从新获取最大N值(具有全局唯一性的、递增的提案编号 N),这个时候ProposerTwo(提议者2)本地获取的N值为4所以会再次发起一轮投票

AcceptorTwo(决策者2)和AcceptorThree(决策者3)再次收到ProposerTwo(提议者2)的提议(Proposal)时。AcceptorTwo(决策者2)和AcceptorThree(决策者3)本地存储的最大N值都小于现在最新的ProposerTwo(提议者2)的N值4,所以全部通过返回提议,更新本地N值

当ProposerTwo(提议者2)的N值为1的时候,小于本地存的最大N值,所以不给予通过,也就不回复ProposerTwo(提议者2)

最后ProposerTwo(提议者2)收到超过半数的同意反馈,所以通过

此时 :

  • AcceptorOne(决策者1)本地 N值为2

  • AcceptorTwo(决策者2) 本地 N值为4

  • AcceptorThree(决策者3)本地 N值为4

到此第一阶段的工作就已经完成了,整个流程都是文字较多,看起需要多看几遍。同时我也给大家画了一个流程图如下:

图片

由于上面已经走完第一阶段,那么接下来肯定就是第二阶段的流程了

同时整体第二阶段可以分为两块来理解,第一块是正式提交提议,第二块是表决确定阶段

第一阶段执行完得到的结果:

  • Proposer

    • ProposerOne(提议者1) 本地N值为2

    • ProposerTwo(提议者2) 本地N值为4

    • ProposerThree(提议者3) 本地N值为3

  • Acceptor

    • AcceptorOne(决策者1) 本地N值为2

    • AcceptorTwo(决策者2) 本地N值为4

    • AcceptorThree(决策者3) 本地N值为4

第一块:

  • ProposerOne(提议者1)正式发出提议到AcceptorOne(决策者1)和AcceptorTwo(决策者2),通过第一阶段的结果可以知道只有AcceptorOne(决策者1)表决通过,AcceptorTwo(决策者2)不通过因为小于本地N值

  • ProposerTwo(提议者2)正式发出提议到AcceptorTwo(决策者2)和AcceptorThree(决策者3),同样的通过第一阶段的结果,可以知道两个决策者都通过,所以超过半数投票

  • ProposerThree(提议者3)正式发出提议到AcceptorTwo(决策者2)和AcceptorThree(决策者3),同样的通过第一阶段的结果,可以知道两个决策者都没有通过

第二块:

从上面的第一块结果来看,只有**ProposerTwo(提议者2)**得到半数同意,所以ProposerTwo(提议者2)立马就能成为leader。至此选举状态就结束,即ProposerTwo(提议者2)会发布广播给所有的learner,通知它们过来同步数据。当数据完成同步时,那个整个服务器的集群就能正常工作了。

总结

整个Paxos算法过程还是比较难理解,为了讲明白这里面的流程都是按最简单的例子来的。这里面也可以有更多的机器发起更多的提议。但是整个流程那就更难理解了。

理解Paxos算法需要按上面的两个阶段来理解。第一阶段是做什么,得到了什么结果,第二阶段又是基于第一阶段的结果执行怎样的一个选举流程,这个是大家需要思考的重点。

这里主要是跟大家分享的是Paxos算法这个选举过程,也有很多其他的优化版本比如 Fast PaxosEPaxos等等。

Zookeeper

在zookeeper中的选举算法就是用的 Fast Paxos算法,为什么用Fast paxos?

Fast Paxos算法是Paxos的优化版本,解决了Paxos算法的活锁问题保证每次线程过来获取到唯一的N值。

ZAB(Zookeeper Atomic BroadCast)原子广播协议

ZAB其实就是上面算法的一种实现,所以Zookeeper也就是依赖ZAB来实现分布式数据的一致性的。

所以在zookeeper中,只有一台服务器机器作为leader机器,所以当客户端链接到机器的某一个节点时

  • 当这个客户端提交的是读取数据请求,那么当前连接的机器节点,就会把自己保存的数据返回出去。

  • 当这个客户端提交的是写数据请求时,首先会看当前连接的节点是不是leader节点,如果不是leader节点则会转发出去到leader机器的节点上,由leader机器写入,然后广播出去通知其他的节点过来同步数据

在ZAB中的三类角色

  • Leader:ZK集群的老大,唯一的一个可以进行写数据的机器。

  • Follower:ZK集群的具有一定职位的干活人。只能进行数据的读取,当老大(leader)机器挂了之后可以参与选举投票的机器。

  • Observe:最小的干活小弟,只能进行数据读取,就算老大(leader)机器挂了,跟他一毛关系没有,不能参与选举投票的机器。

在ZAB中的三个重点数据

  • Zxid:是zookeeper中的事务ID,总长度为64位的长度的Long类型数据。其中有两部分构成前32位是epoch后32位是xid

  • Epoch:每一个leader都会有一个这个值,表示当前leader获取到的最大N值,可以理解为“年代”

  • Xid:事务ID,表示当前zookeeper集群当前提交的事物ID是多少(watch机制),方便选举的过程后不会出现事务重复执行或者遗漏等一些特殊情况。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值