• 沒有找到結果。

协议推导

在文檔中 分布式系统原理介绍 (頁 62-69)

2 分布式系统原理

2.8 Paxos 协议

2.8.5 协议推导

Paxos 协议是被人为设计出来,其设计过程也是协议的推导过程。Paxos 协议利用了 Quorom 机 制,选择的W=R=N/2+1。简单而言,协议就是 Proposer 更新 Acceptor 的过程,一旦某个 Acceptor 成功更新了超过半数的Acceptor,则更新成功。Learner 按 Quorum 去读取 Acceptor,一旦某个 value 在超过半数的Proposer 上被成功读取,则说明这是一个被批准的 value。协议通过引入轮次,使得高 轮次的提议抢占低轮次的提议来避免死锁。

协议设计关键点是如何满足“在一次Paxos 算法实例过程中只批准一个 Value”这一约束条件。

称这个约束条件为“约束条件1”。由于直接在工程中实现“约束条件 1”很困难,所以协议的设计 过程就是不断推导出“约束条件 1”的必要不充分条件,直到某个必要不充分条件在工程上易于实 现。从而满足这个条件也就能满足“约束条件P1”。

一、提出“约束条件2:一旦一个 value 获得超过半数的 Acceptor 批准,之后 Paxos 协议只能批 准这个value”,易证明“约束条件 2”=>“约束条件 1”。

二、加强该约束“约束条件 2”,寻找其必要不充分条件,提出“约束条件 3:一旦一个 value

获得超过半数的Acceptor 批准,之后任何 Acceptor 只能批准这个 value”。容易证明“约束条件 3”=>

“约束条件2”

三、既然“约束条件3”中要使得“之后任何 Acceptor 只能批准这个 value”那么等价于“之后 Proposer 发送的 accept 消息也只能是这个 value”。所以“约束条件 3”等价于“约束条件 4:一旦一 个value v 获得超过半数的 Acceptor 批准,之后 Proposer 提议的 value 只能是 v”。

四、加强“约束条件4”,得到 Paxos 协议中的 Proposer 流程的“步骤 3”,即“约束条件 5:Proposer 提议一个value v 前,要么之前没有任何一个 value 被批准,要么存在一个大小为 N/2+1 的 Acceptor 集合,这个集合内的各个Acceptor 批准过的轮数最大的 value 是 v。”

“约束条件5”的前半部分“提议 value v 前,没有任何一个 value 被批准”所以选择任意 value 提案一定不违背“约束条件4”。确定“之前没有任何一个 value 被批准”的方法就是读取 Acceptor,

如果有超过半数的Acceptor 批准的 value 为空,那么肯定没有一个 value 被批准过。这也就是 Proposer 流程“步骤3.1”。

“约束条件5”的后半部分对应 Proposer 流程“步骤 3.2”,即读取了 N/2+1 的 Acceptor 的状态,

这些Acceptor 批准了某些 value,由于没有读取所有的 Acceptor,故可能无法确定是否一定有 value 已经被批准了。例如,5 个 Acceptor 时读取了 3 个 Acceptor 状态情况是(B=1, V=v1)(B=2, V=v2) (B=3, V=NULL),v2可能是一个已经被批准的value,是否已经被批准取决于另外两个 Acceptor 上的状态,

如果另外两个Acceptor 都批准了 v2,则 v2是一个已经被批准的 value,如果另外两个 Acceptor 上的 value 为 NULL,那么 Paxos 协议还没有批准任何 value。“约束条件 5”的做法是在这种情况下选择 一个轮数号大的value 即 v2,从而可以保证:要么此时Paxos 协议还没有批准任何一个 value,要么 之前Paxos 协议批准的也只能是 v2

下面证明“约束条件5”的后半部分=> “约束条件 4”:

1. 如果之前 Paxos 尚没有批准任何 value,那么选择轮次编号最大的 value 提案显然是“安全的”

不违反“约束条件4”。

2. 假设之前 Paxos 已经批准过一个 value,记为 v0。下面证明在任意第 n 轮 paxos 过程中,v0 一定是此时任意一个N/2+1 的 Acceptor 集合中轮数最大的 value。

