某个值
(提案的Value)达成共识一系列值
达成共识所谓的保证一致性到底是保证的是什么?
1、一般来说, 分布式的一致性的解决方案都是通过状态机复制
来解决, 比如每个操作就是一条日志, 而每个节点都有自己的操作日志, 而所谓的保证一致性就是保证节点间日志的一致性, 通过与其他节点进行日志的同步.
2、当日志保证一致了, 相同的日志序列输入到相同的状态机
然后输出后最后得到的结果是一样的.
相同的输入序列
为 {x0, x1, x2, x3, ...xn}
, 那么这些状态机一定会经过相同的状态转换过程为: S0->S1->S2->S3...Sn
, 最终都达到了相同的状态Sn
, 并且输出相同的结果序列
为: {out1(S1), out2(S2), out3(S3), .... outn(Sn)}
.比如有以下输入序列(从上到下), 只要每台服务器有相同的状态机, 并且处理输入序列之前状态机都处于相同的状态, 那么经过状态机后, 它们最终达到的状态都是一致的, 比如经过指令重放
来实现状态机, 那么经过输入序列后最终a都等于4, b都等于3达到相同的状态.
客户端(Client):
提议者(Proposer):
提案
用于投票表决.接收者(Acceptor):
学习者(Learner):
提议者Proposer
(比如一个写请求set a=3之类), 然后提议者再发动提案请求比如[1,null]传递给第一个接受者Acceptor
, 然后基于状态机复制模型进行将提案请求复制到其他接受者Accpetor
, 当超过半数以上的接受者Acceptor响应表示允许发出一个提案, 说明一阶段准备请求成功. 然后提议者开始二阶段, 提议者把提案id及其内容(如[1,3])发给接受者Accpetor, 然后同样的吐过超过半数以上的响应说明提案通过, 然后该通过的提案就会被学习者进行持久化存储记录.提议者(Proposer)
发起二阶段提交
来完成一次共识协商,一阶段叫准备阶段:
二阶段叫接受阶段
[协商规则]
保证提案的有序性
一阶段
Prepare准备请求阶段
, 提议者P发出了一个提案id为1的提案给3个接受者A、B、C(此时不需要携带提案内容所以为null),保证或者没有已通过的任何提案
(如都为null), 所以响应提议者说他们可以接受此id的提案, 然后存储到本地, 并保证不再接受比此提案id更小的提案. 并发送Promise响应请求
(如果本地之前没有保证或者通过任何提案, 则响应内容为空也就是尚无提案, 否则响应内容为已通过的提案[id, value]
). 当提议者P接收到半数以上
Promise准备响应阶段的成功后, 如图2为3个大于半数, 则可以进入下一个阶段[接受阶段]
.二阶段
Accept接受请求阶段
. 开始发起接受请求, 并且这次会携带提案内容, 但是提案的内容
会从一阶段的Promise准备响应阶段的响应内容中提案编号最大的提案的值作为此次提案的内容. 而如图2, 由于接收者A、B、C之前本地都没有通过任何提案, 所以返回 “[尚无提案]” 的响应, 其实也就是准备响应中都为空. 所以就把自己的提议值3作为此次的提案内容.在一阶段的Prepare准备请求阶段
, 提议者P先发起了提案[1:null], 在1、2时间点接受者A、B接收到后, 因为本地没有已通过的提案,所以接受者A、B会保证该提案, 并保证不再响应比该提案id[1]更小的提案。随后在3点的时间线提议者Q也发起了
一个提案[6,null],接收者A、B、C分别在3点、4点、5点接收到了请求, 因为当前提案6比接收者A、B本地保证的提案1比大,
所以接收者A、B会更改保证的提案为6, 而接受者C本地没有已通过的提案也没有保证任何提案所以会接收该提案, 并更新保证提案为6。
随后在时间点6, 接收者C接收到了提议者P的提案请求[1,null], 而由于接收者C已保证了提案6不再响应比提案6更小的提案,所以拒绝提案1。
在一阶段的Promise准备响应阶段
, 由于接收者A、B、C本地都没有通过任何提案,所以响应给提议者P和Q的内容都是尚无提案
(但是如果已经通过了提案,会将通过的提案[id,value]作为响应内容)。
在二阶段的Accept接收请求阶段
, 提议者P收到了接收者A和B等2个响应。所以超过了半数以上接收者的响应,所以可以开始进行二阶段的提交请求阶段
并根据响应中提案编号最大的提案的值作为此次二阶段接受请求中提案的值
。而由于接收者A和B的响应都为空(即尚无提案),所以把自己的提议值3作为提案的值,发送提案为[1, 3]的接受请求给接收者A、B、C。
而提议者Q收到了3个响应请求,也会开始进入二阶段, 同样也是根据响应中提案编号最大的提案的值作为此次二阶段接受请求中提案的值。
由于接收者A、B、C的响应都为空,所以把自己的提议值7作为提案的值, 发送提案为[6, 7]的接受请求给接收者A、B、C。
在二阶段的Accepted接受响应阶段
, 接收者A、B、C、先收到了提议者P的接受请求的提案[1,3], 但是由于提案的id为1小于它们保证的提案6,
所以提案[1, 3]将被接收者A、B、C拒绝。随后接收者A、B、C收到了接收请求[6,7], 提案编号6不小于已保证的提案6,所以通过该提案[6,7],
也就是接受了值7,三个接收者就值7达成了共识。
既可以是提议者也可以是接收者
, 因为每个节点都可以负责读写请求. 接收到写请求的那个节点会作为一个提议者去发起提案, 然后其他节点作为接收者进行投票表决. 所以可能同时存在多个提议者发起提案造成活锁问题所谓活锁问题就是所有提案都被拒绝并且一直持续下去
, 我们知道接受者在遇到比当前已保证的提案小的情况下会拒绝当前提案, 产生活锁的原因就是并发提案下, 当前提案只完成了一阶段请求, 在准备进入二阶段请求之前, 该提案被其他提议者发出的更高级别的提案覆盖了, 导致后面要发出二阶段请求的时候被拒绝. 如果这么一直循环下去就会造成活锁.提案[1]的一阶段 ⇒ 提案[2]的一阶段 ⇒ 提案[1]的二阶段 => 提案[3] 的一阶段 => 提案2的二阶段 ⇒ 提案[4]的一阶段 ⇒ 提案[3]的二阶段.....
如果接收者A经过了这些状态转换过程, 那么提案1、2、3都会被接收者A拒绝. 同理那样交叉循环下去接收者A永远无法接受提案.第一个Basic Paxos叫选举
, 第二个Basic Paxos叫复制
. 选举出一个leader需要通过一次Basic Paxos来达成共识, 通过引入leader 作为唯一的提案者去解决提案冲突问题, 然后所有的接受者以leader的提案为准, leader可以直接让接受者Acceptor去接受请求, 即不需要再经过一阶段准备阶段, 而是直接进入二阶段的提交阶段, 同时省略了一次状态复制的过程.选举:
复制