Paxos是一种基于消息传递的分布式一致性算法。
有以下几种角色:
Client(客户):Client 向分布式系统发出请求,并等待响应。。例如浏览器
Propser(提案者):接受提倡客户端的请求,试图说服 Acceptor 同意它,并在发生冲突时充当协调者以推动协议向前发展。
Accpetor(Voter,接受者):投票的接受者,只有在形成法定人数(Quorum,一般即为majority多数派)时,提议才会最终被接受。
Learner(学习者):学习者充当协议的复制者。一旦接受者同意客户端请求,学习者可以采取行动(即:执行请求并向客户端发送响应)。
leader(领导者): Paxos 需要杰出的 Proposer(称为领导者)才能取得进展。许多进程可能认为他们是领导者,但协议只有在最终选择其中一个时才能保证进展。如果两个进程认为他们是领导者,他们可能会通过不断提出冲突更新来停止协议。
该协议是 Paxos 家族中最基本的。
一个成功的回合有 2 个阶段:阶段 1(分为a和b部分)和阶段 2(分为a和b部分):
Phase 1a:Prepare(准备)
proposer提出一条Prepare消息,编号为N,此N大于这个proposer之前提出提案编号。内容只有N,不包含其他,请求acceptors的quorum接受。
Phase 1b:Promise(承诺)
如果N大于此acceptor之前接受的任何提案编号则接受,发送Promise响应并返回之前接受的N的值,否则拒绝。
Phase 2a:Accept(接受)
proposer接受了Promise回应后,它需要为其提案设置一个值v 。如果任何 Acceptor 之前接受过任何提案,那么他们将把他们的值发送给 Proposer,Proposer 现在必须将其提案的值v设置为与 Acceptors 报告的最高提案编号相关的值,proposer会发出accept请求,此请求包含提案编号N,以及自己的提案内容。
Phase 2b:Accepted(已接受)
如果 Acceptor 从 Proposer 接收到 Accept 消息(n, v) ,当且仅当它尚未承诺(在 Paxos 协议的阶段 1b 中)只考虑标识符大于n的提议时,它必须接受它.,并发送一个已接受消息给 Proposer 和每个 Learner,否则忽略。
潜在问题:活锁
每个机器都是Proposer,会导致并发冲突很高,也就是每个节点都可能执行多次循环才能确定一条日志。极端情况是每个节点都在无限循环地执行2PC,也就是所谓的“活锁问题”。
改进,解决活锁,为了减少并发冲突,可以变多写为单写,选出一个Leader,只让Leader充当Proposer。其他机器收到写请求,都把写请求转发给Leader;或者让客户端把写请求都发给Leader。
Paxos 算法虽然很有效,但复杂的原理使它实现起来非常困难,截止目前,实现 Paxos 算法的开源软件很少,比较出名的有 Chubby、LibPaxos。
角色:
• Leader(领导者) :负责日志的同步管理,处理来自客户端的请求,与Follower保持heartBeat的联系;
• Follower(追随者) :响应 Leader 的日志同步请求,响应Candidate的邀票请求,以及把客户端请求到Follower的事务转发(重定向)给Leader;
• Candidate(候选者) :负责选举投票,集群刚启动或者Leader宕机时,状态为Follower的节点将转为Candidate并发起选举,选举胜出(获得超过半数节点的投票)后,从Candidate转为Leader状态。
原理:
所有节点的状态都是 Follower。由于没有 Leader,Followers 无法与 Leader 保持心跳(Heart Beat),因此,Followers 会认为 Leader 已经下线,进而转为 Candidate 状态。然后,Candidate 将向集群中其它节点请求投票,要求同意自己升级为 Leader,并根据先来先得和大小比较原则同意第一个且比自己标号大的其他Candidate 的请求。如果 某Candidate 收到超过半数节点的投票(N/2 + 1),它将获胜成为 Leader。
ps:每个节点的选举定时器超时时间都在 100~500 毫秒之间且并不一致(避免同时发起选举)。
应用:
Redis哨兵的选举采用的是Raft算法.
Zab协议是为分布式协调服务Zookeeper专门设计的一种支持崩溃恢复的原子广播协议 ,是Zookeeper保证数据一致性的核心算法。
Zab 协议包括两种基本的模式:崩溃恢复 和 消息广播
原理:
zxid:每次写成功的消息,都有一个全局唯一的标识,叫zxid,它越大表示数据越新。
投票优先级:优先比较zxid,如果相等,再比较机器的id,都按从大到小的顺序。
默认ZAB采用的算法是fast paxos算法。
每次选举都要把选举轮数加一,类似于zxid里的epoch字段,防止不同轮次的选举互相干扰。
每个进入looking状态的节点,最开始投票给自己,然后把投票消息发给其它机器。内容为<第几轮投票,被投节点的zxid,被投节点的编号>。
其他looking状态的节点收到后,
1 首先判断票是否有效。
是否有效的方法为看票的投票轮数和本地记载的投票轮数是否相等:
2.1 如果比本地投票轮数的小,丢弃。
2.2 如果比本地投票轮数的大
证明自己投票过期了,清空本地投票信息,
更新投票轮数和结果为收到的内容。
通知其他所有节点新的投票方案。
2.3 如果和本地投票轮数相等,按照投票的优先级比较收到的选票和自己投出去的选票。
如果收到的优先级大,
更新自己的投票为对方发过来投票方案,把投票发出去。
如果收到的优先级小,则忽略该投票。
如果收到的优先级相等,
则更新对应节点的投票。
3 每收集到一个投票后,查看已经收到的投票结果记录列表,看是否有节点能够达到一半以上的投票数。如果有达到,则终止投票,宣布选举结束,更新自身状态。然后进行发现和同步阶段。
否则继续收集投票。
消息广播
在zookeeper集群中,数据副本的传递策略就是采用消息广播模式。
当集群中的 Learner 完成了初始化状态同步,那么整个 zk 集群就进入到了正常工作模式 了。
消息广播具体步骤
1)客户端发起一个写操作请求。
2)Leader 服务器将客户端的请求转化为事务 Proposal 提案,同时为每个 Proposal 分配一个全局的ID,即zxid。
3)Leader 服务器为每个 Follower 服务器分配一个单独的队列,然后将需要广播的 Proposal 依次放到队列中取,并且根据 FIFO 策略进行消息发送。
4)Follower 接收到 Proposal 后,会首先将其以事务日志的方式写入本地磁盘中,写入成功后向 Leader 反馈一个 Ack 响应消息。
5)Leader 接收到超过半数以上 Follower 的 Ack 响应消息后,即认为消息发送成功,可以发送 commit 消息。
6)Leader 向所有 Follower 广播 commit 消息,同时自身也会完成事务提交。Follower 接收到 commit 消息后,会将上一条事务提交。
参考文档:
https://en.wikipedia.org/wiki/Paxos_(computer_science)
https://blog.csdn.net/m0_46690280/article/details/122614911
https://cloud.tencent.com/developer/article/1897339
https://www.jianshu.com/p/6d9017289cd5