Zookeeper深入原理(3) - Zab协议

Zookeeper深入原理(3) - Zab协议,第1张

Zab协议全称是 Zookeeper Atomic BroadCast (Zookeeper 原子广播),Zookeeper是通过Zab协议来保证分布式事务的一致性。

1Zab协议是zookeeper专门设计的一种 支持崩溃恢复 的 原子广播协议, 是Zookeeper保证数据一致性的核心算法。

2在Zookeeper当中依赖Zab协议来保证数据的一致性,基于这个协议,zookeeper实现了一种主备模型,(Leader+Follower)的架构在保证集群中各个副本之间数据的一致性。 Leader负责处理写事务请求,然后Leader将数据同步到Follower节点上。

3zookeeper客户端会随机连接到集群中的一个节点上,如果是读请求,就会从当前节点进行读取数据,如果是写的请求,就会将事务请求提交到Leader节点,leader节点接收到事务提交,就会广播该事务,如果超过一半节点写入成功,那么该事务就会被提交。

1Zab协议需要确保那些已经在leader服务器上提交的事务最终被所有服务器提交。

2Zab协议需要确保那些在leader服务器上被提出而没有被提交的事务。

1使用主进程(leader)来接受客户端并处理客户端的事务请求,并采用Zab的原子广播协议,将服务器数据变更的状态以事务提议的形式广播到所有的follower副本上去。

2当主进程出现异常,整个zk集群依然能够正常运行。

Zab协议每个leader需要经过三个阶段:发现、同步、广播

发现 :要求Zookeeper集群必须选取出一个leader进程,同时leader需要维护一个follower可用客户端列表,将来客户端可以和这些follower进行通信。

同步 :Leader要将本身的数据与follower进行同步,实现多副本存储,也体现了CAP中的高可用和分区容错。follower将队列中未处理完的消息消费完成后,写入到本地日志中。

广播 :leader接受客户端提出的事务请求,将新的事务请求广播给follower节点。

Zab协议核心:定义了事务请求的处理方式。

1所有的事务请求必须由一个全局唯一的服务器来协调处理(Leader 服务器),其余的服务器是follower服务器。

2leader服务器负责将客户端提出的事务请求,转换成一个事务proposal,并将事务proposal分发给集群中follower服务器,也就是向所有follower节点发送数据广播请求。

3分发之后leader服务器需要等待follower服务器的反馈,在Zab协议中,只要超过半数的follower服务器进行确认了,那么leader就会再次向所有的follower发送commit消息,要求将上一个事务进行提交。

Zab协议包括两种模式: 崩溃恢复 和 消息广播

协议过程

当整个集群启动过程中,或者leader服务器出现宕机或者网络终端等异常时,Zab协议就会进入崩溃恢复模式,选举出新的leader。

当选举出新的leader之后,同时集群中有过半的服务器与该leader服务器完成了状态同步(数据同步),Zab协议就会退出崩溃恢复模型,进入消息广播模式。

如果新增一台服务器加入集群中,当前集群中已经选举出leader,那么加入进来的服务器自动进入恢复模式,找到leader服务器进行状态同步,完成同步后,与其他follower一起参与到广播流程中。

Zookeeper集群中,数据副本的传递策略采用的是消息广播模式。Zab协议中Leader等待follower 的ACK反馈消息,当到达半数以上follower成功反馈即可,不需要等所有的follower全部反馈。

leader服务器出现宕机和网络原因等导致leader与过半的follower服务器不能联系,就会自动进入崩溃恢复模式。

在Zab协议中,为了保证程序的正常运行,整个恢复过程结束后需要重新选出一个leader服务器,因此Zab协议需要一个高效且可靠的算法,来保证快速选举出leader。

Leader算法不仅让leader自己知道自己已经被选取为leader,还需要让集群中的所有服务器快速的感知到选举出的新leader服务器。

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

Zab 协议如何保证数据一致性

假设两种异常情况:

1、一个事务在 Leader 上提交了,并且过半的 Folower 都响应 Ack 了,但是 Leader 在 Commit 消息发出之前挂了。

2、假设一个事务在 Leader 提出之后,Leader 挂了。

要确保如果发生上述两种情况,数据还能保持一致性,那么 Zab 协议选举算法必须满足以下要求:

Zab 协议崩溃恢复要求满足以下两个要求 :

1) 确保已经被 Leader 提交的 Proposal 必须最终被所有的 Follower 服务器提交 。

2) 确保丢弃已经被 Leader 提出的但是没有被提交的 Proposal 。

根据上述要求

Zab协议需要保证选举出来的Leader需要满足以下条件:

1) 新选举出来的 Leader 不能包含未提交的 Proposal 。

即新选举的 Leader 必须都是已经提交了 Proposal 的 Follower 服务器节点。

2) 新选举的 Leader 节点中含有最大的 zxid 。

这样做的好处是可以避免 Leader 服务器检查 Proposal 的提交和丢弃工作。

Zab 如何数据同步

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

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

Zab 数据同步过程中,如何处理需要丢弃的 Proposal

在 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 的异常情况。

基于以上策略

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

本文根据https://wwwjianshucom/p/2bceacd60b8a进行编写。

Tomcat启动,初始化webcontext;

