分布式一致性Paxos算法学习笔记(三):算法的工程化描述

前两篇学习笔记给出paxos算法较为详细的文字描述。如果对算法的两个阶段四个过程的定义还不太了解可以点击这里,这段文字出自夏超伦的论文,描述在算法的两个阶段里proposer、acceptor、learner三个角色需要完成什么工作。可惜小夏同学并没有描述得很详细,我在编码实现的时候发现有很多重要的细节没有提到,proposer pick a value这么重要的过程都没有写.........感觉工程价值不大(自我感觉良好^_^),所以当时就打算要写篇详细的文档记录编码时遇到的问题,废话就不说了,下面就是Paxos算法(basic paxos)的工程化描述。

 一、基本算法 

1.1  基本角色及该角色至少维护的数据 

    Proposer(Coordinator):【crnd,cval】初始化【0,NULL】。在lamportfast paxos[1]一文中描述的角色中,proposer代表客户端用以提出请求,coordinator代表提出提案的真正角色。但为了和其他文献[2]中的角色介绍兼容(俗称向下兼容^_^),本文决定用proposer来表示提出提案的角色。 

    Acceptor:【rnd,vrnd,vval】,初始化【00NULL 

    Learner:学习value,开始执行任务。

    在最初的算法中,每个角色都有多个并且相同角色之间互相平等。

1.2  变量定义

    ● Instance id:实例编号

    ● crnd:P起过的最高的提案编号

    ● cval:P在提案编号为crndround中提出的Value 

    ● rnd:Round number,某Acceptor参加过的最新的提案编号

    ● vrnd: value_round number,某Acceptor最新批准过的提案编号

    ● vval:Acceptor最新批准过的Value值。

    ● Value:客户端的具体请求:命令或者其他

    ● Quorum: (a majority of)大多数的acceptor  

1.3 众所周知的两个阶段 

    Phase1: 包括prepare promise or reject(可选)

Phase2: 包括accept  accepted or Nack(可选)

一个round(一个paxos算法的执行过程)包括Phase1Phase2两个阶段。     

  

1.4 补充说明

Proposer可以不断提出新的提案,而不必等待之前的所有提案都被批准。

这里假设没有拜占庭将军问题(Byzantine failure),即虽然有可能一个消息被传递了两次,但是绝对不会出现错误的消息;只要等待足够的时间,消息就会被传到。即消息可能延时或者丢失(等待无限长的时间),但是不会出错。

   

1.5 算法描述

一个实现paxos算法的系统中可能有多个实例(instance),一般来说,一个实例处理一个来自客户端的命令请求(value),各个实例之间可以同时运行而不互相影响。处理一个实例时,可能包括了一个或者多个round,即每一个实例都需要运行一次一致性算法。

    下面将讨论某一个实例运行时可能出现的各种情况,值得一提的是,round number被定义为提案编号,且所有的提案编号之间必须是一个偏序关系(例如将序数和 proposer 的名字拼接起来),为了避免歧义,设序数为M,每个proposer都有一个proposer_id,则某个prposer_id1proposer所提出的提案中,初始化的提案编号为M+proposer_id。在这里,为了说明简单,我们令M = 10,则proposer_id = 1proposer提出的提案中提案编号被初始化为11 

  1.5.1 关键过程中必须遵循的原则

  Aproposer pick a value原则(phase 2a 消息中如何选择提案value的原则) 

      收到来自某quorumphase  1brnd,vrnd,vval】消息回复。令K为来自该quorum消息中所有vrnd的最大值,V为来自该quorum的消息中vrnd = k的消息对应的vval值的集合(当k>0,fast paxos在文中证明了这样的vval值只有一个)。

       If k = 0,P可以任意挑选一个来自clientvalue作为提案提出

       else,将V中的那个唯一的value作为提案提出

  Bproposer原则

      在提案编号为i (rnd = i)的提案没有被大多数acceptor(某quorum)批准之前,若收到某ANackj】或者rejectj,j>i,说明该实例中已经有更改编号的提案发生,应设置crnd = h+M,(提案编号的递增可以自己定义)并重复prepare过程。

  Cacceptor原则

◆ 任何一个acceptor必须批准它收到的第一个value

    ◆ 如果某acceptor在处理某proposer(proposer_id = x)proposer提出的提案编号为i的提案过程中,收到了来自其他proposer(proposer_id = y)的编号更高的提案消息preparej(j>i),则该acceptor利用rejectj】或者Nackj】提醒proposer(proposer_id = x)有更高编号的提案发生,中断原有的round i。如果x= y ,即同一个proposesr提出了更高编号的提案消息(preparej】),则acceptor接受编号更高j的提案消息(promisej),忽略提案编号为i的提案消息(acceptI,value)

