分布式算法—Paxos

616 阅读9分钟

分布式算法—Paxos

Paxos是能够基于一大堆完全不可靠的网络条件下却能可靠确定地实现共识一致性的算法。也就是说:它允许一组不一定可靠的处理器(服务器)在某些条件得到满足情况下就能达成确定的安全的共识,如果条件不能满足也确保这组处理器(服务器)保持一致。Paxos 算法分为核心算法和完整算法两部分。Paxos 算法的核心部分解决了分布式领域当中非常重要的基础问题,也就是共识问题;完整算法是用来实现状态机的算法。

共识问题

不严格地说,共识问题就算多个进程对一个值达成一致,每个进程都可以提议(propose)一个自己想要的值,但是最终只有一个值会被选中,并且所有进程对这个选中的值达成一致。

共识算法注意点

共识算法有几个需要注意的点:

  • 可以是任意多个进程
  • 所有进程都可以提议一个值
  • 所有进程都可以学习到被选中的值

共识算法的要求

共识算法有两个要求,安全要求和存活要求

安全要求是指:

  • 只有一个被提议的值可能被选中
  • 只有唯一的一个值被选中
  • 只有一个值实际已经被选中,一个进程才能学习到这个值

存活要求是指:某个被提议的值最终一定会被选中,并且如果一个值被选中,那么一个进程最终能够学习到这个值。

Paxos 共识算法简述

算法的角色

Paxos 共识算法有三个角色,分别是提议者(proposer)、接受者(acceptor)和 学习者(learner)。这些角色是逻辑角色,负责完成算法的不同功能,一个进程可以容纳多个角色:

  • proposer 角色负责提出一个值
  • acceptor 角色负责选择一个值
  • learner 角色负责学习到被选中的值

算法的选择值和学习值的过程

共识算法分为两个过程,即选择一个值和学习一个值,具体如下:

  • 在选择一个值的过程中,一组 proposer 中每一个 proposer 都可以提议任意一个值给一组 acceptor ,这组 acceptor 会从所有的 proposer 提议的值中选出唯一的一个值。
  • 在学习一个值的过程中,learner 从 acceptor 中学习到被选中的值

选择值具体过程

选择值过程是一个二阶提交的过程,第一阶段选择提议编号,第二阶段接受提议的值。

  • 1.1:一个 proposer 生成一个新的提议编号 n,n 大于上一次使用的提议编号。并且发送 prepare(n) 消息给大多数 acceptor
  • 1.2:如果一个 acceptor 收到一个提议消息,并且 n 大于本地承诺过的提议编号,则用 n 更新本地承诺过的提议编号。如果有接受过其他的提议,则将提议的编号和值一起返回,如果没有返回提议的编号和 null;
  • 2.1:如果一个 proposer 收到大多数 acceptor 回复的承诺编号为 n 的 promise 消息,则构建一个新的提议,提议编号为 n,提议值为 v。v 按照如下规则确定:
    • 如果在所有的这些 promise 消息中有携带提议的,则用提议编号最大的提议中的值
    • 如果在所有的这些 promise 消息中没有携带提议的,则可以用 proposer 自己要提议的值。这个 proposer 给这些 acceptor 中的每一个节点都发送确认消息
  • 2.2: 如果一个 acceptor 收到确认消息,且 n 大于本地承诺过的编号,则接受提议中的值,并持久化存储。

图解过程:

从这个过程可以看出,acceptor3、acceptor4、acceptor5 接收到 (2, X) 的提议后,会接受这个提议。5 个 acceptor 最终接受的提议是不同的,有 (1, X) 以及 (2, X)。但是其中的值是相同的,也就是对值达成了共识,不要求提议编号也一致。

学习值具体过程

学习值和选择值类似,也是一个二阶提交的过程:

3.1 当 acceptor 接受一个提议后,向所有的 learner 通知这个提议

3.2 如果 learner 收到 acceptor 的通知,则接受这个提议。如果 learner 接受从大多数 acceptor 收到的某个提议,则这个 learner 接受提议中的值。

图解过程:

从图中可以看出,learner 消息的数量是 acceptor 的数量与 learner 的数量的乘积,为了减少消息的数量,可以指定一个learner 为 leader,acceptor 接受提议后,向 learner 的 leader 发送 learn 消息,leader 收到大多数消息后,接受请求中的值再向其他 learner 发送 learn 消息。

共识算法总结

整合前面的选择值和学习值的过程,共识算法一共有如下步骤:

  • proposer 向大多数 acceptor 发起提议编号。
  • acceptor 根据规则(编号最大)接受提议编号,并回复 proposer
  • proposer 接受到大多数 acceptor 的回复后,再根据规则(是否存在已承诺的值)选择一个值发送确认消息。
  • acceptor 接受到确认消息后接受消息中的值,并向 learner leader 发送 learn 请求
  • learn leader 接受到大多数的学习请求后,向其他的 learner 发送学习请求,同步这个值。

