区块链快速入门(三)——CFT(非拜占庭容错)共识算法

区块链快速入门(三)——CFT(非拜占庭容错)共识算法

1、CFT简介

CFT(Crash Fault Tolerance),即故障容错,是非拜占庭问题的容错技术。
Paxos 问题是指分布式的系统中存在故障(crash fault),但不存在恶意(corrupt)节点的场景(便可能消息丢失或重复,但无错误消息)下的共识达成问题,是分布式共识领域最为常见的问题。最先由Leslie Lamport用 Paxon 岛的故事模型来进行描述而得以命名。解决Paxos问题的算法主要有Paxos系列算法和Raft算法,Paxos算法和Raft算法都属于强一致性算法。node

2、Paxos算法

一、Paxos算法产生背景

在常见的分布式系统中,总会发生诸如机器宕机或网络异常(包括消息的延迟、丢失、重复、乱序,网络分区)等状况。Paxos算法须要解决的问题就是如何在一个可能发生异常的分布式系统中,快速且正确地在集群内部对某个数据的值达成一致,而且保证不论发生任何异常,都不会破坏整个系统的一致性。
区块链快速入门(三)——CFT(非拜占庭容错)共识算法
Paxos算法用于解决分布式系统的一致性问题。git

二、Paxos算法简介

1990年,Leslie Lamport 在论文《The Part-time Parliament》中提出了Paxos共识算法,在工程角度实现了一种最大化保障分布式系统一致性(存在极小的几率没法实现一致)的机制。Leslie Lamport做为分布式系统领域的早期研究者,由于相关成果得到了2013年度图灵奖。
Leslie Lamport在论文中将Paxos问题表述以下:
希腊岛屿Paxon上的执法者在议会大厅中表决经过法律(一次Paxos过程),并经过服务员(proposer)传递纸条的方式交流信息,每一个执法者会将经过的法律记录在本身的帐目上。问题在于执法者和服务员都不可靠,他们随时会由于各类事情离开议会大厅(服务器拓机或网络断开),并随时可能有新的执法者进入议会大厅进行法律表决(新加入机器),使用何种方式可以使得表决过程正常进行,且经过的法律不发生矛盾(对一个值达成一致)。 
Paxos过程当中不存在拜占庭将军问题(消息不会被篡改)和两将军问题(信道可靠)。
Paxos是首个获得证实并被普遍应用的共识算法,其原理相似两阶段提交算法,进行了泛化和扩展,经过消息传递来逐步消除系统中的不肯定状态。
做为后续不少共识算法(如 Raft、ZAB等)的基础,Paxos算法基本思想并不复杂,但最初论文中描述比较难懂,甚至连发表也几经波折。2001年,Leslie Lamport专门发表论文《Paxos Made Simple》进行从新解释,其对Paxos算法的描述以下:
Phase1
(a) A proposer selects a proposal number n and sends a prepare request with number n to a majority of acceptors.
(b) If an acceptor receives a prepare request with number n greater than that of any prepare request to which it has already responded, then it responds to the request with a promise not to accept any more proposals numbered less than n and with the highest-numbered pro-posal (if any) that it has accepted.
Phase 2
(a) If the proposer receives a response to its prepare requests (numbered n) from a majority of acceptors, then it sends an accept request to each of those acceptors for a proposal numbered n with a value v , where v is the value of the highest-numbered proposal among the responses, or is any value if the responses reported no proposals.
(b) If an acceptor receives an accept request for a proposal numbered n, it accepts the proposal unless it has already responded to a prepare request having a number greater than n.
Paxos算法目前在Google的Chubby、MegaStore、Spanner等系统中获得了应用,Hadoop中的ZooKeeper也使用了Paxos算法,但使用的算法是原始Paxos算法的改进算法。一般以Paxos算法为基础,在实现过程当中处理实际应用场景中的具体细节,能够获得一个Paxos改进算法。github

三、Paxos算法原理

