因为一台服务器的处理能力是有限的,在大用户量和高并发的状况下,一般须要不少台服务器同时工做对外提供服务。这么多机器同时工做,怎么来管理这些服务器呢?好比某台服务器宕机了,就要确保请求再也不发送到这台服务器;某个程序的配置修改了,多台服务器上的配置要做相应修改;或者多个服务必须按照特定的顺序执行;若是多个服务是有事务的,若是中间某个服务失败了,那这多个服务都必须回滚。对于这些状况最好是有一个专门的管理中心来管理,而ZooKeeper就能够做为这个管理中心。node
ZooKeeper是Apache下的一个Java开源项目(最初由Yahoo开发,后捐献给了Apache)。
ZooKeeper的原始功能很简单,基于它的层次型的目录树的数据结构,并经过对树上的节点进行有效管理,能够设计出各类各样的分布式集群管理功能。此外,ZooKeeper自己也是分布式的。算法
Zookeeper 会维护一个具备层次关系的树状的数据结构,它很是相似于一个标准的文件系统,以下图所示:数据库
ZooKeeper树状结构中的每个节点称做——<font color=#D2691E>Znode</font>。每个节点都有一个名称,且以 /
开头,其中最顶层的节点是 /
,最终的状况就是每个节点在树状结构中,会有一个相似绝对路径的惟一标识,如上图中的 Server1
这个Znode 的标识为 /NameService/Server1
服务器
虽然ZooKeeper的树状结构相似文件系统,可是Znode兼有文件和目录的特色,一个Znode既能在它下面建立子节点,做为路径标识的一部分,同时这个节点同时也能存储数据,但这个存储不是设计用来做常规的数据库存储,而主要存放分布式应用的配置信息、状态信息等,这些数据的共同特性就是它们都是很小的数据,一般以KB为单位。网络
ZooKeeper数据模型中的每一个Znode都维护着一个 <font color=#D2691E>stat</font> 结构。
一个stat仅提供一个Znode的元数据。它由版本号,操做控制列表(ACL),时间戳和数据长度组成。session
版本号 - 每一个Znode都有版本号,这意味着每当与Znode相关联的数据发生变化时,其对应的版本号也会增长。当多个ZooKeeper客户端尝试在同一Znode上执行操做时,版本号的使用就很重要。数据结构
操做控制列表(ACL) - ACL基本上是访问Znode的认证机制。它管理全部Znode读取和写入操做。每个节点都拥有本身的ACL,这个列表规定了用户的权限,即限定了特定用户对目标节点能够执行的操做。权限的种类有:并发
时间戳 - 导致ZooKeeper节点状态改变的每个操做都将使节点接收到一个Zxid格式的时间戳,而且这个时间戳全局有序。也就是说,每一个对节点的改变都将产生一个惟一的Zxid。若是Zxid1的值小于Zxid2的值,那么Zxid1所对应的事件发生在Zxid2所对应的事件以前。实际上,ZooKeeper的每一个节点维护者三个Zxid值,为别为:cZxid、mZxid、pZxid。app
每一个Znode由3部分组成:异步
ZooKeeper中的节点有两种,分别为临时(ephemeral)节点和永久(persistent)节点。节点的类型在建立时即被肯定,而且不能改变。
<font color=#D2691E>顺序节点</font>能够是持久的或临时的。当一个新的Znode被建立为一个顺序节点时,ZooKeeper经过将10位的序列号附加到原始名称来设置Znode的路径。例如,若是将具备路径/myapp
的Znode建立为顺序节点,则ZooKeeper会将路径更改成/myapp0000000001
,并将下一个序列号设置为0000000002
,这个序列号由父节点维护。若是两个顺序节点是同时建立的,那么ZooKeeper不会对每一个Znode使用相同的数字。顺序节点在锁定和同步中起重要做用,顺序号能够被用于为全部的事件进行全局排序,这样客户端能够经过顺序号推断事件的顺序。
综合上面两节内容,ZooKeeper有四种形式的节点:
一个节点自身拥有表示其状态的许多重要属性,以下图所示。
Zookeeper 的客户端和服务器通讯采用长链接方式,每一个客户端和服务器经过心跳来保持链接,这个链接状态称为 Session。
会话对于ZooKeeper的操做很是重要。会话中的请求按FIFO顺序执行。一旦客户端链接到服务器,将创建会话并向客户端分配会话ID 。
客户端以特定的时间间隔发送心跳以保持会话有效。若是ZooKeeper集群在超过指定的时间都没有从客户端接收到心跳,则会话会被认为结束(会话超时)。会话超时一般以毫秒为单位。
Client和Zookeeper集群创建链接,整个session状态变化如图所示:
若是Client由于Timeout和Zookeeper Server失去链接,client处在CONNECTING状态,会自动尝试再去链接Server,若是在session有效期内再次成功链接到某个Server,则回到CONNECTED状态。
注意:若是由于网络状态很差,client和Server失去联系,client会停留在当前状态,会尝试主动再次链接Zookeeper Server。client不能宣称本身的session expired,session expired是由Zookeeper Server来决定的,client能够选择本身主动关闭session。
Zookeeper watch是一种监听通知机制。客户端注册监听它关心的目录节点,当目录节点发生变化(数据改变、被删除、子目录节点增长删除)时,zookeeper会通知客户端。,监视事件能够理解为一次性的触发器。官方定义以下:
a watch event is one-time trigger, sent to the client that set the watch, which occurs when the data for which the watch was set changes。
Watch的关键点:
当设置监视的数据发生改变时,该监视事件会被发送到客户端,例如,若是客户端调用了getData("/znode1", true) 而且稍后 /znode1 节点上的数据发生了改变或者被删除了,客户端将会获取到 /znode1 发生变化的监视事件,而若是 /znode1 再一次发生了变化,除非客户端再次对/znode1 设置监视,不然客户端不会收到事件通知。
Zookeeper客户端和服务端是经过 socket 进行通讯的,因为网络存在故障,因此监视事件颇有可能不会成功地到达客户端,监视事件是异步发送至监视者的,Zookeeper 自己提供了顺序保证(ordering guarantee):即客户端只有首先看到了监视事件后,才会感知到它所设置监视的znode发生了变化(a client will never see a change for which it has set a watch until it first sees the watch event)。网络延迟或者其余因素可能致使不一样的客户端在不一样的时刻感知某一监视事件,可是不一样的客户端所看到的一切具备一致的顺序(有序一致性)。
ZooKeeper能够为全部的读操做设置watch,这些读操做包括:exists()、getChildren()及getData()。
ZooKeeper所管理的watch能够分为两类:
咱们能够经过操做返回的数据来设置不一样的watch:
所以
watch注册与处触发
Zookeeper 中的监视是轻量级的,所以容易设置、维护和分发。当客户端与 Zookeeper 服务器失去联系时,客户端并不会收到监视事件的通知,只有当客户端从新链接后,若在必要的状况下,之前注册的监视会从新被注册并触发,对于开发人员来讲这一般是透明的。只有一种状况会致使监视事件的丢失,即:经过exists()设置了某个znode节点的监视,可是若是某个客户端在此znode节点被建立和删除的时间间隔内与zookeeper服务器失去了联系,该客户端即便稍后从新链接 zookeeper服务器后也得不到事件通知。
咱们使用ZooKeeper,简单地理解就是使用ZooKeeper的<font color=#D2691E>文件系统+通知机制</font>。
Zookeeper服务自身组成一个集群(2n+1个服务容许n个失效)。在Zookeeper集群中,主要分为三者角色,而每个节点同时只能扮演一种角色,这三种角色分别是:
<font color=#D2691E>Leader</font>:
<font color=#D2691E>Follower</font>:
<font color=#D2691E>Observer</font>:
Propsal投票:每个事务都须要集群中超过半数的机器投票承认才能被真正地应用到ZK的内存数据库中。
下图描述了 ZooKeeper集群“客户端-服务端”的结构
Zookeeper提供的一致性是弱一致性,数据的同步有以下规则:ZooKeeper确保对znode树的每个修改都会被同步到集群中超过半数的机器上,那么就认为更新成功。因此就有可能有节点的数据不是最新的而被客户端访问到。而且会有一个时间点,数据在集群中是不一致的.也就是Zookeeper只保证最终一致性,可是实时的一致性能够由客户端调用本身来保证,经过调用sync()方法
sync()
接口。可靠性:一旦一个更新操做被应用,那么在客户端再次更新它以前,它的值将不会改变。这个保证将会产生下面两种结果:
有了这些一致性保证, ZooKeeper 更高级功能的设计与实现将会变得很是容易,例如: leader 选举、队列以及可撤销锁等机制的实现。
用分布式系统的CAP原则来分析ZooKeeper.
sync()
P: 有2点须要分析的.
在ZooKeeper的集群中,各个节点共有下面3种角色和4种状态:
4种状态的解释:
ZooKeeper的核心是原子广播,这个机制保证了各个server之间的同步。实现这个机制的协议叫作Zab协议(ZooKeeper Atomic Broadcast protocol)。Zab协议有两种模式,它们分别是恢复模式(Recovery选主)和广播模式(Broadcast同步)。
当服务启动或者在leader崩溃后,Zab就进入了恢复模式,当领导者被选举出来,且大多数server完成了和leader的状态同步之后,恢复模式就结束了。状态同步保证了leader和Server具备相同的系统状态。
为了保证事务的顺序一致性,ZooKeeper采用了递增的事务id号(zxid)来标识事务。全部的提议(proposal)都在被提出的时候加上了zxid。实现中zxid是一个64位的数字,它高32位是epoch用来标识leader关系是否改变,每次一个leader被选出来,它都会有一个新的epoch(每leader选举一次+1),标识当前属于那个leader的统治时期。低32位为事务操做次数(每增长一次事务+1)
当leader崩溃或者集群启动,这时候zk进入恢复模式,恢复模式须要从新选举出一个新的leader,让全部的Server都恢复到一个正确的状态。Zk的选举算法有两种:一种是基于basic paxos实现的,另一种是基于fast paxos算法实现的。系统默认的选举算法为fast paxos。先介绍basic paxos流程:
经过流程分析咱们能够得出:要使Leader得到多数Server的支持,则Server总数必须是奇数2n+1,且存活的Server的数目不得少于n+1.
每一个Server启动后都会重复以上流程。在恢复模式下,若是是刚从崩溃状态恢复的或者刚启动的server还会从磁盘快照中恢复数据和会话信息,zk会记录事务日志并按期进行快照,方便在恢复时进行状态恢复。选主的具体流程图以下所示:
fast paxos流程是在选举过程当中,某Server首先向全部Server提议本身要成为leader,当其它Server收到提议之后,解决epoch和zxid的冲突,并接受对方的提议,而后向对方发送接受提议完成的消息,重复这个流程,最后必定能选举出Leader。其流程以下所示: