参考文章:
1.zookeeper中的ZAB协议理解
https://blog.csdn.net/junchenbb0430/article/details/77583955
2.ZAB协议和Paxos算法
http://blog.jobbole.com/109589/
3.聊聊zookeeper的ZAB算法
https://www.jianshu.com/p/400a44edee88
最近面试中问到zookeeper 使用什么协议保证数据的一致性,之前很多时候会不假思索的回答是 paxos 的改进协议。但是,这显然不是一个资深开发工程师该有的回答,
那么本篇将会细致的带你了解Zookeper 的一致性性协议(ZAB)协议。ZAB 的全称是 zookeeper Atomic Broadcast
即 zookeeper 原子广播协议(支持崩溃恢复)。
Zookeeper 特性
首先由于 ZAB 协议是针对于 Zookeeper 设计的一些协议,我们看看 Zookeeper 有那些分布式一致性特性 :
顺序一致性:
从同一个客户端发起的事务请求,最终将会严格地按照其发起顺序被应用到Zookeeper 中。
原子性:
所有事物请求的处理结果在整个集群中所有机器上的应用情况是一致的,也就是说,要么整个集群的所有机器都成功应用了某一个事务,要么都没有应用,要么都没有应用,一定不存在集群中部分应用了某一个事务,而另外一个部分没有应用的情况。
单一视图 (Single System Image):
无论客户端连接的是那个Zookeeper 服务器,其看到的服务端数据模型都是一致的。
可靠性:
一旦服务端成功应用了一个事务,并完成对客户端的响应,那么该事务所引起的服务端状态将会一直保留下来,除非有另一个事务又对其进行了变更。
实时性:
Zookeeper 仅仅保证在一定的时间段内,客户端最终一定能够从服务端上读取到最新的数据状态。
ZAB 协议的一些相关概念
下面我们对 ZAB 协议的一些相关概念一一介绍 :
ZAB 协议 其实是 2PC 协议的一个升级版,下面这篇文章对 2PC , 3PC 基础协议进行下介绍 :
2PC 全称: Two phase commit, 即 两阶段提交协议。
但是ZAB 协议不同于 常见的 2阶段提交协议,对协议进行了修改,利用 过半投票法 与 恢复策略 解决了
1.脑裂(数据不一致性)
2. 单点问题(无限等待)等 2阶段提交协议所遇到的问题。
首先在了解ZAB 协议前,我们来看一下 ZAB 协议中的一些名词,
Leader 领导者,
功能:处理 Client 发送的 request请求 , 请求这里指的是更改请求。向 Follower 发送协议 Proposal, 处理Follower 的ACK 信息。并对过半Follower 的ACK 发起 Commit 。
注意 Leader 本身也有可能是 Client. Follower / Client 都有可能是 Client .
Follower 跟随者:
功能: 作为Client 可以发起 Request 请求。处理 Leader 提出的 Proposal ,给出 ack. 接受Leader 提出的 Commit.
在Zookeeper 新版本 增加了 Observer 观察者 的概念:
Observer 观察者:
只做数据同步,转发请求,并不对 协议 Proposal 提出 ACK 等信息。也不参与 崩溃恢复阶段的 Leader 选举。
ZAB 协议的几个阶段
下面我们看下ZAB / Zookeeper 运行的3个阶段:
消息广播阶段:
崩溃恢复:
数据同步:
崩溃恢复阶段 :
当整个集群在启动过程中,或者是当Leader 服务器出现网络中断,崩溃退出 与 重启 等异常情况时,ZAB协议就会进入 恢复模式并产生新的Leader。
2. 产生的条件:
2.1.当Leader出现崩溃推出或机器重启,
2.2.集群中不存在过半的服务器与该Leader 保证正常通信。
在重新开始新一轮的原子广播事务操作之前,所有进程会 先使用崩溃恢复协议 来达到一个一致的状态,于是整个ZAB 流程进入到崩溃恢复模式。
3. ZAB协议崩溃恢复要求满足下2个要求: 如
3.1. 确保已经被leader提交的proposal必须最终被所有的follower服务器提交。
3.2. 确保丢弃已经被leader出的但是没有被提交的proposal。
4. 根据上述要求,
新选举出来的leader不能包含未提交的proposal,即新选举的leader必须都是已经提交了的proposal的follower服务器节点。同时,新选举的leader节点中含有最高的ZXID。这样做的好处就是可以避免了leader服务器检查proposal的提交和丢弃工作。
5. leader服务器发生崩溃时分为如下场景:
5.1. leader在提出proposal时未提交之前崩溃,则经过崩溃恢复之后,新选举的leader一定不能是刚才的leader。因为这个leader存在未提交的proposal。
5.2 leader在发送commit消息之后,崩溃。即消息已经发送到队列中。经过崩溃恢复之后,参与选举的follower服务器(刚才崩溃的leader有可能已经恢复运行,也属于follower节点范畴)中有的节点已经是消费了队列中所有的commit消息。即该follower节点将会被选举为最新的leader。剩下动作就是数据同步过程。
消息广播阶段:
当即集群已经有过半的Follower 服务器完成了 和 Leader 服务器的状态同步,那么整服务架构 就可以进入消息广播模式了。
消息广播阶段其实就是对 阶段提交协议 的 一个最主要的实现步奏。
我们看一下这个流程图:
我们对流程图进行下讲解:
在进入消息广播阶段后,一个新协议的产生主要经过如下几个步奏:
1.Client 在接收到 Request 后都会转发给 Leader 进行处理。Leader 发起一次预提交,先向集群中的Follow 发送 ZXID = curZXID + 1 的协议。
2.每个Follower 在接收到 事务 Proposal 之后, 都会首先将其以事务日志的形式写入到本地磁盘中,并且在写入成功后反馈给Leader 一个 Ack响应。
3.当Leader 在其接收到超过半数的Follower 的 Ack 响应后,就会广播一个 Commit 消息给所有的 Follow 服务器 以通知其进行事务提交,同时Leader 自身也会完成对事务的提交,而每一个服务器在接收到 Commit 消息后,也会完成对 事务 Proposal 的确认。
数据同步:
在集群中新加入节点 或者 新Leader 选举出来后,都需要进行数据同步。Leader 服务器需要确保 所有的 follower 能够接收到每一条事务 Proposal , 并且能够正确地将所有已经提交了的事务 Proposal 应用到内存中去。
Leader 服务器会为每一个 Follower 准备一个队列,并将那些没有被 各Foliower 服务器同步的事务以 Proposal 发送给 Follower 服务器,并且在每一个 Proposal 发送一个 commit, 以表示该事务已经被提交。
ZXID
ZXID 是一个 64位的数字,
其中低32位 是一个单调递增的计数器,针对客户端的每一个事务请求,Leader 服务器在产生一个新的事务Proposal 的时候,会针对该计数器其进行 + 1
高32位代表了 Leader 的 epoch 编号,
每当选举产生一个新的Leader 服务器,就会从这个 Leader 服务器上取出本地日志中最大事务Proposal 的 ZXID , 并从该 ZXID 中解析出对应的 epoch 值, 对其进行 + 1操作,之后以此编号做为新的 epoch , 并将低32位置0 来生成新的 ZXID .