Paxos算法的基本思路相似两阶段提交:多个提案者先要争取到提案的权利(获得大多数接受者的支持);成功的提案者发送提案给全部人进行确认,获得大部分人确认的提案成为批准的结案。
Paxos协议有三种角色:Proposer(提议者),Acceptor(决策者),Learner(决策学习者)。
Paxos 是一个两阶段的通讯协议,Paxos算法的基本流程以下:
区块链快速入门(三)——CFT(非拜占庭容错)共识算法
第一阶段Prepare:
A、Proposer生成一个全局惟一的提案编号N,而后向全部Acceptor发送编号为N的Prepare请求。
B、若是一个Acceptor收到一个编号为N的Prepare请求,且N大于本Acceptor已经响应过的全部Prepare请求的编号,那么本Acceptor就会将其已经接受过的编号最大的提案(若是有的话)做为响应反馈给Proposer,同时本Acceptor承诺再也不接受任何编号小于N的提案。
第二阶段 Accept
A、若是Proposer收到半数以上Acceptor对其发出的编号为N的Prepare请求的响应,那么Proposer就会发送一个针对[N,V]提案的Accept请求给半数以上的Acceptor。V就是收到的响应中编号最大的提案的value,若是响应中不包含任何提案,那么V由Proposer本身决定。
B、若是Acceptor收到一个针对编号为N的提案的Accept请求,只要该Acceptor没有对编号大于N的Prepare请求作出过响应,Acceptor就接受该提案。
Paxos 并不保证系统总处在一致的状态。但因为每次达成共识至少有超过一半的节点参与,最终整个系统都会获知共识结果。若是提案者在提案过程当中出现故障,能够经过超时机制来缓解。
Paxos 能保证在超过一半的节点正常工做时,系统总能以较大几率达成共识。算法

四、提案ID的生成算法

对于提案ID的选择,《Paxos made simple》中提到的是让全部的Proposer都从不相交的数据集合中进行选择。
Google的Chubby论文中给出提案ID的生成算法以下:假设有n个Proposer,每一个编号为promise

ir(0<=ir<n)

,Proposal编号的任何值s都应该大于它已知的最大值,而且知足:安全

s %n = ir    =>     s = m*n + ir

    Proposer已知的最大值来自两部分:Proposer本身对编号自增后的值和接收到Acceptor的拒绝后所获得的值。
以3个Proposer P一、P二、P3为例,开始m=0,编号分别为0,1,2。
1) P1提交的时候发现了P2已经提交,P2编号为1 >P1的0,所以P1从新计算编号:new P1 = 1*3+1 = 4
2) P3以编号2提交,发现小于P1的4,所以P3从新编号:new P3 = 1*3+2 = 5服务器

五、Paxos算法的活锁

若是两个提案者刚好依次提出更新的提案,则致使活锁,系统会永远没法达成共识(实际发生几率很小)。活锁没有产生阻塞,可是一直没法达成一致。
活锁有三种解决方案:
A、在被打回第一阶段再次发起PrepareRequest请求前加入随机等待时间。
B、设置一个超时时间,到达超时时间后,再也不接收PrepareRequest请求。
C、在Proposer中选举出一个leader,经过leader统一发出PrepareRequest和AcceptRequest。微信

六、Paxos算法异常处理

Paxos算法在执行过程当中会产生不少的异常状况:Proposer宕机,Acceptor在接收Proposal后宕机,Proposer接收消息后宕机,Acceptor在Accept后宕机,Learner宕机,存储失败等等。
为保证Paxos算法的正确性,Proposer、Aceptor、Learner都须要实现持久存储,以作到Server恢复后仍能正确参与Paxos处理。
    Proposer存储已提交的最大proposal编号、决议编号(instance id)。
    Acceptor存储已承诺(promise)的最大编号、已接受(Accept)的最大编号和Value、决议编号。
    Learner存储已学习过的决议和编号。网络

七、Paxos算法应用

Paxos算法只有两种状况下服务不可用:一是超过半数的Proposer异常,二是出现活锁。前者能够经过增长Proposer的个数来下降因为Proposer异常影响服务的几率,后者自己发生的几率就极低。
Paxos是分布式系统一致性协议的基础,其它的协议(raft、zab等)都是Paxos协议的改进版本。Paxos侧重理论,实现Paxos很是困难。
微信后台生产级Paxos类库PhxPaxos实现:
https://github.com/Tencent/paxosstore
https://github.com/tencent-wechat/phxpaxos
基于Paxos算法的改进算法的资料集合:
https://github.com/dgryski/awesome-consensusless

3、三军问题

一、三军问题简介

