ZooKeeper是一个开源的分布式服务框架,它是Apache Hadoop项目的一个子项目,主要用来解决分布式应用场景中存在的一些问题,如:统一命名服务、状态同步服务、集群管理、分布式应用配置管理等,它支持Standalone模式和分布式模式,在分布式模式下,可以为分布式应用提供高性能和可靠地协调服务,并且使用ZooKeeper能够大大简化分布式协调服务的实现,为开发分布式应用极大地下降了成本。html
整体架构java
ZooKeeper分布式协调服务框架的整体架构,如图所示: ZooKeeper集群由一组Server节点组成,这一组Server节点中存在一个角色为Leader的节点,其余节点都为Follower。当客户端Client链接到ZooKeeper集群,而且执行写请求时,这些请求会被发送到Leader节点上,而后Leader节点上数据变动会同步到集群中其余的Follower节点。 Leader节点在接收到数据变动请求后,首先将变动写入本地磁盘,以做恢复之用。当全部的写请求持久化到磁盘之后,才会将变动应用到内存中。 ZooKeeper使用了一种自定义的原子消息协议,在消息层的这种原子特性,保证了整个协调系统中的节点数据或状态的一致性。Follower基于这种消息协议可以保证本地的ZooKeeper数据与Leader节点同步,而后基于本地的存储来独立地对外提供服务。 当一个Leader节点发生故障失效时,失败故障是快速响应的,消息层负责从新选择一个Leader,继续做为协调服务集群的中心,处理客户端写请求,并将ZooKeeper协调系统的数据变动同步(广播)到其余的Follower节点。node
设计要点apache
ZooKeeper是基于以下4个目标来进行权衡和设计的,咱们从设计及其特性的角度来详细说明:网络
分布式应用中的各个进程能够经过ZooKeeper的命名空间(Namespace)来进行协调,这个命名空间是共享的、具备层次结构的,更重要的是它的结构足够简单,像咱们平时接触到的文件系统的目录结构同样容易理解,如图所示: 在ZooKeeper中每一个命名空间(Namespace)被称为ZNode,你能够这样理解,每一个ZNode包含一个路径和与之相关的元数据,以及继承自该节点的孩子列表。与传统文件系统不一样的是,ZooKeeper中的数据保存在内存中,实现了分布式同步服务的高吞吐和低延迟。 在上图示例的ZooKeeper的数据模型中,有以下要点:session
ZooKeeper被设计为复制集群架构,每一个节点的数据均可以在集群中复制传播,使集群中的每一个节点数据同步一致,从而达到服务的可靠性和可用性。前面说到,ZooKeeper将数据放在内存中来提升性能,为了不发生单点故障(SPOF),支持数据的复制来达到冗余存储,这是必不可少的。架构
ZooKeeper使用时间戳来记录致使状态变动的事务性操做,也就是说,一组事务经过时间戳来保证有序性。基于这一特性。ZooKeeper能够实现更加高级的抽象操做,如同步等。app
ZooKeeper包括读写两种操做,基于ZooKeeper的分布式应用,若是是读多写少的应用场景(读写比例大约是10:1),那么读性能更可以体现出高效。框架
数据模型异步
ZooKeeper有一个分层的命名空间,结构相似文件系统的目录结构,很是简单而直观。其中,ZNode是最重要的概念,前面咱们已经描述过。另外,有ZNode有关的还包括Watches、ACL、临时节点、序列节点(Sequence Node)。
ZooKeeper中使用Zxid(ZooKeeper Transaction Id)来表示每次节点数据变动,一个Zxid与一个时间戳对应,因此多个不一样的变动对应的事务是有序的。下面是ZNode的组成结构,引用文档以下所示:
ZooKeeper中的Watch是只能触发一次。也就是说,若是客户端在指定的ZNode设置了Watch,若是该ZNode数据发生变动,ZooKeeper会发送一个变动通知给客户端,同时触发设置的Watch事件。若是ZNode数据又发生了变动,客户端在收到第一次通知后没有从新设置该ZNode的Watch,则ZooKeeper就不会发送一个变动通知给客户端。 ZooKeeper异步通知设置Watch的客户端。可是ZooKeeper可以保证在ZNode的变动生效以后才会异步地通知客户端,而后客户端才可以看到ZNode的数据变动。因为网络延迟,多个客户端可能会在不一样的时间看到ZNode数据的变动,可是看到变动的顺序是可以保证有序一致的。 ZNode能够设置两类Watch,一个是Data Watches(该ZNode的数据变动致使触发Watch事件),另外一个是Child Watches(该ZNode的孩子节点发生变动致使触发Watch事件)。调用getData()和exists() 方法能够设置Data Watches,调用getChildren()方法能够设置Child Watches。调用setData()方法触发在该ZNode的注册的Data Watches。调用create()方法建立一个ZNode,将触发该ZNode的Data Watches;调用create()方法建立ZNode的孩子节点,则触发ZNode的Child Watches。调用delete()方法删除ZNode,则同时触发Data Watches和Child Watches,若是该被删除的ZNode还有父节点,则父节点触发一个Child Watches。 另外,若是客户端与ZooKeeper Server断开链接,客户端就没法触发Watches,除非再次与ZooKeeper Server创建链接。
在建立ZNode的时候,能够请求ZooKeeper生成序列,以路径名为前缀,计数器紧接在路径名后面,例如,会生成相似以下形式序列:
qn-0000000001, qn-0000000002, qn-0000000003, qn-0000000004, qn-0000000005, qn-0000000006, qn-0000000007
对于ZNode的父节点来讲,序列中的每一个计数器字符串都是惟一的,最大值为2147483647。
ACL能够控制访问ZooKeeper的节点,只能应用于特定的ZNode上,而不能应用于该ZNode的全部孩子节点上。它主要有以下五种权限:
ZooKeeper内置了4种方式实现ACL:
当客户端链接到ZooKeeper集群时,创建了会话。会话过程当中的状态变迁,如图所示: 创建链接过程当中,会话状态为CONNECTING;当链接创建成功后,会话状态变为CONNECTED。会话过程当中,若是正常的话,会话的状态只能是CONNECTING和CONNECTED两者之一。若是在会话过程当中链接断开,则变为CLOSED状态。
应用陷阱
并不是任何分布式应用都适合使用ZooKeeper来构建协调服务,咱们根据ZooKeeper提供的文档,给出哪些状况下使用会出现问题,又是如何应对这种问题的。总结以下:
客户端链接到ZooKeeper Server之后,会维护一个TCP链接。在CONNECTED状态下,客户端设置了某个ZNode的Watch监听器,能够收到来自该节点变动的通知(后续会触发必定的逻辑执行流程)。可是,若是因为网络异常,客户端断开了与ZooKeeper Server的链接,在断开的过程当中,是没法收到ZooKeeper在ZNode上发送的节点数据变动通知的。 因此,若是使用ZooKeeper的Watch,必需要寻找保持CONNECTED的Watch,才能保证不会丢失该Watch监控的ZNode上的数据变动通知。
与ZooKeeper集群交互时,通常状况下客户端会持有一个ZooKeeper集群节点的列表,或者列表的子集,那么会存在以下两种状况: 一种状况是,若是客户端持有的列表或者列表子集,其中节点都处于Active状态,可以提供协调服务,那么客户端访问ZooKeeper集群没有任何问题。 另外一种状况,客户端持有ZooKeeper集群节点列表或列表子集,若是列表中的某些节点由于故障退出了集群,若是客户端再次链接这一类失效的节点,就没法获取服务。 因此,咱们在应用中使用ZooKeeper集群时,必定要明确这一点,或者跳过无效的节点,或者从新寻找有效的节点继续业务处理,或者检查ZooKeeper集群,使整个集群恢复正常。
若是设置Java堆内存(Heap)不合理,会致使ZooKeeper内存不足,会在内存与文件系统之间进行数据交换,致使ZooKeeper的性能极大地降低,从而可能会影响应用程序。 为了不Swapping问题的出现,主要考虑设置足够的Java堆内存,同时减小被操做系统和Cache使用的内存,尽可能避免在内存与文件系统之间发生数据交换,或者能够将交换限制在必定的范围以内。
ZooKeeper会同步事务到存储设备,若是存储设备不是专用的,而是和其余I/O密集型应用共享同一磁盘,会致使ZooKeeper的效率。由于客户端请求ZNode数据变动而发生的事务,ZooKeeper会在响应以前将事务日志写入存储设备,若是存储设备是专用的,那么整个服务以致外部应用都会得到极大地性能提高。
ZooKeeper的设计初衷是,每一个ZNode只存放少许的同步数据,若是存储了大量数据,致使ZooKeeper每次节点发生变动时须要将事务写入存储设备,同时还要在集群内部复制传播,这将致使不可避免的延迟和性能问题。 因此,若是须要与大量的数据相关,能够将大量数据存储在其余设备中,而只是在ZooKeeper中存储一个简单的映射,如指针、引用等等。
参考连接
本文基于署名-非商业性使用-相同方式共享 4.0许可协议发布,欢迎转载、使用、从新发布,但务必保留文章署名时延军(包含连接:http://shiyanjun.cn),不得用于商业目的,基于本文修改后的做品务必以相同的许可发布。若有任何疑问,请与我联系。