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

2024-05-20 22:37

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

Zab协议全称是 Zookeeper Atomic BroadCast (Zookeeper 原子广播),Zookeeper是通过Zab协议来保证分布式事务的一致性。
  
 1.Zab协议是zookeeper专门设计的一种 支持崩溃恢复 的 原子广播协议, 是Zookeeper保证数据一致性的核心算法。
  
 2.在Zookeeper当中依赖Zab协议来保证数据的一致性,基于这个协议,zookeeper实现了一种主备模型,(Leader+Follower)的架构在保证集群中各个副本之间数据的一致性。 Leader负责处理写事务请求,然后Leader将数据同步到Follower节点上。 
  
 3.zookeeper客户端会随机连接到集群中的一个节点上,如果是读请求,就会从当前节点进行读取数据,如果是写的请求,就会将事务请求提交到Leader节点,leader节点接收到事务提交,就会广播该事务,如果超过一半节点写入成功,那么该事务就会被提交。
  
 
  
  
 1.Zab协议需要确保那些已经在leader服务器上提交的事务最终被所有服务器提交。
  
 2.Zab协议需要确保那些在leader服务器上被提出而没有被提交的事务。
  
 1.使用主进程(leader)来接受客户端并处理客户端的事务请求,并采用Zab的原子广播协议,将服务器数据变更的状态以事务提议的形式广播到所有的follower副本上去。
  
 2.当主进程出现异常,整个zk集群依然能够正常运行。
  
 Zab协议每个leader需要经过三个阶段:发现、同步、广播
  
  发现 :要求Zookeeper集群必须选取出一个leader进程,同时leader需要维护一个follower可用客户端列表,将来客户端可以和这些follower进行通信。
  
  同步 :Leader要将本身的数据与follower进行同步,实现多副本存储,也体现了CAP中的高可用和分区容错。follower将队列中未处理完的消息消费完成后,写入到本地日志中。
  
  广播 :leader接受客户端提出的事务请求,将新的事务请求广播给follower节点。
  
 Zab协议核心:定义了事务请求的处理方式。
  
 1.所有的事务请求必须由一个全局唯一的服务器来协调处理(Leader 服务器),其余的服务器是follower服务器。
  
 2.leader服务器负责将客户端提出的事务请求,转换成一个事务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://www.jianshu.com/p/2bceacd60b8a进行编写。

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

2. ZooKeeper简介与协议

ZooKeeper 是一个开放源码的分布式协调服务,提供类似UNIX文件系统、通知机制
  
 分布式应用程序可以基于 Zookeeper 实现诸如数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master 选举、分布式锁和分布式队列等功能,另一篇文章介绍
  
 Zookeeper 提供一个多层级的节点命名空间(节点称为 znode)。与文件系统不同的是,这些节点都可以设置关联的数据,而文件系统中只有文件节点可以存放数据而目录节点不行。
  
 Zookeeper 为了保证高吞吐和低延迟,在内存中维护了这个树状的目录结构,这种特性使得 Zookeeper 不能用于存放大量的数据,每个节点的存放数据上限为1M。
  
 (1)PERSISTENT-持久节点
   除非手动删除,否则节点一直存在于 Zookeeper 上
  
 (2)EPHEMERAL-临时节点
   临时节点的生命周期与客户端会话绑定,一旦客户端会话失效(客户端与zookeeper 连接断开不一定会话失效),那么这个客户端创建的所有临时节点都会被移除。
  
 (3)PERSISTENT_SEQUENTIAL-持久顺序节点
   基本特性同持久节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。
  
 (4)EPHEMERAL_SEQUENTIAL-临时顺序节点
   基本特性同临时节点,增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。
  
 Zookeeper 允许客户端向服务端的某个 Znode 注册一个 Watcher 监听,当服务端的一些指定事件触发了这个 Watcher,服务端会向指定客户端发送一个事件通知来实现分布式的通知功能,然后客户端根据 Watcher 通知状态和事件类型做出业务上的改变。
  
 工作过程:
  
 watcher的特点:
  
 注意事项:
  
 Zookeeper选择了CAP理论中的CP
  
 zookeeper在选举leader时,会停止服务,直到选举成功之后才会再次对外提供服务,这个时候就说明了服务不可用,但是在选举成功之后,因为一主多从的结构,zookeeper在这时还是一个高可用注册中心,只是在优先保证一致性的前提下,zookeeper才会顾及到可用性
  
 服务器具有四种状态,分别是 LOOKING、FOLLOWING、LEADING、OBSERVING。
   (1)LOOKING:寻 找 Leader 状态。当服务器处于该状态时,它会认为当前集群中没有 Leader,因此需要进入 Leader 选举状态。
   (2)FOLLOWING:跟随者状态。表明当前服务器角色是 Follower。
   (3)LEADING:领导者状态。表明当前服务器角色是 Leader。
   (4)OBSERVING:观察者状态。表明当前服务器角色是 Observer。
  
 ZAB 协议是为分布式协调服务ZooKeeper专门设计的一种支持崩溃恢复的一致性协议。基于该协议,ZooKeeper 实现了一种主从模式的系统架构来保持集群中各个副本之间的数据一致性。
  
 ZAB 协议包括两种基本的模式:崩溃恢复和消息广播。
  
 消息广播主要用于集群之间的数据同步,采用类似分布式事务中的2PC协议:
   (1)Leader将客户端的request转化成一个Proposal(提议)
   (2)Leader为每一个Follower准备了一个FIFO队列,并把Proposal发送到队列上。‘
   (3)leader若收到follower的半数以上ACK反馈
   (4)Leader向所有的follower发送commit。
  
 当整个 zookeeper 集群刚刚启动或者 Leader 服务器宕机、重启或者网络故障导致不存在过半的服务器与 Leader 服务器保持正常通信时,所有进程(服务器)进入崩溃恢复模式
  
 为了保证事务的顺序一致性,zookeeper 采用了全局递增的事务 Id 来标识,所有的 proposal(提议)都在被提出的时候加上了 zxid,zxid 实际上是一个 64 位的数字,高 32 位是 epoch( 时期; 纪元; 世; 新时代)用来标识 leader 周期,如果有新的 leader 产生出来,epoch会自增,低 32 位用来递增计数事务。
  
 当选举出Leader后,该Leader具有全局最大的ZXID,所以同步阶段的工作就是根据Leader的事务日志对Follower节点进行数据同步:

3. 直观理解:Zookeeper分布式一致性协议ZAB

   ZAB是Zookeeper使用的分布式一致性协议,英文全称是:Zookeeper Atomic Broadcast,因此ZAB也称之为Zookeeper原子广播协议。在解决分布式一致性方面,Zookeeper并没有使用Paxos,而是采用了ZAB协议。基于ZAB协议,Zookeeper实现一种主备模式的系统架构来保持集群中主备副本之间数据的一致性。ZAB协议包括两种基本模式:消息广播(Message Broadcasting)和崩溃恢复(Leader Activation)。下面来详细介绍这两种基本模式的实现过程。
     消息广播是Zookeeper用来保证写入事务一致性的方法,在Zookeeper集群中,存在以下三种角色的节点:    Leader: Zookeeper集群的核心角色,在集群启动或崩溃恢复中通过Follower参与选举产生,为客户端提供读写服务,并对事务请求进行处理。    Follower: Zookeeper集群的核心角色,在集群启动或崩溃恢复中参加选举,没有被选上就是这个角色,为客户端提供读取服务,也就是处理非事务请求,Follower不能处理事务请求,对于收到的事务请求会转发给Leader。    Observer: 观察者角色,不参加选举,为客户端提供读取服务,处理非事务请求,对于收到的事务请求会转发给Leader。使用Observer的目的是为了扩展系统,提高读取性能。     下面通过几张图对ZAB的消息广播过程进行简单的介绍。
                                                                                                                                                                     Zookeeper的消息广播过程类似 2PC(Two Phase Commit),ZAB仅需要超过一半以上的Follower返回 Ack 信息就可以执行提交,大大减小了同步阻塞,提高了可用性。
     在Zookeeper集群启动、运行过程中,如果Leader出现崩溃、网络断开、服务停止或重启等异常情况,或集群中有新服务器加入时,ZAB会让当前集群快速进入崩溃恢复模式并选举出新的Leader节点,在此期间整个集群不对外提供任何读取服务。当产生了新的Leader后并集群中过半Follower完成了与Leader的状态同步,那么ZAB协议就会让Zookeeper集群从崩溃恢复模式转换成消息广播模式。崩溃恢复的目的就是保证当前Zookeeper集群快速选举出一个新的Leader并完成与其他Follower的状态同步,以便尽快进入消息广播模式对外提供服务。     Zookeeper崩溃恢复的主要任务就是选举Leader(Leader Election),Leader选举分两个场景:一个是Zookeeper服务器启动时Leader选举,另一个是Zookeeper集群运行过程中Leader崩溃后的Leader选举。在详细介绍Leader选举过程之前,需要先介绍几个参数:
   另外在选举的过程中,每个节点的当前状态会在以下几种状态之中进行转变。
     假设现在存在一个由5个Zookeeper服务器组成的集群Sever1,Sever2,Sever3,Sever4和Sever5,集群的myid分别为:1, 2,3,4,5。依次按照myid递增的顺序进行启动。由于刚启动时zxid和epoch都为0,因此Leader选举的关键因素成了myid。
     在Zookeeper集群刚启动的时候,zxid和epoch并不参与群首选举。但是如果Zookeeper集群在运行了一段时间之后崩溃了,那么epoch和zxid在Leader选举中的重要性将大于myid。重要性的排序为:epoch    zxid    myid。当某一个Follower与Leader失去通信的时候,就会进入Leader选举,此时Follower会跟集群中的其他节点进行通信,但此时会存在两种情况:
     这种崩溃后的Leader选举机制也很好理解,如果Leader挂了,优先选择集群中最后做过(epoch)Leader的节点为新的Leader节点,其次选取有最新事务提交的节点(zxid)为Leader,最后才按默认的最大机器编号(myid)进行投票。

