ZooKeeper架构设计及其应用要点

转载:http://www.aboutyun.com/thread-7731-1-2.html

1.ZooKeeper的数据模型是什么 ?

2.ZooKeeper应用有哪些陷阱 ?
3.每个节点(ZNode)中存储的是什么?
4.一个ZNode维护了一个状态结构都包含了什么?
5.ZNode组成结构是什么?
6.Watches的机制是什么?
7.ZooKeeper内置了哪4种方式实现ACL?






前言
ZooKeeper 是一个开源的分布式服务框架,它是ApacheHadoop 项目的一个子项目,主要用来解决分布式应用场景中存在的一些问题,如:统一命名服务、状态同步服务、集群管理、分布式应用配置管理等,它支持Standalone 模式和分布式模式,在分布式模式下,能够为分布式应用提供高性能和可靠地协调服务,而且使用ZooKeeper 可以大大简化分布式协调服务的实现,为开发分布式应用极大地降低了成本。


总体架构
ZooKeeper 分布式协调服务框架的总体架构,如图所示:
 



ZooKeeper 集群由一组Server 节点组成,这一组Server 节点中存在一个角色为Leader 的节点,其他节点都为Follower 。当客户端Client 连接到ZooKeeper 集群,并且执行写请求时,这些请求会被发送到Leader 节点上,然后Leader 节点上数据变更会同步到集群中其他的Follower 节点。
Leader 节点在接收到数据变更请求后,首先将变更写入本地磁盘,以作恢复之用。当所有的写请求持久化到磁盘以后,才会将变更应用到内存中。
ZooKeeper 使用了一种自定义的原子消息协议,在消息层的这种原子特性,保证了整个协调系统中的节点数据或状态的一致性。Follower 基于这种消息协议能够保证本地的ZooKeeper 数据与Leader 节点同步,然后基于本地的存储来独立地对外提供服务。
当一个Leader 节点发生故障失效时,失败故障是快速响应的,消息层负责重新选择一个Leader ,继续作为协调服务集群的中心,处理客户端写请求,并将ZooKeeper 协调系统的数据变更同步(广播)到其他的Follower 节点。

设计要点
ZooKeeper 是基于如下4 个目标来进行权衡和设计的,我们从设计及其特性的角度来详细说明:


1)简单
分布式应用中的各个进程可以通过ZooKeeper 的命名空间(Namespace )来进行协调,这个命名空间是共享的、具有层次结构的,更重要的是它的结构足够简单,像我们平时接触到的文件系统的目录结构一样容易理解,如图所示:



ZooKeeper 中每个命名空间(Namespace )被称为ZNode ,你可以这样理解,每个ZNode 包含一个路径和与之相关的元数据,以及继承自该节点的孩子列表。与传统文件系统不同的是,ZooKeeper 中的数据保存在内存中,实现了分布式同步服务的高吞吐和低延迟。
在上图示例的ZooKeeper 的数据模型中,有如下要点:

每个节点(ZNode )中存储的是同步相关的数据(这是ZooKeeper 设计的初衷,数据量很小,大概B KB 量级),例如状态信息、配置内容、位置信息等。
一个ZNode 维护了一个状态结构,该结构包括:版本号、ACL 变更、时间戳。每次ZNode 数据发生变化,版本号都会递增,这样客户端的读请求可以基于版本号来检索状态相关数据。
每个ZNode 都有一个ACL ,用来限制是否可以访问该ZNode
在一个命名空间中,对ZNode 上存储的数据执行读和写请求操作都是原子的。
客户端可以在一个ZNode 上设置一个监视器(Watch ),如果该ZNode 数据发生变更,ZooKeeper 会通知客户端,从而触发监视器中实现的逻辑的执行。
每个客户端与ZooKeeper 连接,便建立了一次会话(Session ),会话过程中,可能发生CONNECTING CONNECTED CLOSED 三种状态。
ZooKeeper 支持临时节点(EphemeralNodes )的概念,它是与ZooKeeper 中的会话(Session )相关的,如果连接断开,则该节点被删除。

2)冗余
ZooKeeper 被设计为复制集群架构,每个节点的数据都可以在集群中复制传播,使集群中的每个节点数据同步一致,从而达到服务的可靠性和可用性。前面说到,ZooKeeper 将数据放在内存中来提高性能,为了避免发生单点故障(SPOF ),支持数据的复制来达到冗余存储,这是必不可少的。


3)有序
ZooKeeper 使用时间戳来记录导致状态变更的事务性操作,也就是说,一组事务通过时间戳来保证有序性。基于这一特性。ZooKeeper 可以实现更加高级的抽象操作,如同步等。


4)快速
ZooKeeper 包括读写两种操作,基于ZooKeeper 的分布式应用,如果是读多写少的应用场景(读写比例大约是10:1 ),那么读性能更能够体现出高效。


数据模型
ZooKeeper 有一个分层的命名空间,结构类似文件系统的目录结构,非常简单而直观。其中,ZNode 是最重要的概念,前面我们已经描述过。另外,有ZNode 有关的还包括Watches ACL 、临时节点、序列节点(Sequence Node )。


1)ZNode结构
ZooKeeper 中使用Zxid ZooKeeperTransaction Id )来表示每次节点数据变更,一个Zxid 与一个时间戳对应,所以多个不同的变更对应的事务是有序的。下面是ZNode 的组成结构,引用文档如下所示:

czxid – The zxid of the change that causedthis znode to be created.
mzxid – The zxid of the change that lastmodified this znode.
ctime – The time in milliseconds from epochwhen this znode was created.
mtime – The time in milliseconds from epochwhen this znode was last modified.
version – The number of changes to the dataof this znode.
cversion – The number of changes to thechildren of this znode.
aversion – The number of changes to the ACLof this znode.
ephemeralOwner – The session id of theowner of this znode if the znode is an ephemeral node. If it is not anephemeral node, it will be zero.
dataLength – The length of the data fieldof this znode.
numChildren – The number of children ofthis znode.