三军问题的描述以下:
1) 1支红军在山谷里扎营,在周围的山坡上驻扎着3支蓝军;
2) 红军比任意1支蓝军都要强大;若是1支蓝军单独做战,红军胜;若是2支或以上蓝军同时进攻,蓝军胜;
3) 三支蓝军须要同步他们的进攻时间;但他们唯一的通讯媒介是派通讯兵步行进入山谷,在那里他们可能被俘虏,从而将信息丢失;或者为了不被俘虏,可能在山谷停留很长时间;
4) 每支军队有1个参谋负责提议进攻时间;每支军队也有1个将军批准参谋提出的进攻时间;很明显,1个参谋提出的进攻时间须要得到至少2个将军的批准才有意义;
5) 问题:是否存在一个协议,可以使得蓝军同步他们的进攻时间?
三军问题符合Paxos问题场景,参谋和将军须要遵循一些基本的规则:
1) 参谋以两阶段提交(prepare/commit)的方式来发起提议,在Prepare阶段须要给出一个提议编号;
2) 在Prepare阶段产生冲突,将军以提议编号大小来裁决,提议编号大的参谋胜出;
3) 参谋在Prepare阶段若是收到将军返回的已接受进攻时间,在Commit阶段必须使用返回的进攻时间;

二、两参谋前后提议场景

区块链快速入门(三)——CFT(非拜占庭容错)共识算法
A、参谋1发起提议,派通讯兵带信给3个将军,内容为(编号1);
B、3个将军收到参谋1的提议,因为以前尚未保存任何编号,所以把(编号1)保存下来,避免遗忘;同时让通讯兵带信回去,内容为(ok);
C、参谋1收到至少2个将军的回复,再次派通讯兵带信给3个将军,内容为(编号1,进攻时间1);
D、3个将军收到参谋1的时间,把(编号1,进攻时间1)保存下来,避免遗忘;同时让通讯兵带信回去,内容为(Accepted);
E、参谋1收到至少2个将军的(Accepted)内容,确认进攻时间已经被你们接收;
F、参谋2发起提议,派通讯兵带信给3个将军,内容为(编号2);
G、3个将军收到参谋2的提议,因为(编号2)比(编号1)大,所以把(编号2)保存下来,避免遗忘;又因为以前已经接受参谋1的提议,所以让通讯兵带信回去,内容为(编号1,进攻时间1);
H、参谋2收到至少2个将军的回复,因为回复中带来了已接受的参谋1的提议内容,参谋2所以再也不提出新的进攻时间,接受参谋1提出的时间;

三、两参谋交叉提议场景

区块链快速入门(三)——CFT(非拜占庭容错)共识算法
1) 参谋1发起提议,派通讯兵带信给3个将军,内容为(编号1);
2) 3个将军的状况以下
A、将军1和将军2收到参谋1的提议,将军1和将军2把(编号1)记录下来,若是有其余参谋提出更小的编号,将被拒绝;同时让通讯兵带信回去,内容为(ok);
B、负责通知将军3的通讯兵被抓,所以将军3没收到参谋1的提议;
3) 参谋2在同一时间也发起了提议,派通讯兵带信给3个将军,内容为(编号2);
4) 3个将军的状况以下
A、将军2和将军3收到参谋2的提议,将军2和将军3把(编号2)记录下来,若是有其余参谋提出更小的编号,将被拒绝;同时让通讯兵带信回去,内容为(ok);
B、负责通知将军1的通讯兵被抓,所以将军1没收到参谋2的提议;
5) 参谋1收到至少2个将军的回复,再次派通讯兵带信给有答复的2个将军,内容为(编号1,进攻时间1);
6) 2个将军的状况以下
A、将军1收到了(编号1,进攻时间1),和本身保存的编号相同,所以把(编号1,进攻时间1)保存下来;同时让通讯兵带信回去,内容为(Accepted);
B、将军2收到了(编号1,进攻时间1),因为(编号1)小于已经保存的(编号2),所以让通讯兵带信回去,内容为(Rejected,编号2);
7) 参谋2收到至少2个将军的回复,再次派通讯兵带信给有答复的2个将军,内容为(编号2,进攻时间2);
8) 将军2和将军3收到了(编号2,进攻时间2),和本身保存的编号相同,所以把(编号2,进攻时间2)保存下来,同时让通讯兵带信回去,内容为(Accepted);
9) 参谋2收到至少2个将军的(Accepted)内容,确认进攻时间已经被多数派接受;
10) 参谋1只收到了1个将军的(Accepted)内容,同时收到一个(Rejected,编号2);参谋1从新发起提议,派通讯兵带信给3个将军,内容为(编号3);
11) 3个将军的状况以下
A、将军1收到参谋1的提议,因为(编号3)大于以前保存的(编号1),所以把(编号3)保存下来;因为将军1已经接受参谋1前一次的提议,所以让通讯兵带信回去,内容为(编号1,进攻时间1);
B、将军2收到参谋1的提议,因为(编号3)大于以前保存的(编号2),所以把(编号3)保存下来;因为将军2已经接受参谋2的提议,所以让通讯兵带信回去,内容为(编号2,进攻时间2);
C、 负责通知将军3的通讯兵被抓,所以将军3没收到参谋1的提议;
12) 参谋1收到了至少2个将军的回复,比较两个回复的编号大小,选择大编号对应的进攻时间做为最新的提议;参谋1再次派通讯兵带信给有答复的2个将军,内容为(编号3,进攻时间2);
13) 将军1和将军2收到了(编号3,进攻时间2),和本身保存的编号相同,所以保存(编号3,进攻时间2),同时让通讯兵带信回去,内容为(Accepted);
14) 参谋1收到了至少2个将军的(accepted)内容,确认进攻时间已经被多数派接受;

