目录
一、Zab协议的定义和作用?
一种支持崩溃恢复的原子广播协议,保证分布式事务的最终一致性
二、Zab协议实现
单一主进程处理事务请求与原子广播协议 + 保证一个全局的变更序列被顺序引用 + 当主进程出现异常的时候,整个zk集群依旧能正常工作
三、Zab协议实现的leader三阶段:发现、同步、广播
四、Zab协议核心:定义了事务请求的处理方式
五、Zab协议内容:原子广播+崩溃恢复
1)原子广播:1.客户端写请求 ---> 2.leader发事务提案zxid ---> 3.follower各自通过FIFO队列接收后,以事务日志写入本地磁盘,并反馈ack响应 ---> 4.leader收到超半数follower的ack后,自己提交事务,广播commit让所有follow也完成提交
2)崩溃恢复
六、Zab协议的特性
在leader已提交的事务最终被所有服务器提交 + 在 Leader 上被提出而没有被提交的事务被丢弃
七、保证数据一致性:主备模型
八、保证数据同步:单独的 FIFO 消息队列做到异步解耦
九、保证消息有序:全局单递增的唯一事务ID(zxid)
十、如何处理需要丢弃的Proposal ?
zxid = 高32位+低32位,每次选举leader,高32位+1,低32位从0开始
转载https://www.jianshu.com/p/2bceacd60b8a仅作为本人学习用。
一、Zab协议的定义和作用?
一种支持崩溃恢复的原子广播协议,保证分布式事务的最终一致性
定义:Zab = Zookeeper Atomic Broadcast(zookeeper原子广播协议)
作用:是一种支持崩溃恢复的原子广播协议,保证分布式事务的最终一致性(consistency)
二、Zab协议实现
单一主进程处理事务请求与原子广播协议 + 保证一个全局的变更序列被顺序引用 + 当主进程出现异常的时候,整个zk集群依旧能正常工作
1)单一的主进程来接收并处理客户端的事务请求 + Zab的原子广播协议
使用一个单一的主进程(Leader)来接收并处理客户端的事务请求(也就是写请求),并采用了Zab的原子广播协议,将服务器数据的状态变更以 事务proposal (事务提议)的形式广播到所有的副本(Follower)进程上去。
2)保证一个全局的变更序列被顺序引用。
Zookeeper是一个树形结构,很多操作都要先检查才能确定是否可以执行,比如P1的事务t1可能是创建节点"/a",t2可能是创建节点"/a/bb",只有先创建了父节点"/a",才能创建子节点"/a/b"。
为了保证这一点,Zab要保证同一个Leader发起的事务要按顺序被apply,同时还要保证只有先前Leader的事务被apply之后,新选举出来的Leader才能再次发起事务。
3)当主进程出现异常的时候,整个zk集群依旧能正常工作。
三、Zab协议实现的leader三阶段:发现、同步、广播
1)发现:要求zookeeper集群必须选举出一个Leader进程,同时Leader会维护一个Follower可用客户端列表。将来客户端可以和这些Follower节点进行通信。
2)同步:Leader要负责将本身的数据与Follower完成同步,做到多副本存储。这样也是提现了CAP中的高可用和分区容错。Follower将队列中未处理完的请求消费完成后,写入本地事务日志中。
3)广播:Leader可以接受客户端新的事务Proposal请求,将新的Proposal请求广播给所有的Follower。
四、Zab协议核心:定义了事务请求的处理方式
1)所有的事务请求必须由一个全局唯一的服务器来协调处理,这样的服务器被叫做 Leader服务器。其他剩余的服务器则是Follower服务器。
2)Leader服务器 负责将一个客户端事务请求,转换成一个事务Proposal,并将该Proposal分发给集群中所有的Follower服务器,也就是向所有Follower节点发送数据广播请求(或数据复制)
3)分发之后Leader服务器需要等待所有Follower服务器的反馈(Ack请求),在Zab协议中,只要超过半数的Follower服务器进行了正确的反馈后(收到半数以上的Follower的Ack请求),那么Leader就会再次向所有的Follower服务器发送Commit消息,要求其将上一个事务proposal进行提交。
五、Zab协议内容:原子广播+崩溃恢复
1)原子广播:1.客户端写请求 ---> 2.leader发事务提案zxid ---> 3.follower各自通过FIFO队列接收后,以事务日志写入本地磁盘,并反馈ack响应 ---> 4.leader收到超半数follower的ack后,自己提交事务,广播commit让所有follow也完成提交
将服务器数据的状态变更以事务proposal(事务提议)的形式广播到所有的副本(Follower)进程上去。6个步骤:
(step 1)客户端发起一个写操作请求。
(step 2)Leader 服务器将客户端的请求转化为事务 Proposal 提案,同时为每个 Proposal 分配一个全局的ID,即zxid。
(step 3)Leader 服务器为每个 Follower 服务器分配一个单独的FIFO队列,然后将需要广播的 Proposal 依次放到队列中取,并且根据 FIFO 策略进行消息发送。
(step 4)Follower 接收到 Proposal 后,会首先将其以事务日志的方式写入本地磁盘中,写入成功后向 Leader 反馈一个 Ack 响应消息。
(step 5)Leader 接收到超过半数以上 Follower 的 Ack 响应消息后,即认为消息发送成功,可以发送 commit 消息。
(step 6)Leader 向所有 Follower 广播 commit 消息,同时自身也会完成事务提交。Follower 接收到 commit 消息后,会将上一条事务提交。
2)崩溃恢复
Leader选举+数据恢复。场景:出现崩溃或者由于网络原因导致Leader服务器失去了与过半Follower的联系,就会进入崩溃恢复模式。
2.1)Leader选举:
Fast Leader Election(FLE,快速选举)。成为Leader的条件:max(epoch) -> max(zxid) -> max(serverid)
1)选 epoch 最大的
2)若 epoch 相等,选 zxid 最大的
3)若 epoch 和 zxid 相等,选择 server_id 最大的(zoo.cfg中的myid)
节点在选举开始时,都默认投票给自己,当接收其他节点的选票时,会根据上面的 Leader条件 判断并且更改自己的选票,然后重新发送选票给其他节点。当有一个节点的得票超过半数,该节点会设置自己的状态为 Leading ,其他节点会设置自己的状态为 Following。
2.2)数据恢复:
1)当整个集群启动过程中,或者当 Leader 服务器出现网络中弄断、崩溃退出或重启等异常时,Zab协议就会 进入崩溃恢复模式,选举产生新的Leader。
2)当选举产生了新的 Leader,同时集群中有过半的机器与该 Leader 服务器完成了状态同步(即数据同步)之后,Zab协议就会退出崩溃恢复模式,进入消息广播模式。
3)这时,如果有一台遵守Zab协议的服务器加入集群,因为此时集群中已经存在一个Leader服务器在广播消息,那么该新加入的服务器自动进入恢复模式:找到Leader服务器,并且完成数据同步。同步完成后,作为新的Follower一起参与到消息广播流程中。
六、Zab协议的特性
在leader已提交的事务最终被所有服务器提交 + 在 Leader 上被提出而没有被提交的事务被丢弃
1)Zab协议需要确保那些已经在 Leader 服务器上提交(Commit)的事务最终被所有的服务器提交。
2)Zab协议需要确保丢弃那些只在 Leader 上被提出而没有被提交的事务。
七、保证数据一致性:主备模型
主备模型(Leader和Follower):只有一台客户端(Leader)负责处理外部的写事务请求,然后Leader客户端将数据同步到其他Follower节点。主备模型保证了集群中各副本之间数据的一致性。
注:zookeeper 采用 Zab 协议的核心,就是只要有一台服务器提交了 Proposal,就要确保所有的服务器最终都能正确提交 Proposal。这也是 CAP/BASE 实现最终一致性的一个体现。
八、保证数据同步:单独的 FIFO 消息队列做到异步解耦
当Leader出现崩溃退出或者机器重启,亦或是集群中不存在超过半数的服务器与Leader保存正常通信,Zab就会再一次进入崩溃恢复,发起新一轮Leader选举并实现数据同步。
同步完成后又会进入消息广播模式,接收事务请求。
注:Leader 服务器与每一个 Follower 服务器之间都维护了一个单独的 FIFO 消息队列进行收发消息,使用队列消息可以做到异步解耦。 Leader 和 Follower 之间只需要往队列中发消息即可。如果使用同步的方式会引起阻塞,性能要下降很多。
九、保证消息有序:全局单递增的唯一事务ID(zxid)
在整个消息广播中,Leader会将每一个事务请求转换成对应的 proposal 来进行广播,并且在广播 事务Proposal 之前,Leader服务器会首先为这个事务Proposal分配一个全局单递增的唯一ID,称之为事务ID(即zxid),由于Zab协议需要保证每一个消息的严格的顺序关系,因此必须将每一个proposal按照其zxid的先后顺序进行排序和处理。
十、如何处理需要丢弃的Proposal ?
zxid = 高32位+低32位,每次选举leader,高32位+1,低32位从0开始
在 Zab 的事务编号 zxid 设计中,zxid是一个64位的数字。
其中,低32位可以看成一个简单的单增计数器,针对客户端每一个事务请求,Leader 在产生新的 Proposal 事务时,都会对该计数器加1。
其中,高32位则代表了 Leader 周期的 epoch 编号。epoch 编号可以理解为当前集群所处的年代,或者周期。每次Leader变更之后都会在 epoch 的基础上加1,这样旧的 Leader 崩溃恢复之后,其他Follower 也不会听它的了,因为 Follower 只服从epoch最高的 Leader 命令。Zab 协议通过 epoch 编号来区分 Leader 变化周期,能够有效避免不同的 Leader 错误的使用了相同的 zxid 编号提出了不一样的 Proposal 的异常情况。
每当选举产生一个新的 Leader ,就会从这个 Leader 服务器上取出本地事务日志充最大编号 Proposal 的 zxid,并从 zxid 中解析得到对应的 epoch 编号,然后再对其加1,之后该编号就作为新的 epoch 值,并将低32位数字归零,由0开始重新生成zxid。
当一个包含了上一个 Leader 周期中尚未提交过的事务 Proposal 的服务器启动时,当这台机器加入集群中,以 Follower 角色连上 Leader 服务器后,Leader 服务器会根据自己服务器上最后提交的 Proposal 来和 Follower 服务器的 Proposal 进行比对,比对的结果肯定是 Leader 要求 Follower 进行一个回退操作,回退到一个确实已经被集群中过半机器 Commit 的最新 Proposal。