首先说到分布式算法,必须提到Paxos算法。 在过去的几十年里,它基本上是分布式共识的代名词。 因为目前最常用的一组共识算法在此基础上得到了改进。 例如,Fast Paxos算法、Cheap Paxos算法、Raft算法、ZAB协议等。
兰伯特提出的Paxos算法有两个部分。 一种是Basic Paxos算法,它描述了多节点之间如何就某个值(建议的值)达成协议。 另一种是Multi-Paxos思想,它描述了运行多个Basic Paxos实例并就一系列值达成共识,但由于朗伯提到的Multi-Paxos思想,如何选出指导者等是实现代码所必需的
Basic Paxos 是 Multi-Paxos 思想的核心,说白了,Multi-Paxos 就是多执行 几次 Basic Paxos
思考问题。 假设您要实现一个由节点a、b和c组成的分布式群集,该群集提供只读KV存储服务。 你应该知道。 创建只读变量时,必须为其赋值。 而且,这个值以后不能修改。 因此,一旦一个节点创建了只读变量,就不能更改它。 因此,所有节点必须对只读变量的值达成一致,然后所有节点才能一起创建只读变量。
在中,如果多个客户端(如客户端1、2 )访问此系统并尝试创建相同的只读变量(如x ),则客户端1将尝试创建值为3的x,而客户端2将尝试创建值为7的x。 这样,如何达成共识,实现各节点上的x值一致呢? 带着这个问题,我们进入今天的学习。
三种角色:为了帮助理解Basic Paxos算法,朗伯在课堂上还使用了一些独特而重要的概念,如建议、准备请求、接受请求和角色。 其中最重要的是“作用”。 角色是指将Basic Paxos最中心的3个功能抽象化后的功能,例如,接收者(Acceptor )对提议的值进行投票,并存储接收到的值。
他们的关系如下
提议者(Proposer):提议用于投票的值。 为了简化演示,可以将图1中的客户端1和2视为提案者。 但是,在大部分场景中,接收到集群内客户端请求的节点才是提议者(图1的体系结构是为了简单地展示算法的原理)。 这样做的好处是没有对业务代码的入侵性。 这意味着您可以像使用数据库一样访问后端数据,而无需在业务代码中实现算法逻辑。
接受者(Acceptor):对提议的每一个值进行投票,存储可以接受的值,例如a、b、c三个节点。 一般来说,集群中的所有节点都充当接收者,参与共识协商,接受并存储数据。
说到这里,你可能会怀疑,接收客户端请求的节点不是说了是提案人吗? 这里怎么又是接受者? 这是因为一个节点(或进程)可以同时扮演多个角色。 想象一下。 在一个三节点群集中,如果一个节点接收到请求,则该节点将以提案者的身份启动两阶段提交。 然后,该节点与其他两个节点一起作为接收方进行协议协商。 如下图所示。
学习者(Learner):被告知投票结果,接收并保存达成协议的值,不参与投票过程。 一般来说,学习者是数据备份节点,如“Master-Slave”模型的Slave,被动接收数据并进行灾难恢复。
这三种角色,在本质上代表的是三种功能:
提案人代表访问和协调功能,收到客户请求后,开始两阶段提交,进行协商一致; 接受者代表投票协商并保存数据,对提议的值进行投票,接受并保存同意值的学习者代表保存数据,不参加协商一致,只接受并保存达成一致的值。 在Basic Paxos中,兰伯特也使用提案表示了一个提案。 不过,提案中除提案编号外,还包含提案值。 为了简化演示,我用[n,v]表示一个建议。 其中,n是提案编号,v是提案值。
我想强调一下,整个共识谈判分为两个阶段进行。 也就是说,在03中提到的两个阶段提交。 那么具体怎么协商?
设客户机1的提案编号为1,客户机2的提案编号为5,假设节点a、b先接受来自客户机1的准备请求,节点c先接受来自客户机2的准备请求。
准备(Prepare )阶段观察最初的阶段,首先客户机1、2作为提案者,分别向所有接收者发送包含提案号码的准备要求
请注意,在准备申请中不需要指定提案的值,只需携带提案编号即可。 这是很多同学容易误解的地方。
接下来,节点a、b收到提案号1的准备要求,节点c在收到提案号5的准备要求后,进行这样的处理。
由于到目前为止任何提案都没有被通过,所以节点a、b回复说“还没有提案”。 也就是说,节点a和b正在告诉提案者,我到目前为止还没有通过任何提案,今后将不再响应提案号码小于1的准备请求,并承诺号码小于1的提案将不会通过。
节点c也一样,返回1
个 “尚无提案”的响应,并承诺以后不再响应提案编号小 于等于 5 的准备请求,不会通过编号小于 5 的提案。另外,当节点 A、B 收到提案编号为 5 的准备请求,和节点 C 收到提案编号为 1 的准备请 求的时候,将进行这样的处理过程:
当节点 A、B 收到提案编号为 5 的准备请求的时候,因为提案编号 5 大于它们之前响应 的准备请求的提案编号 1,而且两个节点都没有通过任何提案,所以它将返回一个 “尚 无提案”的响应,并承诺以后不再响应提案编号小于等于 5 的准备请求,不会通过编号 小于 5 的提案。
当节点 C 收到提案编号为 1 的准备请求的时候,由于提案编号 1 小于它之前响应的准备 请求的提案编号 5,所以丢弃该准备请求,不做响应。
接受(Accept)阶段第二个阶段也就是接受阶段,首先客户端 1、2 在收到大多数节点的准备响应之后,会分别 发送接受请求:
当客户端 1 收到大多数的接受者(节点 A、B)的准备响应后,根据响应中提案编号最大 的提案的值,设置接受请求中的值。因为该值在来自节点 A、B 的准备响应中都为空 (也就是图 5 中的“尚无提案”),所以就把自己的提议值 3 作为提案的值,发送接受 请求[1, 3]。
当客户端 2 收到大多数的接受者的准备响应后(节点 A、B 和节点 C),根据响应中提 案编号最大的提案的值,来设置接受请求中的值。因为该值在来自节点 A、B、C 的准备 响应中都为空(也就是图 5 和图 6 中的“尚无提案”),所以就把自己的提议值 7 作为 提案的值,发送接受请求[5, 7]。
当三个节点收到 2 个客户端的接受请求时,会进行这样的处理:
当节点 A、B、C 收到接受请求[1, 3]的时候,由于提案的提案编号 1 小于三个节点承诺 能通过的提案的最小提案编号 5,所以提案[1, 3]将被拒绝。
当节点 A、B、C 收到接受请求[5, 7]的时候,由于提案的提案编号 5 不小于三个节点承 诺能通过的提案的最小提案编号 5,所以就通过提案[5, 7],也就是接受了值 7,三个节 点就 X 值为 7 达成了共识。
通过上面的演示过程,你可以看到,最终各节点就 X 的值达成了共识。那么在这里我还想 强调一下,Basic Paxos 的容错能力,源自“大多数”的约定,你可以这么理解:当少于一半的节点出现故障的时候,共识协商仍然在正常工作