4、Multi-Paxos算法

一、Multi-Paxos算法简介

Paxos是对一个值达成一致,Multi-Paxos是连续多个Paxos instance来对多个值达成一致。Multi-Paxos协议中有一个Leader。Leader是系统中惟一的Proposal,在lease租约周期内全部提案都有相同的ProposalId,能够跳过prepare阶段,议案只有accept过程,一个ProposalId能够对应多个Value,因此称为Multi-Paxos。
Multi-Paxos协议是经典的Paxos协议的简化版本,将原来2-Phase过程简化为了1-Phase,从而加快了提交速度。Multi-Paxos要求在各个Proposer中有惟一的Leader,并由Leader惟一地提交value给各Acceptor进行表决,在系统中仅有一个Leader进行value提交的状况下,Prepare的过程就能够被跳过,而Leader的选举则能够由Paxos Lease来完成。

二、PaxosLease

在Paxos算法中,若是可以选举出一个leader,那么有助于提升投票的成功率。另外leader在多个决议的选举中有很重要的做用(用于获得决议的连续id)。所以,如何经过某种方法获得一个leader就是PaxosLease所说明的。
Master选举的过程以下:从众多的Node中选择一个做为Master,若是该Master 一直 alive则无需选举,若是master crash,则其余的node进行选举下一个master。选择正确性的保证是:任什么时候刻最多只能有一个master。
逻辑上Master更像一把无形的锁,任何一个节点拿到这个锁,均可成为master,因此本质上Master选举是个分布式锁的问题,但彻底靠锁来解决选举问题也是有风险的:若是一个Node拿到锁,而后crash,会致使锁没法释放,即死锁。一种可行的方案是给锁加个时间(Lease),拿到锁的Master只能在Lease有效期内访问锁定的资源,在Lease timeout后,其余Node能够继续竞争锁,从根本上避免了死锁。
Master在拿到锁后,若是一直alive,能够向其余node”续租“锁,从而避免频繁的选举活动。

5、Raft算法

一、Raft算法简介

Paxos 算法的设计并无考虑到一些优化机制,同时论文中也没有给出太多实现细节,所以后来出现了很多性能更优化的算法和实现,包括Fast Paxos、Multi-Paxos 等。
Raft算法由斯坦福大学的Diego Ongaro和John Ousterhout 2013年在论文《In Search of an Understandable Consensus Algorithm》中提出。Raft算法面向对多个决策达成一致的问题,是对Multi-Paxos的从新简化设计和实现,分解了领导者选举、日志复制和安全方面的考虑,并经过约束减小了不肯定性的状态空间。
Raft算法将一致性问题分解为领导选举(leader election)、日志复制(log replication)、安全性(safety)三部分。

二、Raft算法角色