到此,proposer 提议的值就被同步到所有的 learn 中。共识算法结束。总结一下,其实 paxos 共识算法可以简单的理解为三阶段提交的过程

  • 第一阶段:确认大多数节点可接受提议编号
  • 第二阶段:提交提议,同步值给大多数 acceptor
  • 第三阶段:同步值给所有 learn 节点

Paxos 完整算法

Paxos 共识算法只能对一个值形成决议,决议的形成至少需要两次网络来回,在高并发情况下可能需要更多的网络来回,极端情况下甚至可能形成活锁。如果想连续确定多个值,Paxos 共识算法搞不定了。因此 Paxos 共识算法几乎只是用来做理论研究,并不直接应用在实际工程中。

实际应用中几乎都需要连续确定多个值,而且希望能有更高的效率。Multi-Paxos正是为解决此问题而提出。Multi-Paxos基于Paxos 共识算法做了两点改进:

  1. 针对每一个要确定的值,运行一次Paxos算法实例(Instance),形成决议。每一个Paxos实例使用唯一的Instance ID标识。
  2. 在所有Proposers中选举一个Leader,由Leader唯一地提交Proposal给Acceptors进行表决。这样没有Proposer竞争,解决了活锁问题。在系统中仅有一个Leader进行Value提交的情况下,Prepare阶段就可以跳过,从而将两阶段变为一阶段,提高效率。

Paxos 完整算法——Multi Paxos,就是多次运行 Paxos 共识算法,形成多个实例的算法。类似于每个进程都会形成一个数组,数组中的每个元素都是一个达成一致的值。

异常情况处理

脑裂问题

Paxos 共识算法会保证即使有多个进程认为自己是 leader ,也就是出现脑裂的情况,每个实例最终也只能有一个值被选中——每个 leader 提议值的时候都有提议 id,而 learn 每次只能接受一个提议 id。

进程1 (leader 1) 不一定每次都能成功,也会出现失败的情况。当进程1 成为 leader,提议通过了一个值,该值还未被进程2(learn) 完全学习,这时进程3 也认为自己是 leader 并让进程2 接受了自己的提议(更大的提议编号),导致进程1 同步给进程2 的内容失效。

空洞处理

各实例是可以并发执行的,这可能会导致一个问题:如果在第一个实例的值的确认过程中出现消息丢失或者网络延时,第一个值没有被确定。同时 leader 开始了第二个实例,并且第二个实例的值成功确定,那个第一个实例的值是空的,第二个实例的值是确定的,这种情况被成为空洞。

如果 leader 法西安某个消息在一定时间内没有得到回复,那么 leader 可以重发这个消息。通过重发机制,空洞最终会被填补上。

如果是这时发生 leader 切换,则情况会有所不同。如果旧的 leader 的提议已经被接受,那么新的 leader 会继续保持这个提议——新的 leader 重发相同的消息;如果旧的 leader 的提议还没有被接受,则新的 leader 可以提议一个新的值。不管怎样,这个空洞最终都会被填补上。

Paxos 算法应用

原子广播

原子广播协议用于把消息向广播对象进行广播,并且保证消息能够被可靠地收到,且所有广播对象以相同的顺序收到。

原子广播协议通常被定义包含以下两个动作:

  • ABroadcast(v):广播动作,当客户端想广播值 v 时,调用这个动作。
  • v=ADeliver():投递动作,客户端通过这个动作接收其他客户端提交的值。这个动作一般是一个回调,当有值要被接受时,客户端会被回调。

原子广播的特性——原子广播保证:如果一个进程调用了广播动作,那么所有的客户端的投递动作都会被回调,并且调用顺序与调用广播的顺序一致。

Paxos 算法与原子广播

在基于 Paxos 算法的原子广播的实现中,原子广播黑盒内部由一组进程组成,原子广播内部包含 proposer 和 acceptor 角色,接收广播回调(aDeliver)的 client 作为 learner 角色。

当一个 client 调用 ABroadcast(v) 动作时,这个请求会作为一个提议给到 proposer ,经过大多数 acceptor 达成共识后,该值同步给所有的 learner。learner 接受到这个值后,完成对 aDeliver() 动作接口的回调。

总结

Paxos 又分为两个算法,Basic Paxos (共识算法) 和 Multi Paxos (完整算法)。共识算法包括选择值和学习值两个具体过程,具体步骤可以概况为三个阶段:

  • 第一阶段:确认大多数节点可接受提议编号
  • 第二阶段:提交提议,同步值给大多数 acceptor
  • 第三阶段:同步值给所有 learn 节点

完整算法则是多次运行 Paxos 共识算法,解决共识算法在工程中的应用问题。完整算法中的 leader 也是通过共识算法达成一致。