1.5.2 一个简单提案提出(round)运行流程(不考虑被中断或被忽略)

  ① 收到来自客户端的请求

  ② 假设proposer_id1proposer开始提出一个提案来处理这个来自客户端的请求,

  ③ 该proposer发出phase 1a 消息,内容至少含有【crnd,prepare11,自身二元组更新为【11NULL】。

  ④ 如果是初始化状态下,则每个acceptor都将原有的三元组结构更新为【110NULL】,表示该acceptor不再处理提案编号小于11的其它提案。并且回promise11NULL】给proposer_id = 1如果是非初始化状态下(vval不为NULL),每个acceptor回复promise11vval】给proposer_id = 1,即回复phase 1b消息。

  ⑤ 如果该proposer_id收到来自大多数(a quorum of)的acceptor回复的promise消息。它按照1.5.1pick a value的原则选择适当的value值,记做vval_s,并组成一个内容至少包含【11vval_s】的accept phase 2a 消息发给所有的acceptor。并将自身的二元组更新为【11,vval_s

  ⑥ 如果是初始状态,收到accept消息的所有acceptor接受该提案,并发送投票结果给proposer和所有learner。非初始状态,在简单模型下,所有的acceptor接受该提案,并发送结果给proposer和所有learner。将自身的三元组更新为【1111vval_s】。

  ⑦ 如果某Learner收到来自收到来自大多数(a quorum of)的acceptoraccepted,至少含有【11,vval_s】消息。该learner学习vval_s 

  1.5.3 考虑中断和忽略 

  根据1.5.1acceptor的原则可知,一个round有可能被中断和被忽略。现在举例来说明:

 

    A、 1.5.2描述的模型中,如果在步骤④到步骤⑥之间,另一个proposer(比如proposer_id = 2,则它的初始化提案编号为12)开始了步骤③,传送了一个至少包含【12】的prepare消息,并且由于网络传输的不确定性,【12】比【11】更早的抵达某个acceptor,则当【11】抵达时,由于两个prepare不是由同一个proposer提出,这个acceptor将会显式地拒绝这个prepare消息。并给proposer_id = 1proposer发送reject12】(或者nack12】)消息。则【11】被中断了。 

    B、 如果在【11】编号的提案的表决之前,proposer_id = 1proposer又提出一个新的prepare消息(这是被允许的),这个新提案的编号可能为【21】(本例中,举例新编号的递增差值是M,则11+M = 21)。并且【11】的消息在网络传输中丢失了,那么当某acceptor先收到【12】,再收到【11】时,由于这两个prepare都是由同一个proposer提出,那么该acceptor将直接忽略消息【11】。 

   

二、发生活锁

1.5.3所述的情况A所示,如果两个proposer同时提出决议,并且互相中断,将会形成活锁。在参考文献[3]。

三、选举leader

为了解决活锁的问题,lamportfast paxos文章中推荐使用竞争leader的方式来进行算法。在这种情况下,只有确认自己成为了leaderproposer才能开始一个新的提案。就避免了被别人中断的情况发生,从而防止了相互中断形成活锁。    

四、算法过程的动画演示 

     在公司做paxos专题的时候做了一个带动画的ppt,演示basic paxos算法的基本过程,感兴趣可以在这里下载

五、后续:“分布式一致性Paxos算法学习笔记(四):算法回顾

     笔记四将对算法进行一次回顾。

 

References

[1]Leslie Lamport.Fastpaxos. DistributedComputing,19(2):79–103,2006.

[2]Leslie Lamport. Paxos made simple.2001,11,1 

[3]http://en.wikipedia.org/wiki/Paxos_algorithm

 特别声明:本篇文章的第一作者是我的师妹yz同学,本文对paxos算法的研究由我俩共同完成,而本文主要内容及文中提到的ppt由yz同学完成。本人为本文的第二作者。如有异议请参考相关法规联系我。

 

以下文字出自小夏同学的论文

2.1.3 P roposer行为分析 

(1.1) 向所有的Acceptors发送Prepare(N_A)请求;

(1.2) 如果收到Reject(N_H)信息,那么重新发送Prepare(N_H+1)

(2.1) 如果收到Acceptors集合的任意一个MajorityPromise(N_A, V_A)回复,那么如果所有的V_A均为空,Proposer自由选取一个V_A’,回发Accept(N_A, V_A);否则回发Accept(N_A, V_i)

(2.2) 如果收到Nack(N_H),回到(1.1)过程,发送Prepare(N_H+1)

(3.1) 如果收到任意一个Majority所有成员的Accepted信息(表明选举完成),向所有Proposers发送自身成为leader的消息;

(3.2) Learner发送value值。其中:

N_A为该次提案的编号;

N_H为当前提案的最高编号;

V_iV_A中提案编号最高的value

2.1.4 A cceptor行为描述 

(1.1)接收Prepare(N_A),如果N_A>N_H,那么回复Promise(N_A, V_A),并置N_H=N_A;否则回复Reject(N_H)

(2.1)接收Accept(N_A, V_A),如果N_A<N_H,那么回复Nack(N_H)信息(暗示了该Proposer提完案后至少有一个其余的Proposer广播了具有更高编号的提案);否则设置this.V_A=V_A,并且回复Accepted信息。其中:

Promise(N_A, V_A):向Proposer保证不再接受编号不大于N_H的提案;

AcceptedProposer发送提案被通过信息;

V_AAcceptor之前审批过的提案(允许为空);

N_HAcceptor之前接收提案的最高编号。

2.1.5 Learner行为描述 

相对来说,Learner的行为理解更简单一些:学习value,开始执行任务。

2.1.6 整体描述 

算法在执行过程中,实例和实例之间是异步的;角色和角色之间既有同步的行为(因为一个完全异步的系统中,一致性问题将是无法被解决的[6]),又有异步的行为。下表2.1中按照我的理解以全局的角度来说明这其中的关系:     

                                     表2.1 一次Paxos实例

角色 / 时段

Phase 1

Phase 2

Phase 3

Proposer (P)

[1]竞争Leader: A发送prepare

[1]接收Apromise

[2]选取一个value并发送accept

(*)

Acceptor (A)

[1]接收处理Pprepare

[2]回复reject promise

[1]接收处理accept

[2]回复acceptednack

[3]通知L

(*)

Learner (L)

[1]接收广播学习value

[2]创建Proposer对象学习value

你可能感兴趣的:(学习笔记)