2)Watches(监视)
ZooKeeper 中的Watch 是只能触发一次。也就是说,如果客户端在指定的ZNode 设置了Watch ,如果该ZNode 数据发生变更,ZooKeeper 会发送一个变更通知给客户端,同时触发设置的Watch 事件。如果ZNode 数据又发生了变更,客户端在收到第一次通知后没有重新设置该ZNode Watch ,则ZooKeeper 就不会发送一个变更通知给客户端。
ZooKeeper 异步通知设置Watch 的客户端。但是ZooKeeper 能够保证在ZNode 的变更生效之后才会异步地通知客户端,然后客户端才能够看到ZNode 的数据变更。由于网络延迟,多个客户端可能会在不同的时间看到ZNode 数据的变更,但是看到变更的顺序是能够保证有序一致的。
ZNode 可以设置两类Watch ,一个是DataWatches (该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 建立连接。


3)Sequence Nodes(序列节点)
在创建ZNode 的时候,可以请求ZooKeeper 生成序列,以路径名为前缀,计数器紧接在路径名后面,例如,会生成类似如下形式序列
qn-0000000001, qn-0000000002,qn-0000000003, qn-0000000004, qn-0000000005, qn-0000000006, qn-0000000007
对于ZNode 的父节点来说,序列中的每个计数器字符串都是唯一的,最大值为2147483647


4)ACLs(访问控制列表)
ACL 可以控制访问ZooKeeper 的节点,只能应用于特定的ZNode 上,而不能应用于该ZNode 的所有孩子节点上。它主要有如下五种权限:

CREATE  允许创建Child Nodes
READ  允许获取ZNode 的数据,以及该节点的孩子列表
WRITE  可以修改ZNode 的数据
DELETE  可以删除一个孩子节点
ADMIN  可以设置权限

5)ZooKeeper内置了4种方式实现ACL

world  一个单独的ID ,表示任何人都可以访问
auth  不使用ID ,只有认证的用户可以访问
digest  使用username:password 生成MD5 哈希值作为认证ID
ip  使用客户端主机IP 地址来进行认证

6)ZooKeeper Session
当客户端连接到ZooKeeper 集群时,建立了会话。会话过程中的状态变迁,如图所示:


建立连接过程中,会话状态为CONNECTING ;当连接建立成功后,会话状态变为CONNECTED 。会话过程中,如果正常的话,会话的状态只能是CONNECTING CONNECTED 二者之一。如果在会话过程中连接断开,则变为CLOSED 状态。


应用陷阱
并非任何分布式应用都适合使用ZooKeeper 来构建协调服务,我们根据ZooKeeper 提供的文档,给出哪些情况下使用会出现问题,又是如何应对这种问题的。 总结如下:


1)丢失ZNode上的变更通知
客户端连接到ZooKeeper Server 以后,会维护一个TCP 连接。在CONNECTED 状态下,客户端设置了某个ZNode Watch 监听器,可以收到来自该节点变更的通知(后续会触发一定的逻辑执行流程)。但是,如果由于网络异常,客户端断开了与ZooKeeper Server 的连接,在断开的过程中,是无法收到ZooKeeper ZNode 上发送的节点数据变更通知的。
所以,如果使用ZooKeeper Watch ,必须要寻找保持CONNECTED Watch ,才能保证不会丢失该Watch 监控的ZNode 上的数据变更通知。


2)无效ZooKeeper集群节点列表
ZooKeeper 集群交互时,一般情况下客户端会持有一个ZooKeeper 集群节点的列表,或者列表的子集,那么会存在如下两种情况:
一种情况是,如果客户端持有的列表或者列表子集,其中节点都处于Active 状态,能够提供协调服务,那么客户端访问ZooKeeper 集群没有任何问题。
另一种情况,客户端持有ZooKeeper 集群节点列表或列表子集,如果列表中的某些节点因为故障退出了集群,如果客户端再次连接这一类失效的节点,就无法获取服务。
所以,我们在应用中使用ZooKeeper 集群时,一定要明确这一点,或者跳过无效的节点,或者重新寻找有效的节点继续业务处理,或者检查ZooKeeper 集群,使整个集群恢复正常。



3)配置导致的性能问题
如果设置Java 堆内存(Heap )不合理,会导致ZooKeeper 内存不足,会在内存与文件系统之间进行数据交换,导致ZooKeeper 的性能极大地下降,从而可能会影响应用程序。
为了避免Swapping 问题的出现,主要考虑设置足够的Java 堆内存,同时减少被操作系统和Cache 使用的内存,尽量避免在内存与文件系统之间发生数据交换,或者可以将交换限制在一定的范围之内。


4)事务日志存储设备性能
ZooKeeper 会同步事务到存储设备,如果存储设备不是专用的,而是和其他I/O 密集型应用共享同一磁盘,会导致ZooKeeper 的效率。因为客户端请求ZNode 数据变更而发生的事务,ZooKeeper 会在响应之前将事务日志写入存储设备,如果存储设备是专用的,那么整个服务以至外部应用都会获得极大地性能提升。


5)ZNode存储大量数据导致性能问题
ZooKeeper 的设计初衷是,每个ZNode 只存放少量的同步数据,如果存储了大量数据,导致ZooKeeper 每次节点发生变更时需要将事务写入存储设备,同时还要在集群内部复制传播,这将导致不可避免的延迟和性能问题。
所以,如果需要与大量的数据相关,可以将大量数据存储在其他设备中,而只是在ZooKeeper 中存储一个简单的映射,如指针、引用等等。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值