ZAB 协议是为分布式协调服务 ZooKeeper 专门设计的一种支持崩溃恢复的原子广播协议。在 ZooKeeper 中,主要依赖 ZAB 协议来实现分布式数据一致性,基于该协议,ZooKeeper 实现了一种主备模式的系统架构来保持集群中各个副本之间的数据一致性。算法
ZAB 是 Zookeeper 原子广播协议的简称,下面咱们来讨论协议的内容,注意:理论与实现是有区别的,若是你对协议的理论不感兴趣,能够直接跳过看实现。服务器
Zookeeper 客户端会随机链接到 Zookeeper 集群的一个节点,若是是读请求,就直接从当前节点中读取数据;若是是写请求,那么节点就会向 leader 提交事务,leader 会广播事务,只要有超过半数节点写入成功,该写请求就会被提交(类 2PC 协议)。架构
那么问题来了:分布式
带着这两个问题,咱们来看看 ZAB 协议是如何解决的。atom
ZAB 中的节点有三种状态设计
代码实现中多了一种:observing 状态,这是 Zookeeper 引入 Observer 以后加入的,Observer 不参与选举,是只读节点,跟 ZAB 协议没有关系日志
节点的持久状态code
在 ZAB 协议的事务编号 Zxid 设计中,Zxid 是一个 64 位的数字,其中低 32 位是一个简单的单调递增的计数器,针对客户端每个事务请求,计数器加 1;而高 32 位则表明 Leader 周期 epoch 的编号,每一个当选产生一个新的 Leader 服务器,就会从这个 Leader 服务器上取出其本地日志中最大事务的ZXID,并从中读取 epoch 值,而后加 1,以此做为新的 epoch,并将低 32 位从 0 开始计数。server
epoch:能够理解为当前集群所处的年代或者周期,每一个 leader 就像皇帝,都有本身的年号,因此每次改朝换代,leader 变动以后,都会在前一个年代的基础上加 1。这样就算旧的 leader 崩溃恢复以后,也没有人听他的了,由于 follower 只遵从当前年代的 leader 的命令。*blog
节点在一开始都处于选举阶段,只要有一个节点获得超半数节点的票数,它就能够当选准 leader。只有到达 Phase 3 准 leader 才会成为真正的 leader。这一阶段的目的是就是为了选出一个准 leader,而后进入下一个阶段。
协议并无规定详细的选举算法,后面咱们会提到实现中使用的 Fast Leader Election。
在这个阶段,followers 跟准 leader 进行通讯,同步 followers 最近接收的事务提议。这个一阶段的主要目的是发现当前大多数节点接收的最新提议,而且准 leader 生成新的 epoch,让 followers 接受,更新它们的 acceptedEpoch
一个 follower 只会链接一个 leader,若是有一个节点 f 认为另外一个 follower p 是 leader,f 在尝试链接 p 时会被拒绝,f 被拒绝以后,就会进入 Phase 0。
同步阶段主要是利用 leader 前一阶段得到的最新提议历史,同步集群中全部的副本。只有当 quorum 都同步完成,准 leader 才会成为真正的 leader。follower 只会接收 zxid 比本身的 lastZxid 大的提议。
到了这个阶段,Zookeeper 集群才能正式对外提供事务服务,而且 leader 能够进行消息广播。同时若是有新的节点加入,还须要对新节点进行同步。
值得注意的是,ZAB 提交事务并不像 2PC 同样须要所有 follower 都 ACK,只须要获得 quorum (超过半数的节点)的 ACK 就能够了。
协议的 Java 版本实现跟上面的定义有些不一样,选举阶段使用的是 Fast Leader Election(FLE),它包含了 Phase 1 的发现职责。由于 FLE 会选举拥有最新提议历史的节点做为 leader,这样就省去了发现最新提议的步骤。实际的实现将 Phase 1 和 Phase 2 合并为 Recovery Phase(恢复阶段)。因此,ZAB 的实现只有三个阶段:
前面提到 FLE 会选举拥有最新提议历史(lastZixd最大)的节点做为 leader,这样就省去了发现最新提议的步骤。这是基于拥有最新提议的节点也有最新提交记录的前提。
epoch
最大的epoch
相等,选 zxid 最大的epoch
和zxid
都相等,选择server id
最大的(就是咱们配置zoo.cfg
中的myid
)节点在选举开始都默认投票给本身,当接收其余节点的选票时,会根据上面的条件更改本身的选票并从新发送选票给其余节点,当有一个节点的得票超过半数,该节点会设置本身的状态为 leading,其余节点会设置本身的状态为 following。
这一阶段 follower 发送它们的 lastZixd 给 leader,leader 根据 lastZixd 决定如何同步数据。这里的实现跟前面 Phase 2 有所不一样:Follower 收到 TRUNC 指令会停止 L.lastCommittedZxid 以后的提议,收到 DIFF 指令会接收新的提议。
history.lastCommittedZxid:最近被提交的提议的 zxid
history:oldThreshold:被认为已经太旧的已提交提议的 zxid
通过上面的分析,咱们能够来回答开始提到的两个问题
这篇文章是根据我对 ZAB 协议的理解写成的,若是以为有些细节没有讲清楚,能够看后面的参考资料,我主要是参考这篇论文的。
参考资料
ZooKeeper’s atomic broadcast protocol:Theory and practice
原文:http://blog.jobbole.com/104985/