读懂zookeeper一致协议:zab原子协议

zookeeper既然作为一个分布式协调中心,那么它要考虑以下几个问题:

1、如何保证写操作的原子性,即要么写入成功,要么写入失败?

2、当集群崩溃恢复时,如何恢复已提交数据?

3、如何保证集群的高可用?

针对这三个问题,zookeeper设计了zab原子协议。Zab协议 的全称是 Zookeeper Atomic Broadcast (Zookeeper原子广播)。

Zab 协议的特性

1)Zab 协议需要确保那些已经在 Leader 服务器上提交(Commit)的事务最终被所有的服务器提交。
2)Zab 协议需要确保丢弃那些只在 Leader 上被提出而没有被提交的事务。

那么,什么是zab协议?

Zab协议是为分布式协调服务Zookeeper专门设计的一种 支持崩溃恢复 的 原子广播协议 ,是Zookeeper保证数据一致性的核心算法。Zab借鉴了Paxos算法,但又不像Paxos那样,是一种通用的分布式一致性算法。它是特别为Zookeeper设计的支持崩溃恢复的原子广播协议。

基于该协议,zk实现了一种主备模型(即Leader和Follower模型)的系统架构来保证集群中各个副本之间数据的一致性。

这里的主备系统架构模型,就是指只有一台客户端(Leader)负责处理外部的写事务请求,然后Leader客户端将数据同步到其他Follower节点。

zab协议分为消息广播和崩溃恢复两种模式。

消息广播

消息广播主要是针对写请求。

zookeeper客户端会随机连接到zookeeper集群中的某个节点,如果是读请求,就直接从当前节点中读取数据;如果是写请求,那么节点就会向 Leader 发送写请求,由Leader依赖zab协议处理。

zab协议对于写操作的处理有些类似于分布式事务的两阶段提交。

在两阶段提交里,写操作分为投票(prepare)阶段和提交(commit)阶段。当一个写请求到达时,协调中心会将请求发给所有的参与者,如果所有的参与者都写入成功,那么就提交这个写请求,反之,只要有一个参与者写入失败就会回滚。在所有的参与者返回结果之前,协调中心会一直阻塞,耗时比较久。

zab协议在此基础上做了修改:将节点角色分为leader和follower,只需要半数以上的follower写入成功即可。

注:observer节点不参与写请求的过程,故本文不讨论。

具体过程如下:
1)客户端发起一个写操作请求。

2)Leader 服务器将客户端的请求转化为事务 Proposal 提案,同时为每个 Proposal 分配一个全局的ID,即zxid。

3)Leader 服务器与每一个 Follower 服务器之间都维护了一个单独的 FIFO 消息队列进行收发消息和异步解耦,将需要广播的 Proposal 依次放到队列中取,并且根据 FIFO 策略进行消息发送。

4)Follower 接收到 Proposal 后,会首先将其以事务日志的方式写入本地磁盘中,写入成功后向 Leader 反馈一个 Ack 响应消息。

5)Leader 接收到超过半数以上 Follower 的 Ack 响应消息后,即认为消息发送成功,可以发送 commit 消息。

6)Leader 向所有 Follower 广播 commit 消息,同时自身也会完成事务提交。Follower 接收到 commit 消息后,会将上一条事务提交。

只要有一台服务器commit了 Proposal,Leader就要确保所有的服务器最终都能正确提交 Proposal。这也是 CAP/BASE 实现最终一致性的一个体现。

崩溃恢复

Leader和Folower以一定间隔进行心跳连接,当超过一半的Follower与Leader失去联系,则认为Leader失效,zookeeper集群进入崩溃恢复模式。

崩溃恢复主要包括两部分:Leader选举 和 数据恢复

在 Zab 协议中,为了保证程序的正确运行,整个恢复过程需要选举出一个新的 Leader 服务器。因此 Zab 协议需要一个高效且可靠的 Leader 选举算法,从而确保能够快速选举出新的 Leader 。

Leader 选举算法不仅仅需要让 Leader 自己知道自己已经被选举为 Leader ,同时还需要让集群中的所有其他机器也能够快速感知到选举产生的新 Leader 服务器。

当有一个节点得到超过半数的节点的投票时,节点就会成为新的Leader。

由于Zab 协议崩溃恢复要求满足以下两个要求:
1)确保已经被 Leader 提交的 Proposal 必须最终被所有的 Follower 服务器提交。
2)确保丢弃已经被 Leader 提出的但是没有被提交的 Proposal。

因此,这个新Leader是在满足以下两个条件中的节点选出来的 :
1)节点不能包含未提交的 Proposal 。
即新选举的 Leader 必须都是已经提交了 Proposal 的 Follower 服务器节点。
2) 节点中含有最大的 zxid 。
这样做的好处是可以避免 选举出来后的Leader 服务器检查 Proposal 的提交和丢弃工作。

完成 Leader 选举后(新的 Leader 具有最高的zxid),在正式开始工作之前(即接收事务请求,然后提出新的 Proposal),Leader 服务器会首先确认事务日志中的所有的 Proposal 是否已经被集群中过半的服务器 Commit。

Leader 服务器需要确保所有的 Follower 服务器能够接收到每一条事务的 Proposal ,并且能将所有已经提交的事务 Proposal 应用到内存数据中。等到 Follower 将所有尚未同步的事务 Proposal 都从 Leader 服务器上同步过后并且应用到内存数据中以后,Leader 才会把该 Follower 加入到真正可用的 Follower 列表中。

zab在同步过程中,除了要保证已经commit的数据恢复和写入Follower服务器之外,还要丢弃没有commit的proposal。

当一个包含了上一个 Leader 周期中尚未提交过的事务 Proposal 的服务器以 Follower 角色连上 Leader 服务器后,Leader 服务器会根据自己服务器上最后提交的 Proposal 来和 Follower 服务器的 Proposal 进行比对,比对的结果肯定是 Leader 要求 Follower 进行一个回退操作,回退到一个确实已经被集群中过半机器 Commit 的最新 Proposal。

Leader和Follower如何对比两个Proposal之间谁先谁后呢?

事务编号zxid

在 Zab 的事务编号 zxid 设计中,zxid是一个64位的数字。

其中低32位可以看成一个简单的单增计数器,针对客户端每一个事务请求,Leader 在产生新的 Proposal 事务时,都会对该计数器加1。而高32位则代表了 Leader 周期的 epoch 编号。

epoch 编号可以理解为当前集群所处的年代,或者周期。每次Leader变更之后都会在 epoch 的基础上加1,这样旧的 Leader 崩溃恢复之后,其他Follower 也不会听它的了,因为 Follower 只服从epoch最高的 Leader 命令。

每当选举产生一个新的 Leader ,就会从这个 Leader 服务器上取出本地事务日志充最大编号 Proposal 的 zxid,并从 zxid 中解析得到对应的 epoch 编号,然后再对其加1,之后该编号就作为新的 epoch 值,并将低32位数字归零,由0开始重新生成zxid。

Zab 协议通过 epoch 编号来区分 Leader 变化周期,能够有效避免不同的 Leader 错误的使用了相同的 zxid 编号提出了不一样的 Proposal 的异常情况。

当数据同步完成后,zookeeper集群就正常接收事务请求了。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值