令v0是在第m 轮被批准的,则在 m 轮时至少有 N/2+1 个 Acceptor 的状态是(B=m,V=v0) 在第m+1 轮,由于 Quorum 限制,任意一个 N/2+1 个 Acceptor 组成的集合中,至少有 1 个 Acceptor 的状态是(B=m+1,V=v0),即 Proposer 至少收到一个 promise(m+1, v0_m)消息,由于此刻 m 必然是 所有value 中最大的编号,所以 Proposer 按“约束条件 5”发出只能提案 value 只能是 v0

在第m+1 轮、m+2 轮… n-1 轮,按递推规则,这些轮提案的 value 也只能是 v0

进一步反证法证明第n 轮:假设在第 n 轮中,提案的 value 不是 v0而是vx。根据“约束条件5”,

因为vx不是v0,那么 vx只能是一个没有被超过半数Acceptor 批准过的 value,说明在 n-1 轮存在一个 N/2+1 的集合,该集合内所有的 value 都没有批准过 v0。这个与从m 轮到 n-1 轮提案的 value 是 v0

相矛盾。至此已经归纳证明了“约束条件5”的后半部分。

2.8.6 工程投影

2.8.6.1 Chubby 中的 Paxos

Chubby 是最早基于 Paxos 的分布式系统之一。Chubby 的设计人员没有直接提供一种 Paxos 的 开发库,而是利用Paxos 实现一个高可用的分布式系统,再利用这个分布式系统对外提供高可用存 储、分布式锁等服务,从而间接的提供了Paxos 功能。

Chubby 中的节点完全是对等的,通过 Paxos 协议,这些节点选举出一个 Master 节点(Primary),

公开的资料中没有解释Chubby 使用 Paxos 的细节,例如如何选择 Paxos 的轮次号,如何避免 Paxos 活锁等。当选举出Primary 节点后,所有读写操作都由 Primary 节点控制,Chubby 系统从一个完全 对等的去中心化状态变为一个Primary-Secondary 的中心化状态。当 Primary 异常时,Chubby 节点将 重新利用paxos 协议发起新一轮的选举以确定新的 primary 节点。新 primary 节点与原 primary 节点 具有完全一样的持久化信息,新primary 将代替原 primary 节点对外提供读写服务。

基于Chubby 的服务,其他的分布式系统可以很容易的实现选择 primary、保存最核心元数据等 功能。利用Chubby 可以大大简化分布式系统的设计:可以认为整个 Chubby 集群逻辑上是一个 magic 的高可用(几乎不会停服务)的中心节点,其他分布式系统可以基于这个大中心节点可以实现中心 化的副本控制协议。由于Chubby 集群本身是由多个节点组成的分布式系统,基于 Chubby 的分布式 系统无需直接实现Paxos 协议,就可以利用 Paxos 协议实现全局完全无单点。

2.8.6.2 Zookeeper 中的 Paxos

Zookeeper 使用了一种修改后的 Paxos 协议。

首先,Zookeeper 的协议运行依赖 TCP 协议实现 FIFO,Zookeeper 通过 TCP 协议获得两点保障:

1、数据总是严格按照 FIFO(first in first out)规则从一个节点传递到另一个节点的;2、当某个 TCP 链接关闭后,这个链接上不再有数据传递。由于 TCP 协议为传输的每一个字节设置了序列号 (sequence number)及确认(acknowledgment),上述两点在 TCP 协议上是完全可以保证的。需要注意的 是Zookeeper 并不要求 TCP 协议可以可靠的将数据传输到对端节点,正如本文在 1.1.4.2.4 分析过的,

基于TCP 协议实现真正意义上的可靠传输也是做不到的。Zookeeper 基于 TCP 的上述两点保障,可 以较大的简化问题模型,忽略诸如网络消息乱序、网络消息重复等的异常,从而较大的简化协议设 计。

再者,在Zookeeper 中,始终分为两种场景:一、Leader activation,在这个场景里,系统中缺 乏Leader(primary),通过一个类似 paxos 协议的过程完成 Leader 选举。二、Active messaging,在 这个场景里,Leader 接收客户端发送的更新操作,以一种类似两阶段提交的过程在各个 follower

(secondary)节点上进行更新操作。在 Leader activation 场景中完成 leader 选举及数据同步后,系统 转入Active messaging 场景,在 active messaging 中 leader 异常后,系统转入 Leader activation 场景。

无论在那种场景,Zookeeper 依赖于一个全局版本号:zxid。zxid 由(epoch, count)两部分组成,