初始化spring, spring初始某些些bean,这些bean包括了zookeeper的连接相关的bean;

这时zkClient(独立线程)已经连接上服务器了,但是classloader没有加载到org/apache/zookeeper/proto/SetWatches类;

spring初始化失败,导致Tomcat webcontext初始化也失败,应用在挂起状态,但zkClient线程还是正常的;

zookeeper服务器重启,zkClient开始重连,连接上zookeeper服务器;

zkClient触发watch的一些代码,ClassLoader尝试加载org/apache/zookeeper/proto/SetWatches类,但是发现找不到类,于是抛出异常;

zkClient捕获到异常,认为重连失败,close掉connection,休眠几秒之后,再次重连;

于是出现了zkClient反复重试连接zookeeper服务器,而且都是秒连秒断的情况。转载,仅供参考。

这篇文章我们重点理解Zookeeper选举机制的思路。

一,Zookeeper选举过程中服务器的状态。

LOOKING:寻找leader状态,该状态下,服务器认为当前集群没有leader,会发起leader选举。在选举过程中,所有服务器的状态都是LOOKING。

FOLLOWING:跟随者状态,该状态下,当前服务器是follower,并且知道leader是谁。此时选举已经结束。

LEADING:领导者状态,该状态下,当前服务器是leader,会与follower维持心跳检测。此时选举已经结束。

OBSERVING:观察者状态,该状态下的服务器是observer,不参与选举。

二,Zookeeper选票数据结构

每个服务器在进行leader选举时,都会发送以下几个关键属性信息:

logicalclock:投票轮次,自增的,volatile的,初始值为1,也就是第一轮选举。

state:当前服务器的状态。

self_id:当前服务器的myid。

self_zxid:当前服务器的最新的zxid。

vote_id:当前服务器推举的leader服务器的myid。

vote_zxid:当前服务器推举的leader服务器的最新的zxid。

三,Zookeeper选举算法

从340版本开始,Zookeeper使用FastLeaderElection选举算法,可以解决之前的LeaderElection算法收敛慢的问题。更为重要的是,FastLeaderElection算法解决了脑裂问题,保证leader的唯一性。也就是说,从Zookeeper340版本开始,Zookeeper可能存在的问题只有2个了:

1,客户端没有缓存。

2,没有自我保护机制。

四,Zookeeper选举流程

1,自增选举轮次。

Zookeeper选举机制有一个前提条件:在一个轮次的选举中,所有选票必须属于该轮次。在选举的某一时刻,确实可能存在某张选票不属于该轮次的情况。所以Zookeeper在选举过程中,始终会先核对选票的轮次。

2,初始化选票。

每个服务器在广播自己的选票时,都会先清空投票箱,这个投票箱存放的是所有接收到的来自其他服务器的选票。投票箱中只记录每个服务器的最后一次投票,如果服务器更新自己的投票,则其他服务器会更新该服务器的选票。

举个例子:服务器2投票给服务器3,服务器3投票给服务器1,则服务器1的投票箱中有如下记录

(2,3),(3,1),(1,1)

当然,这里的选票的结构是简化版的,如果加上选举轮次logicalclock,可能是这样的:

(8,2,3),(8,3,1),(8,1,1)

第一位代表当前的选举轮次,第8轮选举。

3,发送初始化选票。

每个服务器在投票开始阶段,都把票投给自己,然后通过广播通知其他服务器。

4,接收外部选票。

每台服务器都会尝试从其他服务器获取选票,并保存到自己的投票箱。

5,判断选举轮次logicalclock。

确保是同一轮次的投票。如果当前服务器发现自己的轮次落后了,则自增logicalclock,然后重新发送广播告诉其他服务器。

6,选票PK确认自己最终的投票。

注意,在这个阶段,每台服务器都可能改变自己的想法,重新确定把选票投给谁。

有3条规则:

第一条规则:如果当前服务器的logicalclock小于其他服务器,说明自己的选举轮次过期了,此时更新自己的logicalclock,然后重新把自己的选票发送出去。

第二条规则:如果当前服务器的logicalclock等于其他服务器,说明大家进行的是同一轮次的选举,此时比较二者的vote_zxid,vote_zxid大者获胜。如果当前服务器输了,则更新自己的投票为胜者,然后广播告诉其他服务器。

第三条规则:如果当前服务器的logicalclock等于其他服务器,说明大家进行的是同一轮次的选举,此时比较二者的vote_zxid,如果vote_zxid也相等,则比较二者的vote_myid,vote_myid大者获胜。如果当前服务器输了,则更新自己的投票为胜者,然后广播告诉其他服务器。

7,统计选票。

如果已经确定有过半服务器认可了自己的投票,则终止投票。否则继续接收其他服务器的投票。

8,更新服务器状态。

投票结束后,服务器更新自己的状态serverState,如果投给自己的选票过半了,则将自己更新为LEADING,否则将自己更新为FOLLOWING。

这里思考一个问题:Zookeeper启动阶段,myid最大的服务器是不是一定会被选举为leader?

DABAN RP主题是一个优秀的主题,极致后台体验,无插件,集成会员系统
网站模板库 » Zookeeper深入原理(3) - Zab协议

0条评论

发表评论

提供最优质的资源集合

立即查看 了解详情