ZooKeeper 概述

ZooKeeper 是一种为分布式应用所设计的高可用、高性能且一致的开源协调服务,它提供了一项基本服务:分布式锁服务。由于 ZooKeeper 的开源特性,后来我们的开发者在分布式锁的基础上,摸索了出了其他的使用方法:配置维护、组服务、分布式消息队列分布式通知/协调等。

ZooKeeper 数据模型

ZooKeeper的数据模型,在结构上和标准文件系统的非常相似,都是采用这种树形层次结构,ZooKeeper树中的每个节点被称为—Znode。和文件系统的目录树一样,ZooKeeper树中的每个节点可以拥有子节点。

1559107255964.png

Znode结构

ZooKeeper 命名空间中的 Znode,兼具文件和目录两种特点。既像文件一样维护着数据、元信息、ACL(访问控制列表)、时间戳等数据结构,又像目录一样可以作为路径标识的一部分。图中的每个节点称为一个 Znode。 每个 Znode 由3部分组成:

  • stat:此为状态信息, 描述该Znode的版本, 权限等信息
  • data:与该 Znode 关联的数据,保存数据不大于 1M。
  • children:该 Znode 下的子节点

每个 zk 节点都是有各自的版本号,每当节点中的数据发生变化,版本号就会累加(乐观锁)。删除/修改过时节点,版本号不匹配则会报错。

节点类型
  1. 临时节点:该节点的生命周期依赖于创建它们的会话。一旦会话(Session)结束,临时节点将被自动删除,当然可以也可以手动删除。虽然每个临时的 Znode 都会绑定到一个客户端会话,但他们对所有的客户端还是可见的。另外,ZooKeeper 的临时节点不允许拥有子节点。
  2. 永久节点:该节点的生命周期不依赖于会话,并且只有在客户端显示执行删除操作的时候,他们才能被删除。
观察

客户端可以在节点上设置 watch,我们称之为监视器。当节点状态发生改变时( Znode 的增、删、改)将会触发 watch 所对应的操作。当 watch 被触发时,ZooKeeper 将会向客户端发送且仅发送一条通知,因为 watch 只能被触发一次,这样可以减少网络流量。

ZooKeeper服务中操作

在ZooKeeper中有9个基本操作,如下图所示:

301534572468352.png

更新 ZooKeeper 操作是有限制的。delete 或 setData 必须明确要更新的 Znode 的版本号,我们可以调用 exists 找到。如果版本号不匹配,更新将会失败。

更新 ZooKeeper 操作是非阻塞式的。因此客户端如果失去了一个更新(由于另一个进程在同时更新这个 Znode,他可以在不阻塞其他进程执行的情况下,选择重新尝试或进行其他操作。

Zookeeper Watch机制

ZooKeeper可以为所有的读操作设置watch,这些读操作包括:exists()、getChildren() 及 getData()。watch 事件是一次性的触发器,当watch的对象状态发生改变时,将会触发此对象上watch所对应的事件。watch 事件将被异步地发送给客户端,并且 ZooKeeper 为 watch 机制提供了有序的一致性保证。理论上,客户端接收 watch 事件的时间要快于其看到 watch 对象状态变化的时间。

下面是对如何设置监控点的总结

  • 对于 NodeCreated 通过 exists API 设置
  • 对于 NodeDeleted 通过 exists 和 getData() 设置
  • 对于 NodeDataChanged 通过 exists 或 getData 设置
  • 对于 NodeChildrenChanged 通过 getChildren 设置

实现 Watcher 接口

 public void process(WatchedEvent event) {
        log.warn("接受到watch通知:{}", event);
        Event.EventType type = event.getType();
        switch (type) {
            case NodeCreated:
                System.out.println("新建节点:" + event.getPath());
            case NodeDeleted:
                System.out.println("删除节点:" + event.getPath());
            case NodeDataChanged:
                System.out.println("修改节点:" + event.getPath());
            case NodeChildrenChanged:
                System.out.println("子节点:" + event);
            case None:
                System.out.println("none");
        }

    }
上次更新时间: 2024/5/7 05:59:02