看了这一篇Paxos协议其实并不难

2,842 阅读5分钟

今天,我来给大家详细介绍一下 Paxos 协议。Paxos 协议是分布式系统中最为著名的一种一致性算法,它被广泛应用于分布式系统的实现中,例如数据库、分布式文件系统、分布式缓存等。

Paxos 协议是由 Leslie Lamport 在 1989 年提出的一种分布式一致性协议,它是分布式系统领域中的经典协议之一。Leslie Lamport 是分布式计算领域的著名科学家,曾获得图灵奖等多项荣誉。Paxos 协议被称为经典协议,它提供了一种在异步网络模型下实现分布式系统状态机复制的解决方案,而这种解决方案被认为是具有通用性的。Paxos 协议是许多后来的分布式一致性算法的基础,例如 ZooKeeper 和 Raft 等。理解了Paxos 协议将与你理解其他CP的分布式一致性协议具有很大的作用。

Paxos 协议的目的是确保分布式系统中多个节点之间的数据副本保持一致。在这个过程中,如果某个节点发生了故障,其他节点仍然可以继续工作,而不会影响整个系统的正常运行。

paxos中的 角色

 Paxos协议中涉及到的主要角色有三种:提议者(Proposer)、接受者(Acceptor)和学习者(Learner)。

提议者(Proposer)

提议者是Paxos协议中发起提案的角色。当一个提议者要向系统中的其他节点发起一个提案时,它首先会发送一个准备请求给所有的接受者。准备请求中包含一个提案编号,提案编号应该是一个全局唯一的值。接受者会根据提案编号进行处理,并返回一个响应,表示自己是否可以接受该提案。当提议者获得了足够多的接受者的响应后,就可以将提案提交给接受者,并等待学习者确认该提案的最终值。

接受者(Acceptor)

接受者是Paxos协议中处理提案的角色。当一个接受者接收到一个准备请求时,它会检查自己是否已经接受过一个编号更大的提案。如果没有,它会返回一个响应,表示可以接受该提案。同时,它也会记录下最新的提案编号和对应的提案值。当接受者接收到一个提交请求时,它会检查该请求中的提案编号是否大于自己记录的最新提案编号,如果大于,则将自己的记录更新为该提案编号和对应的提案值,并返回一个响应。

学习者(Learner)

学习者是Paxos协议中负责确认提案值的角色。当一个学习者接收到一个提交请求时,它会将该提案值记录在本地存储中。当一个学习者发现自己已经记录了大多数节点接受的相同提案值时,就会确认该提案值是最终的决定值,并将该值返回给提议者。

Paxos 协议如何保证一致性

Paxos 协议由三个阶段组成:准备阶段、提交阶段和学习阶段。这三个阶段共同实现了分布式系统的一致性。

image.png

准备阶段

在准备阶段中,提议者向所有参与者发出一个提案,该提案包含一个提案编号和一个提案值。参与者需要检查该提案编号是否已经被接受过,如果没有被接受过,则可以接受该提案。否则,参与者会向提议者返回已经接受的最高提案编号和对应的提案值。如果提议者收到了多数参与者的接受回复,则进入提交阶段。

提交阶段

在提交阶段中,提议者向所有参与者发出一个提交请求,该请求包含提案编号和提案值。参与者会接受该请求,并将该提案值存储到本地存储中。如果提议者收到了多数参与者的接受回复,则进入学习阶段。

学习阶段

在学习阶段中,提议者向所有参与者发出一个学习请求,该请求包含提案编号和提案值。参与者会接受该请求,并将该提案值存储到本地存储中。在这个阶段中,如果某个参与者还没有接受任何提案,则会将该提案值作为最终的决定值。

用一段代码来看看怎么费事:

# 提议者(Proposer)

proposal_number = 0

while True:

    # 提案编号自增

    proposal_number += 1

    promises = []

    # 向所有接受者发送准备请求

    for acceptor in acceptors:

        response = acceptor.prepare(proposal_number)

        promises.append(response)

    # 判断是否获得了大多数节点的同意

    if has_majority(promises):

        # 如果有至少一个接受者已经接受了某个提案,选择其中编号最大的提案值作为当前提案值

        values = [response.value for response in promises if response is not None]

        chosen_value = choose_value(values)

        # 如果没有已经接受的提案值,使用自己提议的值

        if chosen_value is None:

            chosen_value = my_proposed_value

        # 向所有接受者发送接受请求

        for acceptor in acceptors:

            acceptor.accept(proposal_number, chosen_value)

        # 判断是否获得了大多数节点的同意

        if has_majority([acceptor.accepted(proposal_number) for acceptor in acceptors]):

            # 最终确认的提案值为当前提案值

            return chosen_value

 

# 接受者(Acceptor)

highest_proposal_number = 0

highest_accepted_proposal_number = 0

accepted_proposal_value = None

 

def prepare(proposal_number):

    if proposal_number > highest_proposal_number:

        # 记录当前收到的最高提案编号

        highest_proposal_number = proposal_number

        # 回复承诺,包括当前已经接受的最高提案编号和提案值

        return Promise(highest_accepted_proposal_number, accepted_proposal_value)

    else:

        # 拒绝本次准备请求

        return Promise(None, None)

 

def accept(proposal_number, value):

    if proposal_number >= highest_proposal_number:

        # 记录当前收到的最高提案编号

        highest_proposal_number = proposal_number

        # 记录接受当前提案的最高提案编号和提案值

        highest_accepted_proposal_number = proposal_number

        accepted_proposal_value = value

        # 接受本次提案

        return True

    else:

        # 拒绝本次提案

        return False

 

def accepted(proposal_number):

    if proposal_number == highest_accepted_proposal_number:

        # 返回当前已经接受的最高提案编号和提案值

        return Accepted(highest_accepted_proposal_number, accepted_proposal_value)

    else:

        # 返回未接受提案的信息

        return Accepted(None, None)

 

# 学习者(Learner)

accepted_proposals = {}

 

def learn(value):

    if value.proposal_number not in accepted_proposals:

        accepted_proposals[value.proposal_number] = []

    # 记录接受到的提案值

    accepted_proposals[value.proposal_number].append(value.proposal_value)

    # 判断是否获得了大多数节点的同意

    if has_majority(accepted_proposals[value.proposal_number]):

        # 选择最终确认的提案值
        chosen_value = choose_value(accepted_proposals[value.proposal_number])

        return Decision(value.proposal_number, chosen_value)