4.从Paxos到Zookeeper分布式一致性原理与实践--- Zookeeper 与 Paxos

 

	Apache Zookeeper 是由Apache Hadoop 的子项目发展而来。Zookeeper 为分布式应用提供了高效且可靠的分布式协调服务,提供了
  诸如统一命名服务,配置管理和分布式锁等分布式的基础服务。在解决分布式数据一致性方面,Zookeeper 并没有直接采用 Paxos 算法,
  而是用了 ZAB(Zookeeper Atomic Broadcast)的一致性算法。

1.初识 Zookeeper
	1.Zookeeper 介绍
		Zookeeper 是一个开源的分布式协调服务,由雅虎创建,是 Google Chubby 的开源实现。Zookeeper 的设计目标是将那些复杂
	  且容易出错的分布式一致性服务封装起来,构成一个高效可靠的原语集,并以一系列简单的接口提供给用户使用。

	  Zookeeper 是什么
	  	Zookeeper 是一个典型的分布式数据一致性的解决方案,分布式应用程序可以基于它实现诸如数据发布/订阅,负载均衡,命名服务,
	  分布式协调/通知,集群管理,Master选举,分布式锁和分布式队列等功能。Zookeeper 可以保证如下分布式一致性特性:
	  1.顺序一致性
	  	从同一个客户端发起的事务请求,最终将会严格的按照其发起顺序被应用到 Zookeeper 中去。
	  2.原子性
	  	所有事务请求的处理结果在整个集群中所有机器上的应用情况是一致的,也就是说,要么整个集群所有机器都成功应用了某一个事务,
	  要么都没有应用,一定不会出现集群中部分机器应用了事务,而另外一部分没有应用。
	  3.单一视图
	  	无论客户端连接的是哪个Zookeeper服务器,其看到的服务端的数据模型都是一致的。
	  4.可靠性
	  	一旦服务端成功应用了一个事务,并完成对客户端的响应,那么该事务所引起的服务端状态变更会一直保留下来,除非有另外一个事务
	  又对其进行了变更。
	  5.实时性
	  	通常人们看到实时性的反应是,一旦一个事务被成功应用,那么客户端能立即从服务端上读取到这个事务的最新数据状态。这里需要注意的是,
	  Zookeeper 仅仅保证在一定时间内,客户端最终一定能够从服务端上读取到最新的数据状态。

	  Zookeeper 设计目标
	  	Zookeeper 致力于提供一个高性能,高可用,具有严格的顺序访问控制能力(主要是写操作的严格顺序性)的分布式协调服务。高性能使得Zookeeper
	  能够应用于那些对系统吞吐有明确要求的大型分布式系统中,高可用使得分布式单点问题得到了很好的解决,而严格的顺序访问能力使得客户端能够基于
	  Zookeeper 实现一些复杂的同步原语。

	  	目标一:简单的数据模型
	  		Zookeeper 使得分布式程序能够通过一个共享的,树形结构的名字空间来进行互相协调。这里说的树形结构的名字空间,是指Zookeeper 服务器
	  	  内存中的一个数据模型,其由一系列被称为ZNode的数据节点组成,总的来说,其数据模型类似于一个文件系统,而ZNode之间的层级关系,就像文件
	  	  系统的目录结构一样。不过和传统的磁盘文件系统不同,Zookeeper将全量数据存储在内存中,以此来实现提高服务器吞吐,减少延迟的目的。

	  	目标二:可以构建集群
	  		一个Zookeeper集群通常是由一组机器组成,一般3~5台机器就可以组成一个可用的Zookeeper 集群了。组成Zookeeper集群的每台机器都会在内存
	  	  中维护当前的服务器状态,并且每台机器之间都互相保持着通信。值得一提的是,只要集群中存在超过一半的机器能够正常工作,那么整个集群就能够对外
	  	  提供服务。
	  	  	Zookeeper的客户端程序会选择和集群中任意一台机器共同来创建一个tcp连接,而一旦客户端和某台Zookeeper服务器之间的连接断开之后,客户端
	  	  会自动连接到集群中的其他机器。

	  	目标三:顺序访问
	  		对于来自客户端的每个更新请求,Zookeeper 都会分配一个全局唯一的递增编号,这个编号反应了所有事务操作的先后顺序,应用程序可以使用Zookeeper
	  	  的这个特性来实现更高层次的同步原语。

	  	目标四:高性能
	  		由于 Zookeeper 将全量数据存储在内存中,并直接服务于客户端的所有非事务请求,因此它尤其适用于以读操作为主的应用场景。

	3.Zookeeper 的基本概念	
		集群角色
			通常在分布式系统中,构成一个集群的每一台机器都有自己的角色,最典型的集群模式就是 Master/Slave 模式(主备模式)。在这种模式中,我们把能够
		  处理所有写操作的机器称为 Master 机器,把所有通过异步复制方式获取最新数据,并提供读操作的机器称为 Slave 机器。
		    而在 Zookeeper 中,它没有沿用 Master/Slave 概念,而是引入了 Leader,Follower,和 Observer 三种角色。Zookeeper 集群中的所有机器都
		  通过一个 Leader 选举过程来选定一台被称为 "Leader" 的机器,Leader 服务器为客户端提供读和写服务。除Leader外,其他机器包括Follower和Observer。
		  Follower和Observer都能提供读服务,唯一的区别在于 Observer 机器不参与 Leader 选举过程,也不参与写操作的 '过半写成功' 策略,因此 Observer
		  可以在不影响写性能的情况下提升集群的读性能。

		会话(session)
			session 是指客户端会话。在 Zookeeper 中,一个客户端连接是指客户端和服务端之间的一个tcp连接。Zookeeper 对外的服务端默认端口是 2181,客户端启动
		  的时候,首先会与服务器建立一个tcp连接,从第一次连接建立开始,客户端会话的生命周期也开始了,通过这个连接,客户端能够通过心跳检测与服务端保持有效的会话。
		  也能够向 Zookeeper 服务器发送请求并接受响应,同时还能够通过该连接接收来自服务端的 Watch 事件通知。Session 的 sessionTimeout 值用来设置一个客户端
		  会话的超时时间。由于服务器压力大,网络故障或客户端主动断开连接等各种原因导致客户端连接断开时,只要在 sessionTimeout 规定的时间内能够重新连接上集群的
		  任一一台服务器,那么之前创建的会话仍然有效。

		数据节点(Znode)
			在谈到分布式的时候,我们通常说的'节点'是指组成集群中的每一台机器。然后,在 Zookeeper 中,'节点'分为两类:第一类同样是指构成集群的机器,我们称之为
		  '机器节点';第二类是指数据模型中的数据单元,我们称之为数据节点---ZNode。Zookeeper 将所有的数据存储在内存中,数据模型是一个树(ZNode Tree),
		  由斜杆(/)进行分割的路径,就是一个ZNode,例如 /foo/path1。每个 ZNode 上都会保存自己的数据内容,同时还会保存一系列属性信息。
		    在 Zookeeper 中,ZNode 可以分为持久节点 和临时节点。所谓持久节点是指一旦这个 ZNode 被创建了,除非主动进行ZNode的移除操作,否则这个ZNode将一直
		  保存在 Zookeeper 上。而临时节点就不一样了,它的生命周期和客户端会话绑定,一旦客户端会话失效,那么这个客户端创建的所有临时节点都会被移除。另外,
		  Zookeeper 还允许用户为每一个节点添加一个特殊的属性:SEQUENTIAL。一旦节点被标记上这个属性,那么在这个节点被创建的时候,Zookeeper 会自动在其节点名
		  后面追加一个整型数字,这个整型数字是一个由父节点维护的自增数字。

		版本
			Zookeeper 每个Znode 上都会存储数据,对应于每个ZNode,Zookeeper 都会为其维护一个叫做 Stat 的数据结构,Stat中记录了这个 ZNode 的三个数据版本。
		  分别是 version(当前 ZNode 的版本),cversion(当前 ZNode 子节点的版本),和 aversion(当前 ZNode 的 ACL 版本)。

		Watcher
			Watcher(事件监听器),是 Zookeeper 中的一个很重要的特性。Zookeeper 允许用户在指定节点上注册一些 Watcher,并且在一些特定事件触发的时候,
		  Zookeeper 服务端会将事件通知到感兴趣的客户端上去,该机制是 Zookeeper 实现分布式协调服务的重要特性。

		ACL
			Zookeeper 采用 ACL(Access Control Lists) 策略来进行权限控制。
			1.CREATE:创建子节点的权限
			2.READ:获取节点数据和子节点列表的权限
			3.WRITE:更新节点数据的权限
			4.DELETE:删除子节点的权限
			5.ADMIN:设置节点ACL的权限
			需要注意的是,CREATE和DELETE 这2个都是针对子节点的权限控制。


