1.1. Paxos
Paxos 算法解决的问题是一个分布式系统如何就某个值(决议)达成一致。一个典型的场景是,
在一个分布式数据库系统中,如果各节点的初始状态一致,每个节点执行相同的操作序列,那么
他们最后能得到一个一致的状态。为保证每个节点执行相同的命令序列,需要在每一条指令上执
行一个"一致性算法"以保证每个节点看到的指令一致。zookeeper 使用的 zab 算法是该算法的
一个实现。 在 Paxos 算法中,有三种角色:Proposer,Acceptor,Learners
Paxos 三种角色:Proposer ,Acceptor ,Learners
Proposer :
只要 Proposer 发的提案被半数以上 Acceptor 接受,Proposer 就认为该提案里的 value 被选定
了。
Acceptor :
只要 Acceptor 接受了某个提案,Acceptor 就认为该提案里的 value 被选定了。
Learner :
Acceptor 告诉 Learner 哪个 value 被选定,Learner 就认为那个 value 被选定。
Paxos 算法分为两个阶段。具体如下:
阶段一 (准 leader 确定 ) :
(a) Proposer 选择一个提案编号N,然后向半数以上的Acceptor发送编号为N的Prepare请求。
(b) 如果一个 Acceptor 收到一个编号为 N 的 Prepare 请求,且 N 大于该 Acceptor 已经响应过的
所有 Prepare 请求的编号,那么它就会将它已经接受过的编号最大的提案(如果有的话)作为响
应反馈给 Proposer,同时该 Acceptor 承诺不再接受任何编号小于 N 的提案。
阶段二 ( leader 确认) :
(a) 如果 Proposer 收到半数以上 Acceptor 对其发出的编号为 N 的 Prepare 请求的响应,那么它
就会发送一个针对[N,V]提案的 Accept 请求给半数以上的 Acceptor。注意:V 就是收到的响应中
编号最大的提案的 value,如果响应中不包含任何提案,那么 V 就由 Proposer 自己决定。
(b) 如果 Acceptor 收到一个针对编号为 N 的提案的 Accept 请求,只要该 Acceptor 没有对编号
大于 N 的 Prepare 请求做出过响应,它就接受该提案。
1.2. Zab
ZAB( ZooKeeper Atomic Broadcast , ZooKeeper 原子消息广播协议)协议包括两种基本的模
式:崩溃恢复和消息广播
- 当整个服务框架在启动过程中,或是当 Leader 服务器出现网络中断崩溃退出与重启等异常情
况时,ZAB 就会进入恢复模式并选举产生新的 Leader 服务器。
- 当选举产生了新的 Leader 服务器,同时集群中已经有过半的机器与该 Leader 服务器完成了
状态同步之后,ZAB 协议就会退出崩溃恢复模式,进入消息广播模式。
- 当有新的服务器加入到集群中去,如果此时集群中已经存在一个 Leader 服务器在负责进行消
息广播,那么新加入的服务器会自动进入数据恢复模式,找到 Leader 服务器,并与其进行数
据同步,然后一起参与到消息广播流程中去。
以上其实大致经历了三个步骤:
-
崩溃恢复:主要就是 Leader 选举过程
-
数据同步: Leader 服务器与其他服务器进行数据同步
-
消息广播: Leader 服务器将数据发送给其他 服务器
说明:zookeeper 章节对该协议有详细描述。
1.3. Raft
与 Paxos 不同 Raft 强调的是易懂(Understandability),Raft 和 Paxos 一样只要保证 n/2+1 节
点正常就能够提供服务;raft 把算法流程分为三个子问题:选举(Leader election)、日志复制
(Log replication)、安全性(Safety)三个子问题。
1.3.1. 角色
Raft 把集群中的节点分为三种状态:Leader、 Follower 、Candidate,理所当然每种状态负
责的任务也是不一样的,Raft 运行时提供服务的时候只存在 Leader 与 Follower 两种状态;
Leader (领导 者 - 日志 管理 )
负责日志的同步管理,处理来自客户端的请求,与 Follower 保持这 heartBeat 的联系;
Follower (追随者 - 日志 同步 )
刚启动时所有节点为Follower状态,响应Leader的日志同步请求,响应Candidate的请求,
把请求到 Follower 的事务转发给 Leader;
Candidate (候选者 - 负责 选票 )
负责选举投票,Raft 刚启动时由一个节点从 Follower 转为 Candidate 发起选举,选举出
Leader 后从 Candidate 转为 Leader 状态;
1.3.2. Term( ( 任期)
在 Raft 中使用了一个可以理解为周期(第几届、任期)的概念,用 Term 作为一个周期,每
个 Term 都是一个连续递增的编号,每一轮选举都是一个 Term 周期,在一个 Term 中只能产生一
个 Leader;当某节点收到的请求中 Term 比当前 Term 小时则拒绝该请求。
1.3.3. 选举(Election )
选举 定时 器
Raft 的选举由定时器来触发,每个节点的选举定时器时间都是不一样的,开始时状态都为
Follower 某个节点定时器触发选举后 Term 递增,状态由 Follower 转为 Candidate,向其他节点
发起 RequestVote RPC 请求,这时候有三种可能的情况发生:
1:该 RequestVote请求接收到 n/2+1(过半数)个节点的投票,从Candidate 转为 Leader,
向其他节点发送 heartBeat 以保持 Leader 的正常运转。
2:在此期间如果收到其他节点发送过来的 AppendEntries RPC 请求,如该节点的 Term 大
则当前节点转为 Follower,否则保持 Candidate 拒绝该请求。
3:Election timeout 发生则 Term 递增,重新发起选举
在一个 Term 期间每个节点只能投票一次,所以当有多个 Candidate 存在时就会出现每个
Candidate 发起的选举都存在接收到的投票数都不过半的问题,这时每个 Candidate 都将 Term
递增、重启定时器并重新发起选举,由于每个节点中定时器的时间都是随机的,所以就不会多次
存在有多个 Candidate 同时发起投票的问题。
在 Raft 中当接收到客户端的日志(事务请求)后先把该日志追加到本地的 Log 中,然后通过
heartbeat 把该 Entry 同步给其他 Follower,Follower 接收到日志后记录日志然后向 Leader 发送
ACK,当 Leader 收到大多数(n/2+1)Follower 的 ACK 信息后将该日志设置为已提交并追加到
本地磁盘中,通知客户端并在下个 heartbeat 中 Leader 将通知所有的 Follower 将该日志存储在
自己的本地磁盘中。
1.3.4. 安全性(Safety )
安全性是用于保证每个节点都执行相同序列的安全机制如当某个 Follower 在当前 Leader commit
Log 时变得不可用了,稍后可能该 Follower 又会倍选举为 Leader,这时新 Leader 可能会用新的
Log 覆盖先前已 committed 的 Log,这就是导致节点执行不同序列;Safety 就是用于保证选举出
来的 Leader 一定包含先前 commited Log 的机制;
选举安全性(Election Safety):每个 Term 只能选举出一个 Leader
Leader 完整性(Leader Completeness):这里所说的完整性是指 Leader 日志的完整性,
Raft 在选举阶段就使用 Term 的判断用于保证完整性:当请求投票的该 Candidate 的 Term 较大
或 Term 相同 Index 更大则投票,该节点将容易变成 leader。
1.3.5. raft 协议和 zab 协议区别
相同点
采用 quorum 来确定整个系统的一致性,这个 quorum 一般实现是集群中半数以上的服务器,
zookeeper 里还提供了带权重的 quorum 实现.
都由 leader 来发起写操作.
都采用心跳检测存活性
leader election 都采用先到先得的投票方式
不同点
zab 用的是 epoch 和 count 的组合来唯一表示一个值, 而 raft 用的是 term 和 index
zab 的 follower 在投票给一个 leader 之前必须和 leader 的日志达成一致,而 raft 的 follower
则简单地说是谁的 term 高就投票给谁
raft 协议的心跳是从 leader 到 follower, 而 zab 协议则相反
raft 协议数据只有单向地从 leader 到 follower(成为 leader 的条件之一就是拥有最新的 log),
而 zab 协议在 discovery 阶段, 一个 prospective leader 需要将自己的 log 更新为 quorum 里面
最新的 log,然后才好在 synchronization 阶段将 quorum 里的其他机器的 log 都同步到一致.