Paxos算法
Paxos算法概述
Paxos算法是一种用在分布式系统中达成一致的经典算法,Paxos采取了一个我们非常熟悉的达成共识的方法:少数服从多数。只要有超过一半的机器认可某一个消息,那么最终就所有机器都接受这条消息并将它作为本次的结论。而竞选失败的少数派消息,就会被拒绝,并由第一个从客户端处接收到该消息的机器,向客户端发送失败结果,由客户端进行重试,去尝试在下一轮竞选中胜出。
Paxos中常见角色
1,Proposer(提案者/提议者):提议一个值,用于被投票决议。提案者是在选举中提出提案的人,在分布式系统中就是接受客户端写操作的人。一切行为都由Proposer提出提案开始,Paxos会将提案想要做的事抽象为一个“value”,去多态机器中传递,最后被所有机器接受。
2, Acceptor(附议者/接受者):对每个提议进行投票。Acceptor 从含义上来说就是除了当前Proposer以外的其他机器,他们之间完全平等和独立,Proposer需要争取超过半数(N/2+1)的 Acceptor 批准后,其提案才能通过,它倡导的“value”操作才能被所有机器所接受。
3,Learner(学习者/告知者):被告知投票的结果,不参与投票过程。 该角色是在达成决议时,对结论的学习者,也即是从其他节点“学习”最终提案内容,Learner将学习到这个值,并将其应用到本地系统中,从而确保所有节点最终达成一致。
Paxos算法特点分析
一个简单的提案
1,在最简单的情况下,假设有4台机器,其中一台机器收到了来自客户端的写操作请求,需要将这个请求同步给其他机器。
2,我们将这台收到请求的机器称为Proposer,因为它将要开始将收到的请求作为一个提案提议给其他的机器。
3,此时,其他的Acceptor都闲着呢,也没其他人找,所以当它们收到Proposer的提案时,就直接投票了,说可以可以,我是空的,赞成提案(同意提议)。
两个提案并发进行
1, 现在考虑一个更复杂的分布式场景,由于我们处于一个分布式环境中,每台机器都可能会同时收到来自两个不同客户端的请求。那么在这种情况下,我们该如何处理呢?
2,假设Proposer 1 的提案先达到了 Acceptor 1 和 Acceptor 2,而Proposer 2 的提案先达到了 Acceptor 3,其达到 Acceptor 1 和 Acceptor 2 时,由于机器已经投票给Proposer 1 了,所以Proposer 2 的提案遭到拒绝,Proposer 1 达到 Acceptor 3 的时候同样被拒。
3,Proposer 1 发现超过半数的Acceptor都接受了自己,所以放心大胆地发起要求,让所有Acceptor都按照自己的值来操作。而Proposer 2 发现只有不到半数的Acceptor支持自己,而有超过半数是支持Proposer 1 的值的,因此只能拒绝Client 2,并将自己也改为Proposer 1 的操作
4,到此为止,看起来没有问题,但是,这是因为恰好Acceptor的数量是单数,可以选出“大多数”,但是因为同时成为Proposer的机器数量是不确定的,因此是无法保证Acceptor的数量一定是单数的,这种情况就无法选出“大多数”了。
给提案添加编号
之前我们Proposer的提案都是只有操作内容的,现在我们给他加一个编号,即:
-
Proposer 1 的提案为:[n1, v1]
-
Proposer 2 的提案为:[n2, v2]
回到上面的困境
-
当Proposer 1 想要向Acceptor 2 寻求支持时,Acceptor 2 一看你的编号(1)比我已经支持的编号(2)要小,拒绝拒绝。此时Proposer 1 由于没有得到过半数的支持,会重新寻求支持。
-
而当Proposer 2 想要向Acceptor 1 寻求支持时,Acceptor 1 一看你的编号(2)比我已经支持的编号(1)要大,好的你是老大我听你的。此时Proposer 2 已经得到了超过半数的支持,可以进入正式生效的Accept阶段了。
可能发生的异常情况
异常情况:假设现在有三个Proposer同时收到客户端的请求,那么他们会生成全局唯一的不同编号,带着各自接收到的请求提案,去寻求Acceptor的支持。但假设他们都分别争取到了一个Acceptor的支持,此时由于Prepare阶段只会接受编号更大的提案,所以正常情况下只有Proposer 3 的提案会得到所有Acceptor的支持。
但假设这时候Proposer 3 机器挂了,无法进行下一步的Accept了,怎么办呢?那么所有Acceptor就会陷入持续的等待,而其他的Proposer也会一直重试然后一直失败。
为了解决这个问题,Paxos决定,允许Proposer在提案遭到过半数的拒绝时,更新自己的提案编号,用新的更大的提案编号,去发起新的Prepare请求。
那么此时Proposer 1 和Proposer 2 就会更新自己的编号,从【1】与【2】,改为比如【4】和【5】,重新尝试提案。这时即使Proposer 3 机器挂了,没有完成Accept,Acceptor也会由于接收到了编号更大的提案,从而覆盖掉Proposer 3 的提案,进入新的投票支持阶段。
ZAB协议
ZAB协议介绍
ZAB协议是ZooKeeper的核心组件,它通过确保ZooKeeper集群中所有服务器之间的事务广播顺序,实现了ZooKeeper数据的一致性.
ZAB协议核心
在zookeeper中只有一个Leader,并且只有Leader可以处理外部客户端的事务请求,Leader服务器将事务数据同步到所有的Follwer
从大体上看,ZAB协议可以细分为四个阶段
-
选举(Leader Election)
-
发现(Discovery)
-
同步(Synchronization)
-
广播(Broadcast)
但是在 ZooKeeper 的实现中,将发现和同步两部分内容合成数据恢复一部分,所以按实现划分可以分为三个阶段:
-
Leader 选举(Fast Leader Election)
-
数据恢复(Recovery Phase)
-
广播(Broadcast Phase)
但是在 ZooKeeper 的实现中,将发现和同步两部分内容合成数据恢复一部分,所以按实现划分可以分为三个阶段:
-
Leader 选举(Fast Leader Election)
-
数据恢复(Recovery Phase)
-
广播(Broadcast Phase)
但是在 ZooKeeper 的实现中,将发现和同步两部分内容合成数据恢复一部分,所以按实现划分可以分为三个阶段:
-
Leader 选举(Fast Leader Election)
-
数据恢复(Recovery Phase)
-
广播(Broadcast Phase)
ZAB模式
ZAB 协议按照功能的不同,有两种模式:一种是消息广播模式,另一种是崩溃恢复模式。消息广播模式用于处理客户端的请求,崩溃恢复模式用于在节点意外崩溃时能够快速恢复,继续对外提供服务,让集群达成高可用状态。
消息广播模式
-
客户端发起一个写操作请求
-
Leader 服务器处理客户端请求后将请求转换为 Proposal,同时为每个 Proposal 分配一个全局唯一 ID,即 ZXID
-
Leader 服务器与每个 Follower 之间都有一个队列,Leader 将消息发送到该队列
-
Follower 机器从队列中取出消息处理完(写入本地事务日志中)后,向 Leader 服务器发送 ACK 确认
-
Leader 服务器收到半数以上的 Follower 的 ACK 后,即认为可以发送 Commit
-
Leader 向所有的 Follower 服务器发送 Commit 消息
崩溃恢复模式
一旦 Leader 服务器出现崩溃或者由于网络原因导致 Leader 服务器失去了与过半 Follower 的联系,那么就会进入崩溃恢复模式。
Zookeeper 集群中为保证任何进程能够顺序执行,只能是 Leader 服务器接收写请求,其他服务器接收到客户端的写请求,也会转发至 Leader 服务器进行处理。
ZAB 协议崩溃恢复需满足以下2个请求:
-
确保已经被 Leader 提交的 proposal 必须最终被所有的 Follower 服务器提交
-
确保丢弃已经被 Leader 提出的但没有被提交的 Proposal
也就是新选举出来的 Leader 不能包含未提交的 Proposal,必须都是已经提交了的 Proposal 的 Follower 服务器节点,新选举出来的 Leader 节点中含有最高的 ZXID,所以,在 Leader 选举时,将 ZXID 作为每个 Follower 投票时的信息依据。这样做的好处是避免了 Leader 服务器检查 Proposal 的提交和丢弃工作。