2.Zookeeper 的 ZAB 协议
	1.ZAB 协议
		Zookeeper 并没有完全采用 Paxos 算法,而是使用了一种称为 Zookeeper Atomic Broadcast(ZAB,Zookeeper 原子消息广播协议)的协议作为其数据
	  一致性的核心算法。
	    ZAB 协议是为分布式协调服务 Zookeeper 专门设计的一种支持崩溃恢复的原子广播协议。
	    在 Zookeeper 中,主要依赖 ZAB 协议来实现分布式数据一致性,基于该协议,Zookeeper 实现了一种主备模式的系统架构来保持集群中各个副本之间数据
	  的一致性。具体的,Zookeeper 使用了单一的主进程来接收并处理客户端的所有请求事务,并采用 ZAB 的原子广播协议,将服务器数据的状态变更以事务的
	  Proposal 的形式广播到所有的副本进程上去。ZAB 协议的这个主备模型架构保持了同一时刻集群中只能有一个主进程来广播服务器的状态变更,因此能够很好地 
	  处理客户端大量的并发请求。另一方面,考虑到分布式环境中,顺序执行的一些状态变更其前后具有一定的依赖关系,有些状态的变更必须依赖于比它早生成的那
	  些状态变更。这样的依赖关系也对ZAB提出了一个要求:ZAB协议必须能够保证一个全局的变更序列被顺序的应用,也就是说,ZAB 协议需要保证如果一个状态
	  变更已经被处理了,那么所有其依赖的状态变更都应该已经被提前处理了。最后,考虑到主进程在任何时候都有可能出现崩溃,因此,zab 协议还需要做到当前
	  主进程出现上述异常的时候,仍能正常工作。
	    zab 协议的核心是定义了对于那些会改变 Zookeeper 服务器数据状态的事务请求的处理方式,即:
	    所有的事务请求必须由一个全局唯一的服务器来协调处理,这样的服务器被称为 Leader 服务器,而余下的服务器则称为 Follower 服务器。Leader服务器
	  负责将一个客户端事务请求转换成一个事务Proposal(提议),并将该 Proposal 分发给集群中所有的 Follower 服务器。之后 Leader 服务器需要等待所有
	  Follower 服务器的反馈,一旦超过半数的 Follower 服务器进行了正确的反馈后,那么 Leader 就会再次向所有的 Follower 服务器分发 Commit 消息,
	  要求其将前一个 Proposal 进行提交。

	2.协议介绍
		zab 协议包括两种基本的模式:崩溃恢复和消息广播。
		当整个服务框架在启动过程中,或是当 Leader 服务器出现网络中断,崩溃退出与重启等异常情况时,zab 协议就会进入恢复模式并选举产生新的 Leader
	  服务器。当选举产生了新的Leader服务器,同时集群中已经有过半的机器与该leader服务器完成了状态同步之后,zab 协议就会退出恢复模式。其中,所谓的
	  状态同步是指数据同步,用来保证集群中存在过半的机器能够与leader服务器的数据状态保持一致。
	    当集群中已经有过半的 follower 服务器完成了和 leader 服务器的状态同步,那么整个服务框架就可以进入消息广播模式了。当一台同样遵守zab协议的
	  服务器启动后加入到集群中时,如果此时集群中已经存在一个leader服务器在负责进行消息广播,那么新加入的服务器就会自觉地进入数据恢复模式:找到
	  leader所在的服务器,并与其进行数据同步,然后一起参与到消息广播流程中去。正如上面介绍的,Zookeeper 设计成只允许唯一一个leader服务器来进行
	  事务请求的处理。leader 服务器在接收到客户端的事务请求后,会生成对应事务的提案并发其一轮广播协议;而如果集群中的其他机器接收到客户端的事务请求,
	  那么这些非leader服务器会首先将这个事务请求转发给 leader 服务器。
	    当 leader 服务器出现崩溃或者机器重启,亦或是集群中已经不存在过半的服务器与该 leader 服务器保持正常通信时,那么在重新开始的新一轮的原子
	  广播事务操作之前,所有进程首先会使用崩溃恢复协议来使彼此达到一个一致的状态,于是整个 zab 流程就会从消息广播模式进入到崩溃恢复模式。
	    一个机器要称为新的 leader,必须获得过半进程的支持,同时由于每个进程都有可能会崩溃,因此,在 zab 协议运行过程中,前后会出现多个 leader,
	  并且每个进程也有可能多次成为 leader。进入崩溃恢复模式后,只要集群中存在过半的服务器能够彼此进行正常通信,那么就可以产生一个新的 leader并
	  再次进入消息广播模式。

	  消息广播:
	  	zab 协议的消息广播过程使用的是一个原子广播协议,类似于一个二阶段提交过程。针对客户端的事务请求,leader 服务器会为其生成对应的事务 Proposal,
	  并将其发送给集群中其余所有的机器,然后再分别收集各自的选票,最后进行事务提交。
	    zab 协议中涉及的二阶段提交过程略有不同。在 zab 协议的二阶段提交过程中,移除了中断逻辑,所有的follower服务器要么正常反馈leader提出的事务
	  Proposal,要么就抛弃leader服务器。同时,zab协议将二阶段提交中的中断逻辑移除意味着我们可以在过半的follower服务器已经反馈Ack之后就开始提交事务
	  Proposal了,而不需要等待集群中所有的 follower 服务器都反馈响应。当然,在这种简化了的二阶段提交模型下,是无法处理 leader 服务器崩溃退出而带来
	  的数据不一致性,因此 zab 协议中添加了另外一个模式,即采用崩溃恢复模式来解决这个问题。另外,整个消息广播协议是基于具有FIFO特性的tcp协议来进行
	  网络通信,因此能够很好的保证消息广播过程中消息接收与发送的顺序性。
	    在整个消息广播过程中,leader 服务器会为每个事务请求生成对应的 Proposal 来进行广播,并且在广播事务 Proposal 之前,leader服务器会首先为
	  这个事务 Proposal 分配一个全局单调递增的唯一ID,我们称之为事务ID(即 ZXID)。由于 zab 协议需要保证每一个消息严格的因果关系,因此必须将每一个
	  事务 Proposal 按照其 ZXID 的先后顺序进行排序和处理。
	    具体的,在消息广播过程中,leader服务器会为每一个 follower 服务器都各自分配一个单独的队列,然后按需要将要广播的事务 Proposal 依次放入这些
	  队列中去,并且根据FIFO策略进行消息发送。每一个follower服务器在接收到这个事务 Proposal 之后,都会首先将其以事务日志的形式写入到本地磁盘中去,
	  并且在成功写入后反馈给leader服务器一个 ack 响应。当leader服务器接收超过半数follower的ack响应后,同时 leader 自身也会完成对事务的提交,而
	  每一个 follower 服务器在接收到 commit 消息后,也会完成对事务的提交。

	  崩溃恢复:
	  	zab 协议的这个基于原子广播协议的消息广播过程,在正常情况下运行非常良好,但是一旦leader服务器出现崩溃,或者是由于网络原因导致leader服务器
	  失去了与过半 follower 的联系,那么就会进入崩溃恢复模式。在 zab 协议中,为了保证程序的正确运行,整个恢复过程结束后需要选举出一个新的leader
	  服务器。因此,zab 协议需要一个高效且可靠的 leader 选举算法,从而确保能够快速的选举出现的 leader,同时,leader 选举算法不仅需要让 leader
	  自己知道其自身已经被选举为 leader,同时还要让集群中的所有其他机器也能够快速的感知到选举产生新的leader服务器。

	  	基本特性:
	  		zab 协议规定了如果一个事务Proposal在一台机器上被处理成功,那么应该在所有的机器上都被处理成功,哪怕机器出现故障。
	  		1.zab 协议需要确保哪些已经在 leader 服务器上提交的事务最终被所有服务器都提交。
	  		2.zab 协议需要确保丢弃那些只在 leader 服务器上被提出的事务
	  		相反,如果在崩溃恢复过程中出现一个需要被丢弃的提案,那么在崩溃恢复结束后需要跳过该事务Proposal。

	  		结合上面的情况,就决定了 zab 协议必须设计这样一个 leader 选举算法:能够确保提交已经被 leader 提交的事务Proposal,同时丢弃已经被跳过的
	  	  事务 Proposal。针对这个要求,如果让 leader选举算法能够保证新选举出来的 leader 服务器拥有集群中所有机器最高编号(即 ZXID 最大)的事务Proposal,
	  	 那么就可以保证这个新选举出来的 leader 一定具有所有已经提交的提案。更为重要的是,如果让具有最高编号事务 Proposal 的机器称为 leader,就可以
	  	 省去Leader服务器检查 Proposal 的提交和丢弃工作这一步操作了。

	  	数据同步:
	  		完成leader选举之后,在正式开始工作(即接收客户端的事务请求,然后提出新的提案)之前,leader 服务器会首先确认事务日志中的所有Proposal
	  	  是否都已经被集群中过半的机器提交了,即是否完成数据同步。
	  	    所有正常运行的服务器,要么成为leader,要么成为follower并和 leader 保持同步。leader 服务器需要确保所有的follower服务器能够接受到每一条
	  	  事务Proposal,并且能够正确的将所有已经提交的事务Proposal应用到内存数据库中去。具体的,leader服务器会为每一个follower服务器都准备一个队列,
	  	  并将那些没有被各follower服务器同步的事务以 Proposal 消息的形式逐个发送到follower服务器,并且在每一个 Proposal 消息后面紧接着再发送一个
	  	  commit 消息,以表示该事务已经被提交。等到follower服务器将所有尚未同步的事务 Proposal 都从 leader 服务器上同步过来并成功应用到本地数据库
	  	  中后,leader 服务器就会将该 follower 服务器加入到期真正可用的 follower 列表中,并开始之后的流程。
	  	    zab 协议是如何处理那些需要丢弃的事务 Proposal的。在 zab 协议的事务编号 ZXID 设计中,ZXID 是一个64位的数字,其中低32位可以看做是一个简单
	  	  的单调递增的计数器,针对客户端的每一个事务请求,leader服务器在产生一个新的事务Proposal的时候,都会针对该计数器进行加1操作;而高32位则代表了
	  	  leader 周期 epoch 的编号,每当选举一个新的leader服务器,就会从这个leader服务器上去除其本地日志中最大事务Proposal 的 ZXID,并从该
	  	  ZXID中解析出对应的 epoch 值,然后再对其加1操作,之后就会以该编号作为新的 epoch,并将低32位置0来开始生成新的 ZXID。zab 协议中的这一通过
	  	  epoch 编号来区分 leader 周期变化的策略,能够有效的避免不同的 leader 服务器错误的使用相同的 ZXID 编号提出不一样的事务 Proposal的异常情况,
	  	  这对于识别在 leader 崩溃恢复前后生成的 Proposal 非常有帮助,大大简化和提升了数据恢复的流程。
	  	    基于这样的策略,当一个包含了上一个leader周期中尚未提交的事务 Proposal 的服务器启动时,其肯定无法成为 leader,原因很简单,因为当前集群中一定
	  	  包含一个 Quorum 集合,该集合中的机器一定包含了更高的 epoch 的事务 Proposal,因此这台机器的事务 Proposal 肯定不是最高的,也就无法成为 leader
	  	  了。当这台机器加入到集群中,以 follower 角色连接上leader服务器之后,leader服务器会根据自己服务器上最后被提交的Proposal来和 follower服务器的
	  	  Proposal 进行对比,对比的结果当然是 leader会要求 follower 进行一个回退操作---回退到一个确实已经被集群中过半集群提交的最新的事务 Proposal.


	3.深入ZAB协议
		1.系统模型
			通常在由一组进程 {P1,P2,...Pn} 组成的分布式系统中,其中每一个进程都具有各自的存储设备,各进程之间通过互相通信来实现消息的传递。一般的,在
		  这样的一个分布式系统中,每一个进程都随时有可能会出现一次或多次的崩溃退出,当然,这些进程会在恢复之后再次加入到进程组中。如果一个进程正常工作,
		  那么我们称该进程处于 UP 状态,如果一个进程崩溃了,那么我们称其为 DOWN状态。事实上,当集群中存在过半处于 UP 状态的进程组成的一个进程子集之后,
		  就可以进行正常的消息广播了。我们将这样一个进程子集称为 Quorum。
		    我们使用 Pi和Pj 来分别表示进程组中的两个不同进程,使用Cij 来表示进程 Pi 和 Pj 之间的网络通信信道,其基本满足两个基本特征:
		    1.完整性(Integrity)
		    	进程 Pj 如果收到了来自进程 Pi 的消息m,那么进程 Pi 一定确实发送了消息m。
		    2.前置性(Prefix)
		    	如果进程 Pj 收到了消息m,那么存在这样的消息m1:如果消息m1是消息m的前置消息,那么Pj 务必先接收到消息m1,然后再接收到消息m。我们将存在
		      这种前置性关系的两个消息表示为:m1<m。前置性是整个消息协议设计中最关键的一点,由于每一个消息都有可能是基于之前的消息进行的,因此所有的
		      消息都必须按照严格的先后顺序进行处理。

		2.问题描述
			Zookeeper 是一个高可用的分布式协调服务,在很多大型系统上得到应用。这类应用有一个共同的特点,即通常都存在大量的客户端进程,并且都依赖 
		  Zookeeper 来完成一系列诸如可靠的配置存储和运行时状态记录等分布式协作工作。鉴于这些大型应用对 Zookeeper 的依赖,因此 Zookeeper 必须
		  具备高吞吐和低延迟的特性,并且能够很好的在高并发情况下完成分布式数据的一致性处理,同时能够优雅的处理运行时故障,并且具备快速的从故障中
		  恢复的能力。
		    ZAB 协议是整个 Zookeeper 框架的核心所在,其规定了任何时候都需要保证只有一个主进程负责进行消息广播,而如果主进程崩溃了,就需要选出一个新
		  的主进程。主进程的选举机制和消息广播机制是紧密相关的。随着时间的推移,会出现无数个主进程并构成一个主进程序列:P1,P2,...Pe, 其中e表示主进程
		  序列号,也被称作主进程周期。对于这个主进程序列上的任意2个主进程来说,如果e小于e1,那么我们就说 Pe 是Pe1 之前的主进程,通常使用 Pe < Pe1 
		  来表示。需要注意的是,由于各个进程都可能会发生崩溃然后再次恢复,因此会出现这样的情况:存在这样的 Pe 和 Pe1,它们本质上是同一个进程,只是处于
		  不同周期中而已。

		  	主进程周期:
		  		为了保证主进程每次广播出来的事务消息都是一致的,我们必须确保zab协议只有在充分完成崩溃恢复阶段之后,新的主进程才可以开始生成新的
		  	  事务消息广播。为了实现这个目的,我们假设各个进程都实现了类似 ready(e) 这样一个函数调用,在运行过程中,zab 协议能够非常明确的告知
		  	  上层系统(指主进程和其他副本进程)是否可以开始进行事务消息的广播,同时,在调用ready(e)函数之后,zab 还需要为当前主进程设置一个实例
		  	  值。实例值用于唯一标识当前主进程的周期,在进行消息广播的时候,主进程使用该实例值来设置事务标识中的 epoch 字段---当然,zab需要保证
		  	  实例值在不同的主进程周期中是全局唯一的。如果一个主进程周期 e 早于另外一个主进程周期e1,那么将其表示为 e<e1。

		  	事务:
		  		我们假设各个进程都存在一个类似于 transaction(v,z) 这样的函数调用,来实现主进程对状态变更的广播。主进程每次对 transaction(v,z)
		  	  函数的调用都包含了2个字段:事务内容v 和事务标识z,而每一个事务标识 z=<e,c> 也包含2个组成部分,前者是主进程周期e,后者是当前主进程
		  	  周期内的事务计数c。我们使用 epoch(z) 来表示一个事务标识中的主进程周期 epoch,使用 counter(z) 来表示事务标识中的事务计数。
		  	    针对每一个新的事务,主进程都会首先将事务计数c递增。在实际运行中,如果一个事务标识z优先于另外一个事务z1,那么就有2种情况:一种是
		  	  主进程周期不同,即 epoch(z) < epoch(z1);另外一种情况是主进程周期一样,但是事务计数不同,即 epoch(z) = epoch(z1) 且
		  	  counter(z) < counter(z1),无论哪种情况,均使用 z < z1 来表示。

		3.算法描述
			整个zab协议主要包括消息广播和崩溃恢复2个过程,进一步可以细分为三个阶段,分别是发现(Discovery),同步(Synchronizatioin)和广播(Broadcast)
		  阶段。组成zab协议的每一个分布式进程,会循环的执行这3个阶段,我们将这样一个循环称为一个主进程周期。

		    Fp : Follower f 处理过的最后一个事务 Proposal
		    Fzxid : Follower f 处理过的历史事务 Proposal 中最后一个事务 Proposal 的事务标识 ZXID
		    hf : 每一个 Follower f 通常都已经处理(接受)了不少事务 Proposal,并且会有一个针对已经处理过的事务的集合,并将其表示为 hf,表示 Follower f
		    已经处理过的事务序列。
		    Ie:初始化历史记录,在某一个主进程周期 epoch e 中,当准 Leader 完成阶段一之后,此时它的 hf 就被标记为 Ie.

		    阶段一:发现
		    	阶段一主要就是 Leader 选举过程,用于在多个分布式进程中选举出主进程,准 Leader L 和 Follower F的工作流程分别如下:
		    	步骤 F.1.1 :
		    	Follower F 将自己最后接受的事务 Proposal 的 epoch 值 CEPOCH(Fp)发送给准 Leader L
		    	
		    	步骤 L.1.1 :
		    	当接收来自过半的 Follower 的 CEPOCH(Fp)消息后,准 Leader L 会生成 NEWEPOCH(e1) 消息给这些过半的 Follower。
		    	关于这个 epoch 值 e1,准 Leader L 会从所有接收的 CEPOCH(Fp)消息中选出最大的 epoch 值,然后对其进行加1操作,即为 e1。
		    	
		    	步骤 F.1.2 :
		    	当 Follower 接收到来自准 Leader L 的 NEWEPOCH(e1)消息后,如果其检测到当前的 CEPOCH(Fp)值小于e1,那么就会将 CEPOCH(Fp)
		    	赋值为 e1,同时向这个准 Leader L反馈 ack 消息。这个反馈消息(ACK-E(Fp,hf))中,包含了当前该 Follower 的 epoch CEPOCH(Fp),以及该
		    	Follower 的历史事务 Proposal 集合:hf.

		    	当Leader L 接收到来自过半 Follower 的确认消息 Ack 之后,Leader L 就会从这些过半服务器中选出一个 Follower F,并使用其作为初始化事务集合
		      Ie1.
		        关于这个 Follower F 的选取,对于 Quorum 中其他任意一个 Follower F1,F需要满足以下2个条件中的一个:
		        CEPOCH(F1p) < CEPOCH(Fp)
		        (CEPOCH(F1p) = CEPOCH(Fp)) & (F1zxid < zFxxid 或 F1zxid = Fzxid) 
		        至此,zab 协议完成阶段一的工作流程

		    阶段二:同步
		    	在完成发现流程之后,就进入到了同步阶段。在这一阶段中,Leader L 和 Follower F的工作流程如下:
		    	步骤 L.2.1:
		    		Leader L 会将 e1和 Ie1 以 NEWLEADER(e1,Ie1)消息的形式发送给所有 Quorum 中的 Follower.

		    	步骤 F.2.1:
		    		当 Follower 接收到来自 Leader L 的 NEWLEADER(e1,Ie1)消息后,如果 Follower 发现 CEPOCH(Fp) != e1,那么直接进入下一轮循环,因此
		    	  此时 Follower 发现自己还在上一轮,或者更上一轮,无法参与本轮同步。
		    	    如果 CEPOCH(Fp) = e1,那么 Follower 就会执行事务应用操作。具体的,对于每一个事务 Proposal: <v,z> 属于 Ie1,Follower 都会接受<e1,<v,z>>
		    	  最后,Follower 会反馈给 Leader,表明自己已经接受并处理了所有Ie1中的事务 Proposal.

		    	步骤 L.2.2:
		    		当 Leader 接收到来自过半 Follower 针对 NEWLEADER(e1,Ie1)的反馈消息后,就会向所有的 Follower 发送 Commit 消息,至此 Leader 完成阶段二。

		    	步骤 F.2.2:
		    		当 Follower 收到来自 Leader 的 Commit 消息后,就会依次处理并提交所有在 Ie2中未处理的事务。至此 Follower 完成阶段二。

		    阶段三:广播	
		    	完成同步阶段之后,zab协议就可以正式开始接收客户端新的事务请求,并进行消息广播。
		    	步骤 L.3.1:
		    		Leader L 接收到客户端新的事务请求后,会生成对应的事务 Proposal,并根据 zxid 的顺序向所有的 follower 发送提案 <e1,<v,z>>,其中
		      epoch(z)=e1.

		        步骤 F.3.1 :
		        	Follower 根据消息接收的先后次序来处理这些来自 Leader 的事务 Proposal,并将它们追加到 hf 中去,之后再次反馈 Leader.

		        步骤 F.3.1:
		        	当 leader 接收到来自过半 follower 针对事务 proposal <e1,<v,z>>的 ack 消息后,就会发送 commit<e1,<v,z>> 消息给所有的 follower,
		          要求它们进行事务的提交。

		        步骤 F.3.1:
		        	当follower F 接收到来自 leader 的 commit <e1,<v,z>>消息后,就会开始提交事务 proposal<e1,<v,z>>。需要注意的是,此时该 follower F
		          必定已经提交了事务 Proposal<v1,z1>,其中 <v1,z1>属于 hf,z1 < zZ。         

		4.运行分析
			在zab协议的设计中,每一个进程都有可能处于下面3种状态:
			1.LOOKING : Leader 选举阶段
			2.FOLLOWING : Follower 服务器和 Leader 保持同步状态
			3.LEADING : Leader 服务器作为主进程领导状态
			组成 zab 协议的所有进程启动的时候,其初始化状态都是 looking 状态,此时进程组中不存在 leader。所有处于这种状态的进程,都会试图去选举出一个新的
		  leader。随后,如果进程发现已经选举出新的leader了,那么它就会马上切换到 following 状态,并开始和 leader 保持同步。这里,我们将处于 following 
		  的进程称为 follower,将处于 leading 状态的进程称为 leader。考虑到 leader 进程随时会挂掉,当检测出 leader 已经崩溃或者是放弃了领导地位时,
		  其余的 follower 进程就会切换到 looking 状态,并开始新一轮的 leader 选举。因此在 zab 协议运行的过程中,每个进程都会在 leading,following和
		  looking 状态之间不断的切换。
		    一个可用的 leader : 
		    	如果一个准 leader Le 接收来自过半的 Follower 进程针对 Le 的 NEWLEADER(e,Ie)反馈消息,那么Le就成为了周期e的 Leader。

		    完成了 Leader 选举以及数据同步之后,zab 协议就进入了原子广播阶段。在这一阶段,leader 会以队列的形式为每一个与自己保持同步的 follower 创建
		  一个操作队列。同一时刻,一个 follower 只能和一个 leader 保持同步,leader 进程与所有的 follower 进程之间都通过心跳检测机制来感知彼此的情况。
		  如果leader能够在超时时间内正常收到心跳检测,那么follower就会一直与该 leader 保持连接。而如果在在指定时间 leader 无法从过半的 follower 进程
		  那里接收到心跳检测,或者是 tcp 连接本身断开了,那么 leader 就会终止当前对周期的领导,并转换到 looking 状态,所有的 follower 也会选择放弃这个
		  leader,同时转换到 looking 状态,之后,所有的进程就会开始新一轮的 leader 选举,并在选举产生新的 leader 之后开始新一轮的主进程周期。


	4.ZAB 与 Paxos 算法的联系和区别
		联系:
		1.两者都存在一个类似于 leader 进程的角色,由其负责协调多个 follower 进程的运行
		2.leader 进程都会等待超过半数的 follower 做出正确的反馈后,才会将一个提案进行提交
		3.在 zab 协议中,每个 proposal 中都包含了一个 epoch 值,用来代表当前的 leader 周期,在 paxos 算法中,同样存在这样一个标识,只是名字变成 Bollot

		在paxos算法中,一个新选举产生的主进程会进行2个阶段的工作。第一阶段被称为读阶段,在这个阶段,这个新的主进程会通过和所有其他进程进行通信的方式来收集
	  上一个主进程提出的提案,并将它们提交。第二阶段被称为写阶段,在这个阶段,当前进程开始提出它自己的提案。在 paxos 算法中,zab 协议额外添加了一个同步阶段,
	  zab 协议也存在一个和 paxos 算法中的读阶段非常类似的过程,被称为发现(Discovery)阶段。在同步阶段中,新的 leader 会确保存在过半的 follower 已经提交了
	  之前 leader 周期的所有事务 proposal。这一同步阶段的引入,能够有效的保证 leader 在新的周期中的提出的事务 proposal 之前,所有的进程都已经完成了对之前
	  所有事务 proposal 的提交。一旦完成同步阶段后,那么 zab 就会执行和 paxos 算法类似的写阶段。
	    总的来说,zab 协议和 paxos 算法的本质区别在于,两者的设计目标不太一样。zab 协议主要用于构建一个高可用的分布式数据库主备系统,例如 zookeeper,而 paxos
	  算法则是用于构建一个分布式的一致性状态机系统。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值