@TOC
1 Zookeeper的设计
1.1 什么是Zookeeper?
可用于实现高度可靠的分布式高性能协调的服务,Zookeeper是用于维护配置信息,命名,提供分布式同步以及提供组服务的集中式服务,使用Zookeeper可以实现共识,组管理,领导者选举和状态协议,也可以根据自己的需求来构建分布式应用功能,Zookeeper目前是apache下的开源项目,本文档以3.6.2版本进行说明。代码位置:
https://github.com/apache/zookeeper/tree/release-3.6.2
1.2 Zookeeper有什么特性?
Zookeeper的实现重视高性能,高可用和严格的有序性。高性能意味着它可以在大型分布式系统中使用。可靠性方面使他不至于成为单点故障。严格排序意味着复杂的原语可以在客户端实现。
1.2.1 Zookeeper的设计简单
- 类似标准文件系统的命名空间,分布式程序可以共享层次化的命名空间进行协调
- 命名空间由数据组成简称为znode
- Zookeeper的数据保存在内存中,具有高吞吐量和低延迟的特性
1.2.2 可复制
集群中的zookeeper节点之间可复制数据,只要大多数服务器可用,Zookeeper服务就可用
持久化事物日志和快照到磁盘
客户端连接到单个Zookeeper如果TCP连接断开也客户端可以连接到其他机器。
1.2.3有序性
Zookeeper的每个更新都会有一个数字来记录Zookeeper事物的顺序,后续的操作可以使用该顺序来实现更高级别的抽象,比如同步原语。
1.2.4高性能
具有较高的读性能,可以在数千台机器上运行
1.3 Zookeeper数据模型?
1.3.1 数据模型和分层命名空间
Zookeeper使用的命名空间更像是标准的文件系统,路径则使用斜杠/来分割
1.3.2节点和临时节点
与标准文件系统有所区别的是,zookeeper命名空间中的每个节点都可以有与其关联的数据和子节点。每个节点既可以当做文件也可以当做目录来看,Zookeeper的这些节点用来存储协调数据包含状态信息,配置信息,位置信息等,默认情况下一个节点数据通常比较小,在千字节范围之内
我们一般简称Zookeeper的节点为znode
Zookeeper也有临时节点的概念,只要创建Znode的会话就会处于活动状态,这些znode会一直存在,当会话结束时,znode被删除。
1.3.3 Zookeeper节点Znode?
Zookeeper树中的每个节点被称为znode,znode是程序访问的主要对象,znode具有如下特征
- 可监听
- 数据可访问
- 临时节点和持久节点
- 顺序节点
- 容器节点3.6.0版本开始
- TTL节点3.6.0版本开始
可监听
客户端可以为每个znode设置一个监听器,对已设置了监听器的znode进行了更改,将触发监听方法
数据可访问
对于命名空间上的znode我们可以直接查询数据,写入数据,写入数据如果存在数据则替换数据。Zookeeper上的数据主要是用来存储配置,状态信息,会话等用来协调分布式服务的数据,每个节点上的数据不能超过1M,如果有存储较大数据的需求可以在zookeeper节点中记录路径,将较大的数据存储适合存储较大数据的系统中。
临时节点
Zookeeper支持创建临时节点,临时节点的意思是创建节点后只要会话存在则临时节点就存在,并且临时节点不能有子节点。
顺序节点
顺序节点的实现就是在创建节点时候我们通过对路径设置编号,当我们按属于使用节点的时候就可以通过序号来判断大小
1.3.2 znode的统计信息
Zookeeper节点的统计数据有哪些,分别有什么含义
每个znode都会有一些统计信息方便我们排查问题,查询节点情况使用,接下来我们看下每个统计信息的含义:
- czxid 创建这个znode时的zxid。
- mzxid 上次修改这个znode的zxid
- pzxid 这个znode最后修改的子节点的zxid。
- ctime 创建znode的epoch开始的时间,单位毫秒
- mtime 从epoch开始这个znode最后一次修改的时间,以毫秒为单位,
- version(之前版本为dataVersion) 该znode的数据更改次数。
- cversion 该znode的子节点的修改次数
- aversion 此znode的ACL修改次数。
- ephemeralOwner如果该znode是临时节点,则该znode所有者的会话id。如果它不是临时节点,则为零。
- dataLength znode的数据字段的长度
- numChildren 该znode的子节点数。
1.3.3 更新监听机制
客户端可以在znode上设置一个监听事件,当znode改变时一个监听事件将被触发
客户端可以收到一个通知znode已经改变的数据包,如果客户端与zookeeper服务器连接中断,客户端将收到一个连接断开的通知事件
Zookeeper监听有什么特征?
ZooKeeper中的所有读操作——getData()、getChildren()和exists()——都有设置一个watch作为副作用的选项。
以下是ZooKeeper对手表的定义:
- 监听事件是一次性触发的。
- 异步顺序发送给设置监听的客户端。
- 当设置的数据发生变化时发生。
在这个手表的定义中有三个要点需要考虑:
监听事件是一次性触发器:
当数据发生变更的时候,监听事件将会被发送至客户端一次,后续发生数据变更将不会再重新发送监听事件,如果客户端需要监听新的事件,需要重新设置监听。如果有这个需求客户端框架也也可以参考cruator客户端框架反复监听方法实现。
异步顺序发送给设置监听的客户端
异步发送的监听网络传输事件无法保证哪个客户端先执行,但是每一个客户端内的接收到的监听事件都是顺序是一致的。
当设置的数据发生变化时发生
这指的是一个节点可以改变的不同方式。我们可以把ZooKeeper看作是维护两个表:数据表和孩子表。getData()和exists()设置数据监听。getChildren()设置子节点监听。或者,可以考虑根据返回的数据类型对监听进行设置。getData()和exists()返回关于节点数据的信息,而getChildren()返回一组子节点。因此,setData()将为正在设置的znode触发数据监听(假设设置成功)。一个成功的create()将为创建的znode触发一个数据监视,并为父znode触发一个子监听。一个成功的delete()将为一个被删除的znode触发一个数据监听和一个子监听(因为没有更多的子监听了),以及一个父znode的子监听
Zookeeper监听事件有哪些
我们可以通过读取ZooKeeper状态的三个调用来设置手表:exists、getData和getChildren。下面的列表详细说明了watch可以触发的事件以及使它们生效的调用:
创建的事件:通过调用exists来启用。
删除事件:通过调用exists、getData和getChildren来启用。
更改事件:通过调用exists和getData来启用。
子事件:通过调用getChildren来启用。
监听使用的注意事项
标准监听器是一次性触发器;如果你获得了一个watch事件,并且想要得到未来更改的通知,必须设置另一个watch。
因为标准的监听是一次性触发器,并且在获取事件和发送一个新的请求来获取监听之间有延迟,不能可靠地看到发生在ZooKeeper节点上的每一个变化。准备好处理znode在获取事件和再次设置监听之间多次更改的情况。
当客户端断开与服务器的连接时(例如,当服务器发生故障时),客户端将不会获得任何监听事件,直到连接重新建立。因此,会话事件被发送到所有未执行的监听处理程序。使用会话事件进入安全模式:断开连接时将不会接收事件,因此应该考虑这种情况。
1.3.4 Zookeeper做的保证
Zookeeper非常快速和简单,作为构建复杂分布式系统的基础zookeeper提供的保证
- 顺序一致性-来自客户端的更新将按照他们被发送的顺序
- 原子性-更新要么成功要么失败
- 单系统视图映像-客户端将看到Zookeeper服务的相同视图,而不管它连接到哪个服务器,也就是说,即使客户端故障转移到具有相同会话的不同服务器上,客户端也永远不会看到系统的旧视图
- 可靠性-一旦更新被应用,它将从那时起一直持续,直到客户端覆盖更新。
- 及时性-系统的客户端视图保证在一定时间范围内是最新的。
1.3.4 简单的API
Zookeeper的其中一个设计目标就是提供非常简单的API,目前只提供了如下操作
- create 创建一个节点
- delete 删除一个节点
- exists 测试是否节点存在
- get data 从节点上读取数据
- set data 写数据到节点上
- get children 检索子节点列表
- sync 等待数据传播
1.3.5 Zookeeper数据模型
ZooKeeper有一个层次结构的命名空间,很像一个分布式文件系统。唯一的区别是命名空间中的每个节点都可以拥有与其关联的数据以及子节点。
这就像有一个文件系统,它允许一个文件同时也是一个目录。到节点的路径总是表示为规范的、绝对的、斜杠分隔的路径; 下面就是路径命名的基本规则
空字符(\u0000)不能是路径名的一部分。(这导致了C绑定的问题。)
以下字符不能使用,因为它们不能很好地显示,或者以令人困惑的方式呈现:\u0001 - \u001F和\u007F u009F。
不允许使用以下字符:\ud800 - uF8FF, \uFFF0 - uFFFF。
“.”可以作为另一个名称的一部分,但“.”和“..”不能单独用来表示路径上的节点,因为ZooKeeper不使用相对路径。以下是无效的:”/a/b/./c”或“c/a/b../”。
保留“zookeeper” 关键字。
1.3.6 Zookeeper如何做访问控制权限?
Zookeeper的访问控制ACL的实现与UNIX文件访问权限非常相似:它使用权限位来允许/不允许对一个节点的各种操作,以及这些权限位所适用的范围。与标准的UNIX权限不同,ZooKeeper节点不受用户(文件所有者)、组(group)和其他用户(other)这三个标准作用域的限制。ZooKeeper没有znode的所有者的概念。相反,ACL指定与这些id相关联的id和权限集。
ACL只属于特定的znode。 不适用于子节点。例如,如果/app只能通过ip:172.16.16.1读取,而/app/status是全局可读的,任何人都可以读取/app/status; acl不是递归的。
ACL的权限
ZooKeeper支持以下权限:
- CREATE: 可以创建一个子节点
- READ: 可以从一个节点获取数据并列出它的子节点
- WRITE: 对节点进行数据设置
- DELETE:删除子节点
- ADMIN: 可以设置权限
Zookeeper有以下内置ACL方案:
world 有一个单一的id,代表任何人。
auth 是一种特殊的方案,它忽略任何提供的表达式,而使用当前用户、凭据和方案。当保存ACL时,ZooKeeper服务器会忽略提供的任何表达式(不管是user like with SASL authentication还是user:password like with DIGEST authentication)。但是,ACL中仍然必须提供表达式,因为ACL必须匹配表单scheme:expression:perms。提供此方案是为了方便用户创建znode,然后将对该znode的访问限制给该用户,这是一种常见的用例。如果没有被认证的用户,使用auth方案设置ACL会失败。
digest 使用username:password字符串生成MD5哈希,然后用作ACL ID标识。身份验证是通过明文发送用户名:密码来完成的。当在ACL中使用时,表达式将是用户名:base64编码的SHA1密码摘要。
ip 使用客户端主机ip作为ACL ID标识。ACL表达式的形式是addr/bits,其中addr的最有效位与客户端主机IP的最有效位相匹配。
x509 使用client X500主体作为ACL ID标识。ACL表达式就是客户机的X500主体名。当使用安全端口时,客户端将自动进行身份验证,并设置其x509方案的认证信息。
技术咨询与支持,可以扫描微信公众号进行回复咨询
Zookeeper16, Zookeeper源码解析16