08 07 2020

Zookeeper 简介

文档地址:https://zookeeper.apache.org/doc/r3.6.2/zookeeperOver.html

Zookeeper 是一个高性能的分布式协调服务,由雅虎创建,是 Google Chubby 的开源实现。

ZooKeeper包含一个简单的原语集,提供Java和C的接口。

设计目标

  • 简单易用:ZooKeeper 封装了复杂出错的关键服务,将简单易用的接口提供给用户。
  • 高性能:Zookeeper 将数据保留在内存中,以此实现高吞吐和低延迟,适合应用于读较多的场景。
  • 高可用:Zookeeper通常以集群模式部署,集群中服务器之间相互通信,只要一半以上的服务器能正常工作,那么集群就能正常对外提供服务,官网上集群示意图如下:
    zkservice
    客户端连接到单个ZooKeeper服务器。客户端维护一个TCP连接,通过该连接发送请求,获取响应,获取监视事件并发送心跳。如果与服务器的TCP连接断开,则客户端将连接到其他服务器。
  • 顺序访问:对于客户端的每个更新请求,Zookeeper都会分配一个全局唯一的递增编号zxid(ZooKeeper Transaction Id),这个编号反应了所有事务操作的先后顺序,应用程序可以使用 ZooKeeper这个特性来实现更高层次的同步原语。

应用场景

分布式应用程序可以基于 ZooKeeper 实现诸如数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master 选举、配置维护,名字服务、分布式同步、分布式锁和分布式队列等功能。

Zookeeper 的数据结构

ZooKeeper 的数据模型和标准文件系统的类似,是一种层次化的属性结构,但与文件系统不同的是,ZooKeeper 的数据是结构化存储的,并没有在物理上体现出文件和目录。

结构如下:

ZooKeeper 树中的每一个节点被称为 Znode,Znode 维护了一个 统计信息结构(stat),其中包括了数据更改的时间和版本号等。ZooKeeper中每个znode的Stat结构由以下字段组成:

  • czxid:导致创建此znode的更改的zxid
  • mzxid:上次修改此znode的更改的zxid
  • pzxid:最后一次修改此znode子级的更改的zxid
  • ctime:创建此znode时从纪元开始的时间(以毫秒为单位)
  • mtime:自上次修改此znode以来的时间(以纪元为单位)
  • 版本:此znode的数据更改数
  • cversion:此znode的子级更改的数量
  • aversion:此znode的ACL的更改数
  • ephemeralOwner:如果znode是一个临时节点,则此znode的所有者的会话ID。如果它不是临时节点,则它将为零
  • dataLength:此znode的数据字段的长度
  • numChildren:此znode的子级数

Zookeeper 的节点

ZooKeeper 中的 Znode 在创建时需要指定节点类型,节点类型如下:

持久化节点(Persistent Nodes)

节点的数据会持久化到磁盘。

临时节点(Ephemeral Nodes)

节点的生命周期和创建该节点的客户端的生命周期保持一致,一旦该客户端的会话结束,则该客户端所创建的临时节点会被自动删除。

有序节点(Sequence Nodes)

在创建的节点后面会增加一个递增的序列,该序列在同一级父节点之下是唯一的。需要注意的是,持久化节点或者临时节点也是可以设置为有序节点的,也就是持久化有序(PERSISTENT_SEQUENTIAL)节点或者临时有序节点。

在 3.6.0 版本又增加了两种节点类型,分别是:

容器节点(Container Nodes)

容器znode是特殊用途的znode,可用于诸如领导者,锁等配方。当容器节点下的最后一个子节点被删除时,容器节点就会被自动删除。

TTL 节点(TTL Nodes)

针对持久化节点或者持久化有序节点,我们可以设置一个存活时间,如果在存活时间之内该节点没有任何修改并且没有任何子节点,它就会被自动删除。

需要注意的是,在同一层级目录下,节点的名称必须是唯一的,就像我们在同一个目录下不能创建两个相同名称的文件夹一样。

ZooKeeper中的时间

ZooKeeper以多种方式跟踪时间:

  • zxid:对ZooKeeper状态的每次更改都会以zxid(ZooKeeper交易ID)的形式接收戳记。这将向ZooKeeper公开所有更改的总顺序。每个更改将具有唯一的zxid,并且如果zxid1小于zxid2,则zxid1在zxid2之前发生。
  • 版本号:对节点的每次更改都会导致该节点的版本号之一增加。这三个版本号分别是版本(对znode的数据进行更改的次数),转换(对znode的子级进行更改的次数)和对版本(对znode的ACL进行更改的次数)。
  • tick:当使用多服务器 ZooKeeper 中,服务器使用tick来定义状态上传、会话超时、节点间连接超时等事件的时序。tick仅被最小会话超时(2倍的tick时间)间接使用:如果客户端要求小于最小会话超时的时间,服务器将告知客户端,实际使用的是最小会话超时。
  • 真实时间:ZooKeeper完全不使用实时或时钟时间,只是在创建znode和修改znode时将时间戳记入stat结构中。

Watcher 机制

ZooKeeper 提供了一种针对 Znode 的订阅/通知机制,也就是当 Znode 节点状态发生变化时或者 ZooKeeper 客户端连接状态发生变化时,会触发事件通知。

在 ZooKeeper 提供的 Java API 中,提供了三种机制来针对 Znode 进行注册监听,分别是:

  • getData():用于获取指定节点的 value 信息,并且可以注册监听,当监听的节点进行创建、修改、删除操作时,会触发相应的事件通知。
  • getChildren():用于获取指定节点的所有子节点,并且允许注册监听,当监听节点的子节点进行创建、修改、删除操作时,触发相应的事件通知。
  • exist():用于判断指定节点是否存在,同样可以注册针对节点的监听,监听的事件类型和 getData() 相同。

Watcher 事件的触发都是一次性的,比如客户端通过 getData(‘/znode1’,true) 注册监听,如果 /znode1 节点发生数据变化,那么客户端会收到一个修改事件通知,但是 /znode1 再次发生变化时,客户端无法收到事件通知,为了解决这个问题,客户端必须在收到的事件回调中再次注册事件。

延伸阅读
  1. ZooKeeper(二):linux 安装 ZooKeeper
  2. ZooKeeper (一):入门
  3. RPC 基础
发表评论