Raft保证当复制状态机数量为3f+1时, 最多能够容许f个状态机虚假。
一个view中只有一个primary 其余为副本。
视图更改说明primary崩溃或失败。算法
算法对节点的要求:安全
客户端经过发送消息 <REQUEST,o,t,c>
到primary请求状态机执行操做o。
t:时间戳用于确保该操做只执行一次,而且全部的请求都按照时间戳前后排序。
由节点发送到客户端的消息包括(当前视图号v,容许客户端去跟踪视图发现当前的primary).优化
节点直接发送响应到客户端,响应内容包括<REPLY,v,t,c,i,r>
.
v:当前视图号。
t:响应请求的时间戳。
i:节点ID
r:执行操做获得的结果。日志
每个节点的状态包括服务的状态。消息日志包括节点被接受的信息,以及节点当前的视图。
当primary接受到客户端的请求m,将开始三个阶段的协议进行自动多播请求到节点。
除非消息的数量超出协议中给定的最大消息数量不然primary当即开始该三阶段协议。若是消息超过最大消息数,将会将请求放置缓冲区。code
三阶段分为pre-prepare,prepare,commit
。blog
pre-prepare
和prepare
阶段用于对在同一视图中发送的请求彻底排序,即便提出请求排序的primary为虚假节点也是如此。prepare
和commit
阶段用于确保在视图之间对提交的请求进行彻底排序 在pre-prepare
阶段,primary定义了一个序列号n,到请求消息中。多播一个pre-prepare
消息并联合消息m到全部节点。并将该消息添加到日志中。该消息内容为 <<PRE-PREPARE,v,n,d>_s,m>
(_s
表明签名)这里的v代表被发送的消息处于的视图。m是客户端的请求消息。d为m的摘要。
为了保持消息较小。请求没有包括在pre-prepare
消息中。这是很重要的由于pre-prepare
消息用于做为该请求定义的序列号n在视图v中的证实。另外,它将协议与协议彻底分离,以将请求传输到节点;容许咱们为协议消息使用针对小消息优化的传输,对于大型请求针对大消息使用优化的传输。
节点接收到提供的pre-prepare消息后:排序
pre-prepare
消息是有效的,而且d是m的摘要。pre-prepare
消息中的序列号在低的阈值h与高阈值H之间。最后一个条件用于阻止错误的primary为了耗尽序列号空间而选择一个很是大的值。it
若是节点i接受了 <<PRE-PREPARE,v,n,d>_s,m>
消息。节点将会进入prepare
阶段,并多播 <PREPARE,v,n,d,i>_s
消息到全部其余的节点,并将该消息添加到它的日志中。不然将什么也不作。集群
节点(包括primary)接收了prepare
消息:变量
并添加他们到本身的日志中。
只有当节点i已将如下消息添加到它的日志:
pre-prepare
消息(来自不一样节点2f个) 而且节点经过检查prepare
消息与pre-prepare
消息具备相同的视图,序列号和签名,才认为prepared (m,v,n,i)
消息为有效的。
算法的pre-prepare
和prepare
阶段保证诚实节点赞成视图中请求的总顺序。更准确的,确保如下的变量:
prepared (m,v,n,i)
消息是有效的,那么prepared (m’,v,n,j)
消息是无效的。而且任何D(m') 不等于D(m).prepared (m,v,n,i)
消息和 R=3f+1代表至少有f+1个诚实节点在视图v中发送了序列号为n的pre-prepare
消息或者是prepare
消息。prepared (m’,v,n,j)
消息若是是有效的,那么须要至少一个诚实节点必须发送两个冲突的prepare
消息(或者是视图为v的primary
发送pre-prepare
消息),两个prepare
消息具备相同的视图和序列号可是具备不一样的摘要信息。可是这是不可能的由于节点不是虚假节点。 当prepared (m,v,n,i)
消息为有效的那么节点i多播 <COMMIT,v,n,D(m),i>_s
消息到其余节点.这个过程为commit
阶段。节点接收commit
消息并添加该信息到日志中。
若是而且只有当对于全部在f+1诚实节点中的节点i,prepared (m,v,n,i)
消息都是有效的,那么committed (m,v,n,i)
消息则是有效的。
若是而且只有当节点i从不一样的节点接收到2f+1个commit
消息(可能包括本身),而且与请求m的pre-prepare
消息匹配(具备相同的视图,序列号和摘要)。则committed-local (m,v,n,i)
消息是有效的。
commit
阶段确保如下变量:
committed-local (m,v,n,i)
消息是有效的。那么committed(m,v,n)
消息是有效的。 每个节点i在当committed-local(m,v,n,i)
消息是有效的,而且i的状态反应了在全部请求中该请求的序列号是最小的状况下将会执行该操做。确保了全部诚实节点能够以相同的顺序执行请求,保证了安全性。在执行完请求操做后,节点将返回一个响应到客户端。
当请求的时间戳小于最后一次回复的时间戳时节点抛弃该请求。保证只执行一次。
不依赖消息顺序交付。所以可能节点乱序提交请求。这是无所谓的,由于节点保持了pre-prepare
,prepare
,和commit
消息日志一直到该请求被执行。
图展现了该算法的以一种正常的例子(没有primary虚假)的操做。节点0为primary,节点3为虚假节点。C为客户端.