高位的epoch 部分是选举编号,每次提议进行新的 leader 选举时 epoch 都会增加,低位的 count 部分 是 leader 为每个更新操作决定的序号。可以认为,一个 leader 对应一个唯一的 epoch,每个 leader 任期内产生的更新操作对应一个唯一的有序的 count,从而从全局的视野,一个 zxid 代表了一个更 新操作的全局序号(版本号)。

每个zookeeper 节点都有各自最后 commit 的 zxid,表示这个 zookeeper 节点上最近成功执行的 更新操作,也代表了这个节点的数据版本。在Leader activation 阶段,每个 zookeeper 节点都以自己 的zxid 作为 Paxos 中的 b 参数发起 paxos 实例,设置自己作为 leader(此为 value)。每个 zookeeper 节点既是proposer 又是 acceptor,所以,每个 zookeeper 节点只会 accpet 提案编号 b 大于自身 zxid 的提案。不难理解,通过paxos 协议过程,某个超过 quorum 半数的节点中持有最大的 zxid 的节点 会成为新的 leader。值得注意的是,假如参与选举的每个 zookeeper 节点的 zxid 都一样,即所有的 节点都以相同的b=zxid 发提案,那么就有可能发送类似 2.8.4 中无法选举出 leader 的情况。zookeeper 解决这个问题的办法很简单,zookeeper 要求为每个节点配置一个不同的的节点编号,记为 nodeid,

paxos 过程中以 b=(zxid, nodeid)发起提议,从而当 zxid 相同时会优先选择节点编号较大的节点成为 leader。成为新 leader 的节点首先与 follower 完成数据同步后,再次说明,数据同步过程可能会涉及 删除follower 上的最后一条脏数据,详细分析见 2.4.6.3 。当与至少半数节点完成数据同步后,leader 更新epoch,在各个 follower 上以(epoch + 1, 0) 为 zxid 写一条没有数据的更新操作。这个更新操作 称为 NEW_LEADER 消息,是为了在各个节点上更新 leader 信息,当收到超过半数的 follower 对 NEW_LEADER 的确认后,leader 发起对 NEW_LEADER 的 COMMIT 操作,并进入 active messaging 状态提供服务。

进入active messaging 状态的 leader 会接收从客户端发来的更新操作,为每个更新操作生成递增 的count,组成递增的 zxid。Leader 将更新操作以 zxid 的顺序发送给各个 follower(包括 leader 本身,

一个leader 同时也是 follower),当收到超过半数的 follower 的确认后,Leader 发送针对该更新操作 的COMMIT 消息给各个 follower。这个更新操作的过程很类似两阶段提交,只是 leader 永远不会对 更新操作做abort 操作。

如果leader 不能更新超过半数的 follower,也说明 leader 失去了 quorum,此时可以发起新的 leader 选举,最后一条更新操作处于“中间状态”,其是否生效取决于选举出的新leader 是否有该条更新操 作。从另一个角度,当leader 失去 quorum 的 follower,也说明可能有一个超过半数的节点集合正在

选举新的leader。

Zookeeper 通过 zxid 将两个场景阶段较好的结合起来,且能保证全局的强一致性。由于同一时 刻只有一个zookeeper 节点能获得超过半数的 follower,所以同一时刻最多只存在唯一的 leader;每 个leader 利用 FIFO 以 zxid 顺序更新各个 follower,只有成功完成前一个更新操作的才会进行下一个 更新操作,在同一个 leader 任期内,数据在全局满足 quorum 约束的强一致,即读超过半数的节点 一定可以读到最新已提交的数据;每个成功的更新操作都至少被超过半数的节点确认,使得新选举

Zookeeper 通过 zxid 将两个场景阶段较好的结合起来,且能保证全局的强一致性。由于同一时 刻只有一个zookeeper 节点能获得超过半数的 follower,所以同一时刻最多只存在唯一的 leader;每 个leader 利用 FIFO 以 zxid 顺序更新各个 follower,只有成功完成前一个更新操作的才会进行下一个 更新操作,在同一个 leader 任期内,数据在全局满足 quorum 约束的强一致,即读超过半数的节点 一定可以读到最新已提交的数据;每个成功的更新操作都至少被超过半数的节点确认,使得新选举

在文檔中 分布式系统原理介绍 (頁 62-69)