ZooKeeper简介ZooKeeper是一个开源的分布式应用程序协调服务,它包含一组简单的原语,分布式应用程序可以基于这些原语实现同步服务、配置维护和命名服务。ZooKeeper的设计目的1.最终一致性:无论客户端连接到哪个服务器,都会显示相同的视图,这是zookeeper最重要的性能。2.可靠性:简单、健壮、性能好,如果消息m被一台服务器接受,则所有服务器都会接受。3、实时性:Zookeeper保证客户端在一个时间间隔内获取服务器的更新信息或服务器故障的信息。但是由于网络延迟等原因,Zookeeper不能保证两个客户端同时获取到最新更新的数据。如果需要最新的数据,需要在读取数据前调用sync()接口。4.免等待:慢速或无效的客户端一定不能干扰快客户端的请求,让每个客户端都能有效等待。5.原子性:更新只能成功或失败,没有中间状态。6.顺序:包括全局顺序和部分顺序:全局顺序是指如果在一个服务器上消息a先于消息b发布,那么在所有服务器上消息a都会先于消息b发布;偏序是指如果同一个发送者在消息a之后发布消息b,则a一定在b之前。ZooKeeper数据模型Zookeeper会维护一个具有层次关系的数据结构,与标准的文件系统非常相似,如图:znode,这个znode由它所在的路径唯一标识。例如,Server1的znode标识为/NameService/Server1。2)一个znode可以有子节点目录,每个znode可以存储数据,注意EPHEMERAL(临时)类型目录节点不能有子节点目录。3)znode是有版本(version)的,每个znode中存储的数据可以有多个版本,即在一个访问路径中可以存储多份数据,版本号自动递增。4)znode的类型:持久化节点,一旦创建,不会意外丢失,即使服务器完全重启,它仍然存在。每个Persist节点都可以包含数据以及子节点。临时节点在创建它的客户端和服务器之间的会话结束时自动删除。服务器重启会导致Session结束,所以此时Ephemeral类型的znode也会被自动删除。Non-sequencenode,当多个client同时创建同一个Non-sequencenode时,只有一个可以创建成功,其他的都会失败。并且创建的节点名称与创建时指定的节点名称完全相同。序列节点,创建的节点名称在指定名称后有一个10位十进制数的序号。多个客户端创建同名节点时,都可以创建成功,只是序号不同。5)znode可以被监控,包括目录节点存储数据的修改,子节点目录的变化等,一旦变化可以通知到设置监控的客户端,这是核心特性Zookeeper的特性,Zookeeper的很多功能都是基于这个特性实现的。6)ZXID:每次Zookeeper的状态改变都会产生一个zxid??(ZooKeeperTransactionId)。zxid是全局排序的。如果zxid1小于zxid2,则zxid1出现在zxid2之前。ZooKeeperSessionClient与Zookeeper集群建立连接,整个会话的状态变化如图:如果Client因为Timeout与ZookeeperServer失去连接,则客户端处于CONNECTING状态,会自动尝试连接再次连接到服务器。如果在会话有效期内再次连接成功,AServer返回CONNECTED状态。注意:如果客户端因为网络状况不佳而与服务器失去联系,客户端会停留在当前状态,并尝试再次主动连接到ZookeeperServer。客户端不能声明自己的会话已经过期。session过期由ZookeeperServer决定。客户端可以选择主动关闭会话。ZooKeeperWatchZookeeperwatch是一种监控通知机制。Zookeeper的所有读操作getData()、getChildren()和exists()都可以设置为monitor(监视)。watch事件可以理解为一次性触发。官方定义如下:watch事件是一次性触发,发送给设置watch的客户端,当设置watch的数据发生变化时触发。Watch的三个关键点:(一次性触发)一次性触发当要监控的数据集发生变化时,会向客户端发送监控事件。例如客户端调用getData(/znode1,true),之后/znode1节点上的数据发生变化或者被删除,客户端会得到/znode1发生变化的监听事件;而如果/znode1再次被更改,则一旦更改,客户端将不会收到事件通知,除非客户端再次对/znode1设置监视。(发送给客户端)发送给客户端Zookeeper客户端和服务器通过套接字进行通信。由于网络故障,监控事件可能无法成功到达客户端。监视事件被异步发送到监视器。Zookeeper本身提供了一种顺序保证(orderingguarante):即客户端在第一次看到watch事件之前,不会感知到它所设置监控的znode发生了变化(客户端永远不会看到它设置了watch直到它第一次看到watch事件)。网络延迟或其他因素可能会导致不同的客户端在不同的时间感知到一个监控事件,但不同的客户端看到的一切都有一个一致的顺序。(设置watch的数据)设置watch的数据是指znode本身有不同的变化方式。你也可以想象Zookeeper维护了两个watchlist:datawatch和childwatch(数据表和子表)getData()和exists()设置数据表,getChildren()设置子表。或者你也可以想象,Zookeeper设置的不同监视器返回不同的数据,getData()和exists()返回znode节点的信息,getChildren()返回子节点列表。所以setData()会触发某个节点上的数据监听集(假设数据设置成功),create()操作成功会触发当前节点和父节点监听的子节点上的数据监听集.成功的删除操作会触发当前节点的数据监听和子节点监听事件,也会触发该节点父节点的子watch。Zookeeper中的监控是轻量级的,因此易于设置、维护和分发。当客户端与Zookeeper服务器失去联系时,客户端不会收到监听事件的通知。只有当客户端重新连接时,如有必要,才会重新注册并触发之前注册的监听。对于开发这通常对人员是透明的。只有一种情况会导致监听事件丢失,那就是:一个znode节点的监听是通过exists()设置的,但是如果客户端在创建和删除的时间间隔内与zookeeper服务器失去联系对于这个znode节点,即使稍后重新连接到zookeeper服务器,客户端也不会收到事件通知。一致性保证Zookeeper是一种高效且可扩展的服务。读写操作都被设计成快速的,读操作比写操作快。顺序一致性:来自客户端的更新请求将按顺序执行。原子性:更新要么成功要么失败,没有部分成功。唯一系统镜像(SingleSystemImage):无论客户端连接到哪个Server,看到的系统镜像都是一致的。可靠性:一旦更新有效,它将一直有效直到被覆盖。时效性:确保每个客户端看到的系统信息在一定时间内是一致的。ZooKeeper的工作原理在zookeeper集群中,每个节点有以下3种角色和4种状态:角色:leader、follower、observer状态:leading、following、observing、lookingZookeeper的核心是原子广播。这种机制保证了各个Server之间的同步。实现这种机制的协议称为Zab协议(ZooKeeper原子广播协议)。Zab协议有两种模式,分别是恢复模式(Recoveryelectionmaster)和广播模式(Broadcastsynchronization)。当服务启动或领导者崩溃后,Zab进入恢复模式。当领导者被选举出来,并且大部分服务器完成与领导者的状态同步时,恢复模式结束。状态同步确保领导者和服务器具有相同的系统状态。为了保证事务的顺序一致性,zookeeper使用一个递增的事务id号(zxid)来标识事务。所有提案在提出时都添加了zxid。在实现中,zxid是一个64位的数字,它的高32位被epoch用来标识leader关系是否发生了变化。每选出一个leader,都会有一个新的epoch,用来标识该leader当前的执政时期。低32位用于向上计数。每个server在工作过程中有4种状态:LOOKING:当前server不知道leader是谁,正在搜??索。LEADING:ThecurrentServeristheelectedleader.FOLLOWING:leader已经选出,当前Server与其同步。OBSERVING:观察者的行为在大多数情况下与追随者完全相同,只是不参与选举和投票,而只是接受(观察)选举和投票的结果。LeaderElection当leader崩溃或者leader失去大部分follower时,zk此时进入recovery模式,recovery模式需要重新选举新的leader,将所有server恢复到正确的状态。Zk的选举算法有两种:一种是基于basicpaxos算法,一种是基于fastpaxos算法。系统默认的选举算法是fastpaxos。先介绍一下基本的paxos流程:1、选举线程是发起当前服务器选举的线程。其主要功能是统计投票结果,选择推荐服务器;2、选举线程首先向所有服务器(包括自己)发起查询;3、选举线程收到回复后,验证是否是自己发起的查询(验证zxid是否一致),然后获取对方的id(myid),存入当前查询对象列表,最终得到对方提议的leader相关信息(id,zxid),并将此信息存入本次选举的投票记录表中;4、收到所有服务器的回复后,计算出zxid最大的服务器,并将该服务器的相关信息设置为下一次选举要投票的服务器;5、线程设置zxid最大的当前服务器为当前服务器要推荐的leader。如果此时获胜服务器获得n/2+1个服务器选票,则将当前推荐的leader设置为获胜服务器,并根据获胜服务器的相关信息设置自己的状态,否则,继续此过程直到leaderiselected.通过流程分析,我们可以得出结论:Leader要获得大部分Server的支持,Server的总数必须是奇数2n+1,存活的Server数量不能少于n+1.每个Server启动后都会重复上述过程。在recovery模式下,如果服务器刚刚从崩溃中恢复或者刚刚启动,它也会从磁盘快照中恢复数据和session信息。ZK会记录事务日志,并定期进行快照,以方便恢复时的状态恢复。fastpaxos过程是在选举过程中,一个server首先向所有server提议自己想成为leader。其他服务器收到提案后,解决epoch和zxid的冲突,接受对方的提案,然后将接受提案发送给对方。消息,重复这个过程,最终会选举出一个Leader。Leader工作流Leader具有三个主要功能:恢复数据;与follower保持心跳,接收follower请求,判断follower的请求消息类型;follower的消息类型主要包括PING消息、REQUEST消息、ACK消息、REVALIDATE消息,根据不同的Message类型,进行不同的处理。说明:PING消息是指follower的心跳信息;REQUEST消息是follower发送的提议信息,包括写入请求和同步请求;ACK消息是follower对提案的回复,如果超过半数的follower通过,则提交该提案;REVALIDATEmessage用于延长SESSION的有效时间。Follower工作流程Follower有四个主要功能:向Leader发送请求(PING消息、REQUEST消息、ACK消息、REVALIDATE消息);接收Leader消息并处理;接收Client的请求,如果是写请求,则发送给Leader进行投票;返回客户端结果。Follower的消息循环处理来自Leader的以下消息:PING消息:心跳消息PROPOSAL消息:Leader发起的提案,需要Follower投票OMMIT消息:服务器端最新提案的信息UPTODATE消息:表示同步完成REVALIDATE消息:根据Leader的REVALIDATE结果,关闭会话重新生效或允许其接受消息SYNC消息:返回SYNC结果给客户端,此消息最初由客户端发起强制最新更新。Zab:BroadcastingStateUpdatesZookeeperServer收到一个请求。如果是follower,就会转发给leader。leader执行请求,并以Transaction的形式广播执行。Zookeeper集群如何决定一个Transaction是否被commit执行?通过“两阶段提交协议”(atwo-phasecommit):Leader向所有follower发送PROPOSAL消息。follower收到这个PROPOSAL消息,写入磁盘,并向leader发送ACK消息,告知已经收到。当Leader收到quorum的follower的ACK后,它会发送一个commit消息来执行。Zab协议保证:如果leader按照T1和T2的顺序广播,那么所有的server必须先执行T1再执行T2。如果任何服务器按T1和T2的顺序提交,则所有其他服务器也必须按T1和T2的顺序执行。“两阶段提交协议”最大的问题是如果leader在发送PROPOSAL消息后崩溃或者暂时失去连接,整个集群将处于不确定状态(follower不知道是放弃commit还是执行提交)。Zookeeper此时会选出一个新的leader,请求处理也会转移到新的leader上。不同的领导人被不同的时代所识别。切换leader时,需要解决以下两个问题:1.永远不要忘记传递的消息。领导者在提交给任何追随者之前崩溃,只有它自己提交。新的Leader必须确保这个事务也必须被提交。2.放开被跳过的消息Leader生成某个提案,但是在崩溃之前,没有follower看到这个提案。当服务器恢复时,该提议必须被丢弃。Zookeeper会尽量保证不会同时有两个活跃的Leader,因为两个不同的Leader会导致集群处于不一致的状态,所以Zab协议也保证:在新的leader广播Transaction之前,之前的LeaderLeader提交Transaction将首先执行。在任何时候,没有两个服务器会同时拥有法定人数的支持者。这里的quorum是Server数量的一半以上,准确的说是有投票权的Servers(不包括Observers)。总结简要介绍了Zookeeper的基本原理、数据模型、Session、Watch机制、一致性保证、LeaderElection、Leader和Follower工作流以及Zab协议。
