脑图:算法说明
里面有好几个算法说明
http://naotu.baidu.com/file/584323ee64d5ce77a926915e7752d287?token=4bd4b488beb80e0e
实例1
下面画个时序图来加深理解
以这个实例画时序图
https://zh.wikipedia.org/zh/Paxos%E7%AE%97%E6%B3%95#.E5.AE.9E.E4.BE.8B
其中A1能和A2 A3通信,A5能和A2 A3通信
| A1(Proposer),想把v修改成A1 | A2(Acceptor) | A3(Acceptor) | A4(Acceptor) | A5(Proposer),想把v修改成A5 |
A1、A5提出proposal | 发送:Proposal:{n=1}
|
|
|
| 发送:Proposal:{n=2} |
A2、A3收到A1 A3、A4收到A5 其中A3先收到A1后收到A5 |
| 收到:Proposal:{n=1} 回应:Promise不接受小于1的proposal | 收到:Proposal:{n=1} 回应:Promise不接受小于1的proposal | 收到:Proposal:{n=2} 回应ok 回应:Promise不接受小于2的proposal |
|
A1收到了过半数Promise,可以发送Accept请求 | 发送:Accept:{n=1,v=A1} |
|
|
|
|
A2、A3收到Accept请求,也没啥问题,修改v A4收到Accept请求,没啥问题,修改v |
| 收到:Accept:{n=1,v=A1},将v修改从A1,并返回ok | 收到:Accept:{n=1,v=A1},将v修改从A1,并返回ok |
|
|
A3后收到了A5,需要告诉A5说自己曾经接受了A1 |
|
| 收到:Proposal:{n=2} 回应ok 回应:Promise不接受小于2的proposal,而且我曾经接受过{n=1,v=A1}的Proposal |
|
|
A5收到了A3 A4的Promise请求 |
|
|
|
| 收到A3的请求里说他曾经接受了Proposal{n=1,v=A1},那我不能将v修改成我原来想修改A5了,还是修改成A1吧。发送:Accept{n=2,v=A1) |
A4 A5收到A3的Accept请求 |
|
| 收到:Accept:{n=2,v=A1},将v修改从A1,并返回ok | 收到:Accept:{n=2,v=A1},将v修改从A1,并返回ok |
|
于是大家伙的v都等于A1了。 |
|
|
|
|
|
实例2
再看一个大神的例子巩固一下: Raft作者讲解Paxos
首先,这是一个类似2阶段提交的算法:包括:Prepare、Accept
- 大神定义了一个proposal Number = 当前是第几轮RoundNumber + Server Id
- 大的priority number的优先级更高
- Proposer有可能选择一个比他之前使用的更高的proposal number。
- 每个server都要保存一个maxRound:当前自己看到的最大轮数
- 如果要产生一个新的proposal number,那就需要:
- maxRound++,然后拼接自己的server id
- 这个maxRound需要持久化到硬盘里,在crash/重启之后不能复用原来的。
算法流程
Proposers | Acceptors |
1、选择proposal number:n | |
2、广播Prepare(n)到所有servers | |
3、回复Prepare(n):承诺不会接受<n的Proposal 如果n>minProposal,那就设置minProsal=n Return(acceptedProposal, acceptedValue) | |
4、当收到大多数的回复: 如果有人返回了acceptedvalue,将value替换成最大的accetedValue
| |
5、广播Accept(n, value) | |
| 6、回复Accept(n, value) 如果n>=minProposal那么 AcceptedProposal = minproposal = n acceptedValue = value
Return(minProposal)
|
7、当收到大多数回复: 看下是否有拒绝?(result > n?) 拒绝的话就返回到1(需要更新当前见过的最大的round,下次就更有机会赢了) 否则,就选好了新的value。 |
Acceptors必须将minProposal(承诺不可以Accept小于minProposal这个的Proposal)、acceptedProposal、acceptedValue保存在可靠存储中(硬盘)
开始举例子
例子1:
之前的value已经选好了,新的Proposer应该发现并且使用
注:
P3.1 表示 Proposal(n=3.1),ProposalNumber=3.1; 3:第三轮; 1:serverId=1
A3.1 X 表示 Accept(n=3.1, v=X) , ProposalNumber=3.1, 提交的value=X
过程:
server1作为Proposal 提出提案:P3.1, 得到了server1-3的回应,于是发出 A3.1 X
然后这个时候,server5也提出提案:P4.5;server5 本来想设置value=Y的,他先发出P4.5,server3收到之后,返回了他Accepted的Proposal(3.1)和Value(X);server5收到server3的response,发现有人返回了acceptedValue,于是发Accept请求的时候,value不再是原来想发的Y,而是改成了X,发出了A 4.5 X
例子2:
前一个value没有被选定,但是新的proposer看见了
server1提出提案P3.1 ,想设置value=X ,收到了server1-3的回复 然后发出A3.1 X;
server5提出提案P4.5,想设置vallue=Y,收到了server3-5的回复,同上,server3已经Accept3.1X了,所以会给sever5返回曾经accept的X,server5将value改成了X,发出A4.5 X;
这时候server1和server2才收到A3.1X,但是没关系,大家都会设置好value=X
例子3
后面的Proposal prepares
前面的流程都一样,不一样的地方在于,server3先收到A3.1 X,这时候由于server3 Promise了,他见过4.5了(minProposal=4.5),不会accept <4.5的Proposal,而且会把minProposal=4.5返回给server1,server1就可以发现自己失败了,需要重新来过;
然后A4.5Y到了,这时候可以接受,于是server5发出的A4.5 Y,得到了server3-5的accept
!!!!!!!!!可以看到!!!关键的步骤在于,如果关键的角色server3,在A3.1X之前收到了一个P4.5,P的Proposal number比较大,他就会拒绝A3.1X,然后原来提出的A3.1X的server1就需要重试
有时候会陷入死循环:
server1提出P3.1,server5提出P3.5,关键的角色是server3,server3收到了A3.1X,3.1<3.5,拒绝:
server1重新来过,提出P4.1,server3收到A3.5Y,但是3.5<4.1,再拒绝,server5重新来过:
根本停不下来。
解决方案:每次重试的时候随机地延时一下,给别的proposer一个机会完成choosing。
或者使用Multi-Paxos,选个leader来搞。
注:只有proposer知道哪个value被chosen
如果其他server也想知道,需要自己执行一遍paxos协议,发起自己的proposal。