Raft算法包括领导者(Leader)、候选者(Candidate)和跟随者(Follower)三种角色,决策前经过选举一个全局的领导者来简化后续的决策过程。领导者决定日志(log)的提交,日志只能由领导者向跟随者单向复制。
Leader:集群中只有一个处于Leader状态的服务器,负责响应全部客户端的请求。
Follower:刚启动时全部节点为Follower状态,响应Leader的日志同步请求,响应Candidate的请求。
Candidate:Follower状态服务器准备发起新的Leader选举前须要转换到的状态,是Follower和Leader的中间状态。
区块链快速入门(三)——CFT(非拜占庭容错)共识算法
全部节点初始状态都是Follower角色
超时时间内没有收到Leader的请求则转换为Candidate进行选举
Candidate收到大多数节点的选票则转换为Leader;发现Leader或者收到更高任期的请求则转换为Follower
Leader在收到更高任期的请求后转换为Follower

三、Term(任期)

Term(任期):每一个Leader都有本身的任期,任期到期就须要开始新一轮选举,在每一个任期内,能够没有leader,可是不能出现大于两个的leader。
Raft算法将整个系统执行时间划分为若干个不一样时间间隔长度的Term(任期)构成的序列,以递增的数字来做为Term的编号;每一个Term由Election开始,在任期内若干处于Candidate状态的服务器竞争产生新的Leader,若是一个候选人赢得了选举,就会在该任期的剩余时间担任领导人;在某些状况下,选票会被瓜分,有可能没有选出领导人,那么将会开始另外一个任期,而且马上开始下一次选举。Raft算法保证在给定的一个任期最多只有一个领导人。
区块链快速入门(三)——CFT(非拜占庭容错)共识算法

四、Raft算法的流程

Raft算法典型的过程包括两个主要阶段:
(1)Leader选举
当整个系统启动时,全部服务器都处于Follower状态;若是系统中存在Leader,Leader会周期性的发送心跳(AppendEntries RPC)来告诉其它服务器本身是Leader;若是Follower通过一段时间没有收到任何心跳信息,则能够认为Leader不存在,须要进行Leader选举。
在选举前,Follower增长其Term编号并改变状态为Candidate状态,而后向集群内的其它服务器发出RequestVote RPC,Candidate状态持续到发生下面三个中的任意事件:
A、赢得选举:Candidate接受了大多数服务器的投票,成为Leader,而后向其它服务器发送心跳(AppendEntries RPC)告诉其它服务器。
B、其它服务器得到选举:Candidate在等待的过程当中接收到自称为Leader的服务器发送来的心跳(AppendEntries RPC),若是RPC的Term编号大于等于Candidate自身的Term编号,则Candidate认可Leader,自身状态变成Follower;不然拒绝认可Leader,状态依然为Candidate。
C、一段时间事后,若是没有新的Leader产生,则Term递增,从新发起选举(由于有可能同时有多个Follower转为Candidate状态,致使分流,都没有得到多数票)。
(2)日志复制
Log复制主要做用是用于保证节点的一致性,所作的操做是为了保证一致性与高可用性;当Leader选举出来后便开始负责客户端的请求,全部请求都必须先通过Leader处理。Leader接受到客户端请求后,将其追加到Log的尾部,而后向集群内其它服务器发出AppendEntries RPC,引起其它服务器复制新请求的操做,当大多数服务器复制完后,Leader将请求应用到内部状态机,并将执行结果返回给客户端。
区块链快速入门(三)——CFT(非拜占庭容错)共识算法
每一个Log中的项目包含2个内容:操做命令自己和Term编号;还有一个全局的Log Index来指示Log项目在Log中的顺序编号。当大多数服务器在Log中存储了Log项目,则可认为Log项目是能够提交的,上图中的Log Index为7前的Log项目均可以提交。
(3)安全性
安全性是用来保证每一个节点都执行相同序列的安全机制,如当某个Follower在当前Leader提交命令时不可用,稍后可能该Follower又会被选举为Leader,这时新Leader可能会用新的Log覆盖先前已提交的Log,这就是致使节点执行不一样序列;安全性就是用于保证选举出来的Leader必定包含先前已经提交Log的机制。
为了达到安全性,Raft算法增长了两个约束条件:
A、要求只有其Log包含了全部已经提交的操做命令的那些服务器才有权被选为Leader。
B、对于新Leader来讲,只有它本身已经提交过当前Term的操做命令才被认为是真正的提交。

五、Raft算法的应用

Raft算法已经被多种语言实现,如Go语言、C++、Python等主流开发语言。
Raft算法原理动画演示:
http://thesecretlivesofdata.com/raft/
Raft共识算法资源以下:
https://raft.github.io/
Raft算法的Go语言实现:
https://github.com/goraft/raft

相关文章
相关标签/搜索