Zookeeper 数据模型
Zookeeper有一个层级的名称空间(namespace),这一点特别像一个分布式文件系统,与分布式文件系统唯一不同的是,名称空间(namespace)中的每个节点都可以具有与其关联的数据以及子节点。就像拥有一个文件系统一样,该文件系统也允许文件成为目录。节点的路径始终表示为规范的,绝对的,斜杠分隔的路径。没有相对参考。 遵循以下约束,可以在路径中使用任何unicode字符:
- 空字符(\ u0000)不能是路径名的一部分。 (这会导致C绑定出现问题)
- \u0001 - \u001F、\u007F 这些字符不能使用。因为它们不能很好地显示,或以令人困惑的方式呈现;
- \ud800 - uF8FF、 \uFFF0 - uFFFF,这些字符不被允许使用;
- "." 字符可以用作其他名称的一部分,但是"." 和".." 不能单独作为路径上的节点,应为Zookeeper不能使用相对路径,像 "/a/b/./c" 或者 "/a/b/../c",都是非法的路径。
ZNodes(数据节点)
ZooKeeper树中的每个节点都称为 znode。Znodes维护一个状态结构,其中包括数据更改、ACL(Access Control List访问控制列表)更改的版本号,和时间戳。版本号和时间戳一起,使ZooKeeper可以验证缓存并协调更新。znode的数据每次更改时,版本号都会增加。例如,每当客户端检索数据时,它也接收数据的版本。并且当客户端执行更新或删除时,它必须提供它正在更改的znode的数据版本。如果它提供的版本与数据的实际版本不匹配,则更新将失败。
Znodes是程序设计员访问的主要实体。 它们具有几个特征,在这里值得一提:
Watches(监控)
客户端可以在znodes上设置监控,对该znode的更改会触发监控,然后清除监控。监控触发时,ZooKeeper会向客户端发送通知。
Data Access(数据访问)
存储在名称空间中每个znode上的数据都会被原子地读取和写入。读取将获取与znode关联的所有数据字节,而写入将替换所有数据。每个节点都有一个访问控制列表(ACL),用于限制谁可以执行操作。
ZooKeeper并非被设计为通用数据库或大型对象的存储。相反,它管理协调数据。这些数据可以采用 配置、状态信息、集合点等形式。各种形式的协调数据的共同特征是它们相对较小:以千字节为单位。ZooKeeper客户端和服务器实现具有健全性检查,以确保znode的数据少于1M,但是数据应该比平均值少的多。在相对大的数据上进行操作将导致某些操作比其他操作花费更多的时间,并且会造成某些操作的延迟,因为需要更多时间才能通过网络将更多数据移动到存储介质上。
。如果需要大数据存储,则处理此类数据的通常方式是,将其存储在大容量存储系统上,比如: NFS 或者 HDFS,并将指针存储到ZooKeeper中的存储位置。
Ephemeral Nodes(临时节点)
ZooKeeper还具有临时节点的概念。只要创建该临时节点的客户端会话处于活动状态,这些临时节点就会一直存在。当会话因超时或发生异常而关闭,节点也会被删除。由于这种特性,临时节点不允许有子节点。可以使用getEphemerals()方法获取会话的临时节点列表。
getEphemerals() // 获取会话的临时节点列表
getEphemerals() 用来获取 会话为给定的路径创建的临时节点列表。如果路径为空,它将列出该会话的所有临时节点。假设有一个场景:一个会话的临时节点需要被收集,以进行重复数据输入校验,并且节点是以顺序方式创建的,因此你不知道进行重复校验的名称。以上场景中,getEphemerals()方法可以获取会话的临时节点列表,这可能是服务发现的典型用例。
Sequence Nodes(有序节点,命名是唯一的)
创建节点时,你还可以要求ZooKeeper在路径末尾附加一个单调递增的计数器,该计数器对于父节点是唯一的。计数器的格式是%010d -- 也就是用0填充满10位数(计数器这样的格式是为了简化排序),即 "000000000001"。用于存储下一个序列号的计数器是父节点维护的有符号的整形int(4字节),递增超过2147483647(结果为“ -2147483648”)时,计数器将溢出。
Time in ZooKeeper
ZooKeeper通过多种方式追踪时间:
- Zxid:对ZooKeeper状态的每次更改都会产生一个zxid (ZooKeeper事务Id)形式的戳。这将向ZooKeeper公开所有更改的总顺序。每次更改都会有一个唯一的事务id,而且如果zxid1小于zxid2,则zxid1发生在zxid2之前。
- Version numbers:对节点的每次更改都会导致该节点的版本号增加。有三个版本号:version(对节点数据修改的版本号)、cversion(对节点的子节点修改的版本号)、aversion(对节点的ACL修改的版本号)。
- Ticks:当使用多服务的zookeeper时,servers使用ticks来定义事件的时间单位,如状态上传、会话超时、server之间的连接超时。
- Real time:ZooKeeper除了在创建znode和修改znode时把时间戳放到状态结构中,根本不使用实时的,或者时钟时间。
Zookeeper的状态结构
zookeeper每个节点都有自己的状态信息,他们包含以下状态属性:
- czxid:创建此节点的 更改事务id
- mzxid:最后修改此节点的 更改事务id
- pzxid:最后修改znode子节点的 更改事务id
- ctime:创建此节点的起始时间(以毫秒为单位)
- mtime:最后修改此节点的时间(以毫秒为单位)
- version:对节点数据修改的版本号
- cversion:对znode的子节点修改的版本号
- aversion:对节点的ACL修改的版本号
- ephemeralOwner:如果znode是临时节点,则时znode的所有者的会话ID;若不是临时节点,则为0
- dataLength:此znode的数据字段的长度
- numChildren:znode子节点数量
ZooKeeper Sessions(Zookeeper会话)
ZooKeeper客户端通过使用语言绑定创建一个服务句柄来建立与ZooKeeper服务的会话。一旦创建,句柄开始处于CONNECTING状态,客户端尝试连接到组成ZooKeeper服务的服务器之一,此时它切换到CONNECTED状态。在正常操作过程中,客户端句柄将处于这两种状态之一。如果发生了不可恢复的错误,比如会话过期或验证失败,或者应用程序明确关闭了句柄,句柄将移动到CLOSED状态。下图显示了ZooKeeper客户端可能的状态转换。
state_dia.jpg
要创建一个客户端会话,应用程序代码必须提供一个连接字符串,连接字符串包由以逗号分隔的 host:port 对的列表组成,每对 host:port对应zookeeper服务器,例如: "127.0.0.1:4545" 或 "127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002"。ZooKeeper客户端将选择一个任意服务器并尝试连接到该服务器。如果此连接失败,或者客户端由于某种原因与服务器断开连接,则客户端将自动尝试列表中的下一个服务器,直到(重新)建立连接为止。
ZooKeeper Watches
ZooKeeper中所有的读取操作--getData()、getChildren()和existence()--都有设置watch作为副作用的选项。下面是ZooKeeper对watch的定义:watch事件是一次性触发,发送到设置watch的客户端,当设置watch的数据发生变化时,就会发生。在这个watch的定义中,有三个关键点需要考虑。
- One-time trigger:当数据发生变化时,将向客户端发送一个watch事件。例如,如果客户端做了一个getData("/znode1", true),之后/znode1的数据被更改或删除,客户端将得到一个/znode1的watch事件。如果/znode1再次发生变化,除非客户端做了另一次读取,设置了新的监视,否则不会发送监视事件。
- Sent to the client:这意味着一个事件正在发往客户端的途中,变更操作的成功返回码 到达发起变更的客户端之前,此事件可能无法到达客户端,因为监视是异步发送到监视者的。ZooKeeper提供了一个排序保证:客户端在第一次看到监视事件之前,永远不会看到它已经设置了监视的变更。网络延迟或其他因素可能导致不同的客户端在不同的时间看到watch和更新的返回代码。关键的一点是,不同客户端看到的所有内容都会有一个一致的顺序。
- The data for which the watch was set:这是指节点可以更改的不同方式。这指的是一个节点可以改变的不同方式,它让我们把ZooKeeper看成是维护两个监视器列表:数据监视器和子节点监视器。getData()和exist()设置数据监视器。getChildren()设置子节点监视器。
监视器在客户端连接的 ZooKeeper 服务器上进行本地维护。这使得监视器可以轻量级地设置、维护和调度。当客户端连接到一个新的服务器,监视器将被触发任何会话事件。在与服务器断开连接时,将不会接收监视事件。当客户端重新连接时,任何先前注册的监视器将被重新注册,并在需要时被触发。一般来说,这一切都以透明方式进行。有一种情况可能造成监视器遗漏:如果在断开连接时创建并删除了一个尚未创建的节点,则会遗漏对该节点存在的监视器。
Semantics of Watches(监视器的语义)
我们可以通过读取ZooKeeper状态的三个调用来设置监视器:existence、getData和getChildren。下面的列表详细介绍了监视器可以触发的事件和启用它们的调用:
- Created event 创建事件:通过调用exists启用
- Deleted event 删除事件:通过调用exists、getData和getChildren启用。
- Changed event 修改事件:通过调用exist和getData启用。
- Child event 子事件:通过调用getChildren启用。
Persistent, Recursive Watches(持久、递归的监视器,3.6.0版本)
上述标准监视器有所不同,您可以设置触发是不会被删除的监视器。此外,这些手表会触发NodeCreated、NodeDeleted和NodeDataChanged 的事件类型,并可选择从监视器注册的znode开始,递归地触发所有znode。请注意,持久性递归监视不会触发NodeChildrenChanged事件,因为这将是多余的。
使用方法addWatch()设置持久性监视,触发语义和保证(除一次性触发外)与标准监视器相同。关于事件的唯一例外是递归的持久化监视器永远不会触发子节点改变事件,因为它们是多余的。使用watcherType.Any的watcher类型的removeWatches()来删除持久化监视器。
Remove Watches(删除监视器)
我们可以通过调用removeWatches来删除在znode上注册的监视器。此外,ZooKeeper客户端可以通过将本地标志设置为true,即使没有服务器连接,也可以在本地删除监视器。
以下列表详细说明了成功删除监视器后将触发的事件:
- Child Remove event:添加对getChildren的调用的监视程序。
- Data Remove event:添加对exists 或getData的调用的监视程序。
- Persistent Remove event:添加对 添加持久性监视器 的调用的监视程序。
What ZooKeeper Guarantees about Watches(Zookeeper对监视器的保证规则)
关于监视器,Zookeeper有以下保证规则:
- 监视器是根据对其他事件、其他监视器和异步响应进行排序的。ZooKeeper客户端库可确保按顺序分派所有内容。
- 客户端将看到它正在监视的znode的监视事件,然后才能看到与该znode对应的新数据。
- ZooKeeper中监视事件的顺序与ZooKeeper服务器看到的更新顺序相对应。
Things to Remember about Watches(关于监视器的一些须知)
- 标准监视器是一次触发。 如果您收到监视事件,并且希望收到将来的更改通知,则必须设置另一个监视器。
- 因为标准监视器是一次触发,并且在获取事件和发送新请求以获取监视器之间存在延迟,所以您无法可靠地看到ZooKeeper中节点发生的每项更改。请注意znode在获取事件和重新设置监视器之间多次更改的情况。 (您可能并不在意,但至少意识到它可能会发生。)
- 对于给定的通知,监视对象或方法/上下文对 将仅被触发一次。例如,如果为一个存在注册了相同的监视对象,并且对同一文件进行了getData调用,然后删除了该文件,则该监视对象将通过该文件的删除通知被调用一次。
- 与服务器断开连接时(例如,服务器发生故障时),只有重新建立连接后,才能获得监视。因此,会话事件将发送到所有出色的监视处理程序。使用会话事件进入安全模式:断开连接后,将不会收到事件,因此您的进程应在该模式下谨慎行事。
ZooKeeper access control using ACLs(使用ACL的ZooKeeper访问控制)
ZooKeeper使用ACL来控制对其znode(ZooKeeper数据树的数据节点)的访问。ACL实现与UNIX文件访问权限非常相似:它使用许可位来允许/禁止针对节点及其所适用范围的各种操作。与标准UNIX权限不同,ZooKeeper节点不受user(文件所有者),group 和 world(其他)的三个标准范围的限制。ZooKeeper没有znode所有者的概念,相反,ACL会指定一组ID和与这些ID相关联的权限。
还请注意,ACL仅与特定的节点有关,它不适用于子节点。例如,如果/ app仅可被ip:172.16.16.1读取,并且/app/status是world可读的,则任何人都可以读取/app/status; ACL不是递归的。
ZooKeeper支持可插拔的身份验证方案。使用scheme:expression的格式指定ID,其中scheme是ID对应的身份验证方案,有效表达式集由scheme定义。例如,ip:172.16.16.1是使用ip方案的、地址为172.16.16.1的主机的 ID;而digest:bob:password是用户使用digest方案的名字为bob的id。
当客户端连接到ZooKeeper并对进行身份验证时,ZooKeeper会将与客户端相对应的所有ID与客户端连接相关联。当客户端尝试访问节点时,将根据znodes的ACL检查这些ID。 ACL由成对的(scheme:expression,perms)(方案:表达式,权限)组成。expression的格式特定于该scheme。例如,(ip:19.22.0.0/16,READ)对IP地址以19.22开头的任何客户端提供READ权限。
网友评论