直观理解:Zookeeper分布式一致性协议ZAB

4. Zookeeper原理解析

微信公众号: Spark大数据
   
  
  一、Zookeeper介绍 
  
 ZooKeeper是一种为分布式应用所设计的高可用、高性能且一致的开源协调服务,它提供了一项基本服务: 分布式锁服务 。
  
 分布式应用可以基于它实现更高级的服务,实现诸如同步服务、配置维护和集群管理或者命名的服务。Zookeeper服务自身组成一个集群,2n+1个(奇数)服务允许n个失效,集群内一半以上机器可用,Zookeeper就可用。
  
 假设 3台机器组成的集群,可以有允许一台失效,如果有2台失效,这个集群就不可用,1<1.5,一般的搭建zookeeper集群时,以奇数台机器来搭建。目的:是为了提高容错能允许多损失一台。
                                          
  1.1 数据模型 
  
 1)ZooKeeper本质上是一个 分布式的小文件存储系统 ;
  
 2)Zookeeper表现为一个分层的文件系统目录树结构(不同于文件系统的是,节点可以有自己的数据,而文件系统中的目录节点只有子节点), 每个节点可以存少量的数据(1M左右) 。
  
 3)每个节点称做一个ZNode。 每个ZNode都可以通过其路径唯一标识 。
                                          
 4)ZooKeeper中的 每个节点存储的数据要被原子性的操作 。也就是说读操作将获取与节点相关的所有数据,写操作也将替换掉节点的所有数据。
  
 5)在zookeeper创建顺序节点(create -s ),节点路径后加编号,这个计数对于此节点的父节点来说是唯一的。
  
 /app/
  
 /s100000000001
  
 /s100000000002
  
 6)ZooKeeper中的节点有两种,分别为 临时节点和永久节点 。节点的类型在创建时即被确定,并且不能改变。
  
  ① 临时节点 :在客户端用create -e创建,该节点的生命周期依赖于创建它们的会话。一旦会话(Session)结束,临时节点将被自动删除,当然可以也可以手动删除。虽然每个临时的Znode都会绑定到一个客户端会话,但他们对所有的客户端还是可见的。另外,**ZooKeeper的临时节点不允许拥有子节点。
  
  ② 永久节点 :在客户端用create 创建,该节点的生命周期不依赖于会话,并且只有在客户端显示执行删除操作的时候,他们才能被删除。
  
 7) 客户端可以给节点设置watch,我们称之为监视器 。当节点状态发生改变时(Znode的增、删、改)将会触发watch所对应的操作。当watch被触发时,ZooKeeper将会向客户端发送且仅发送一条通知。
                                          
 分布式锁
                                          
 zookeeper 是高可用协调流程图
  
  1.2 zookeepr角色介绍 
                                          
  领导者(leader) ,负责进行投票的发起和决议,更新系统状态(数据同步),发送心跳。
  
  学习者(learner) ,包括跟随者(follower)和观察者(observer)。
  
  跟随者(follower) ,用于接受客户端请求、向客户端返回结果,在选主过程中参与投票。
  
  观察者(Observer) ,可以接受客户端请求,会把请求转发给leader, 但observer不参加投票过程,只同步leader的状态 ,observer的目的是为了扩展系统,提高读取速度。
                                          
 1)leader失效后会在follower中重新选举新的leader
  
 2)每个follower都和leader有连接,接受leader的数据更新操作
  
 3)客户端可以连接到每个server,每个server的数据完全相同
  
 4)每个节点的服务Server,记录事务日志和快照到持久存储
                                          
  1.3 工作原理 
  
 Zookeeper的核心是原子广播,这个机制保证了各个Server之间的同步。实现这个机制的协议叫做Zab协议。 Zab协议有两种模式 ,它们分别是恢复模式(选主)和广播模式(同步)。
  
  恢复模式: 当服务启动或者在领导者崩溃后,Zab就进入了恢复模式,恢复模式不接受客户端请求,当领导者被选举出来,且大多数Server完成了和leader的状态同步以后,恢复模式就结束了。状态同步保证了leader和Server具有相同的系统状态。
  
  广播模式: 一旦Leader已经和多数的Follower进行了状态同步后,他就可以开始广播消息了,即进入广播状态。这时候当一个Server加入ZooKeeper服务中,它会在恢复模式下启动,发现Leader,并和Leader进行状态同步。待到同步结束,它也参与消息广播。ZooKeeper的广播状态一直到Leader崩溃了或者Leader失去了大部分的Followers支持。
  
 1.4 Zookeeper节点数据操作流程
  
  (1)写操作 
  
 1)在Client向Follwer 或 Observer 发出一个写的请求;
  
 2)Follwer 或 Observer 把请求发送给Leader;
  
 3)Leader接收到以后向所有follower发起提案;
  
 4)Follwer收到提案后执行写操作,然后把操作结果发送给Leader;
  
 5)当多数follower返回提案结果后,leader会commit该提议,通知其他Follower 和 Observer 同步信息;
  
 6)Follwer 或Observer把请求结果返回给Client。
                                          
  (2)读操作 
  
 1)在Client向Follwer 或 Observer 发出一个读的请求;
  
 2)Follwer 或 Observer 把请求结果返回给Client;
  
  1.5 主要特点 
  
  最终一致性 :client不论连接到哪个Server,展示给它都是同一个视图,这是zookeeper最重要的特性;
  
  可靠性 :具有简单、健壮、良好的性能,如果消息被某一台服务器接受,那么它将被所有的服务器接受;
  
  实时性 :Zookeeper保证客户端将在一个时间间隔范围内获得服务器的更新信息,或者服务器失效的信息。但由于网络延时等原因,Zookeeper不能保证两个客户端能同时得到刚更新的数据,如果需要最新数据,应该在读数据之前调用sync()接口;
  
  等待无关(wait-free) :慢的或者失效的client,不得干预快速的client的请求,使得每个client都能有效的等待;
  
  原子性 :更新只能成功或者失败,没有中间状态;
  
  顺序性 :按照客户端发送请求的顺序更新数据。
  
  1.6 zookeepr应用场景 
  
  1.6.1 数据发布与订阅 
                                          
 发布与订阅即所谓的配置管理,顾名思义就是将数据发布到ZK节点上,供订阅者动态获取数据,实现配置信息的集中式管理和动态更新。
  
 应用配置集中到节点上,应用启动时主动获取,并在节点上注册一个watcher,每次配置更新都会通知到应用。
                                          
  1.6.2 命名空间服务  分布式命名服务,创建一个节点后,节点的路径就是全局唯一的,可以作为全局名称使用。
  
  1.6.3 分布式通知/协调 
  
 不同的系统都监听同一个节点,一旦有了更新,另一个系统能够收到通知。
                                          
  1.6.4 分布式锁 
  
 Zookeeper能保证数据的强一致性,用户任何时候都可以相信集群中每个节点的数据都是相同的。锁的两种体现方式:
  
  (1)保持独占 
  
 一个用户创建一个节点作为锁,另一个用户检测该节点,如果存在,代表别的用户已经锁住,如果不存在,则可以创建一个节点,代表拥有一个锁。
  
  (2)控制时序 
  
 有一个节点作为父节点,其底下是带有编号的子节点,所有要获取锁的用户,需要在父节点下创建带有编号的子节点,编号最小的会持有锁;当最我号的节点被删除后,锁被释放,再重新找最我号的节点来持有锁,这样保证了全局有序。
                                          
  1.6.5 集群管理 
  
 每个加入集群的机器都创建一个节点,写入自己的状态。监控父节点的用户会收到通知,进行相应的处理。离开时删除节点,监控父节点的用户同样会收到通知。