zookeeper入门实例

zookeeper概述

Zookeeper 是为分布式应用程序提供高性能协调服务的工具集合,也是Google的Chubby一个开源的实现,是Hadoop 的分布式协调服务。它包含一个简单的原语集5,分布式应用程序可以基于它实现配置维护、命名服务、分布式同步、组服务等。Zookeeper可以用来保证数据在ZK集群之间的数据的事务性一致6。其中ZooKeeper提供通用的分布式锁服务7,用以协调分布式应用。
Zookeeper作为Hadoop项目中的一个子项目,是 Hadoop集群管理的一个必不可少的模块,它主要用来解决分布式应用中经常遇到的数据管理问题,如集群管理、统一命名服务、分布式配置管理、分布式消息队列、分布式锁、分布式协调等。在Hadoop中,它管理Hadoop集群中的NameNode,还有在Hbase中Master Election、Server 之间状态同状步等。
  Zoopkeeper 提供了一套很好的分布式集群管理的机制,就是它这种基于层次型的目录树的数据结构,并对树中的节点进行有效管理,从而可以设计出多种多样的分布式的数据管理模型。

zookeeper数据结构

ZooKeeper拥有一个层次的命名空间,这个和分布式的文件系统非常相似。不同的是ZooKeeper命名空间中的Znode,兼具文件和目录两种特点。既像文件一样维护着数据、元信息、ACL、时间戳等数据结构,又像目录一样可以作为路径标识的一部分,并可以具有子znode。用户对znode具有增、删、改、查等操作(权限允许的情况下)。
znode具有原子性操作,每个znode的数据将被原子性地读写,读操作会读取与znode相关的所有数据,写操作会一次性替换所有数据。zookeeper并没有被设计为常规的数据库或者大数据存储,相反的是,它用来管理调度数据,比如分布式应用中的配置文件信息、状态信息、汇集位置等等。
Zonde由路径标注,ZooKeeper中被表示成有反斜杠分割的Unicode字符串,如同Unix中的文件路径。路径必须是绝对的,因此他们必须由反斜杠来字符开头。除此以外,他们必须是唯一的,也就是说每一个路径只有一个表示,因此这些路径不能改变。

ZooKeeper节点Znode的组成部分

1.stat:此为状态信息, 描述该znode的版本, 权限等信息.

2.data:与该znode关联的数据.

3.children:该znode下的子节点.

znode的主要特征

1. Watches

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

2. 数据访问

ZooKeeper中的每个节点存储的数据要被原子性的操作。也就是说读操作将获取与节点相关的所有数据,写操作也将替换掉节点的所有数据。另外,每一个节点都拥有自己的ACL(访问控制列表),这个列表规定了用户的权限,即限定了特定用户对目标节点可以执行的操作。

3. 节点类型

ZooKeeper中的节点有两种,分别为临时节点和永久节点。节点的类型在创建时即被确定,并且不能改变。
ZooKeeper的临时节点:该节点的生命周期依赖于创建它们的会话。一旦会话结束,临时节点将被自动删除,当然可以也可以手动删除。另外,需要注意是,ZooKeeper的临时节点不允许拥有子节点。
ZooKeeper的永久节点:该节点的生命周期不依赖于会话,并且只有在客户端显示执行删除操作的时候,他们才能被删除。

4. 顺序节点(唯一性的保证)

当创建Znode的时候,用户可以请求在ZooKeeper的路径结尾添加一个递增的计数。这个计数对于此节点的父节点来说是唯一的,它的格式为“%10d”(10位数字,没有数值的数位用0补充,例如“0000000001”)。当计数值大于232-1时,计数器将溢出。

org.apache.zookeeper.CreateMode中定义了四种节点类型,分别对应:

PERSISTENT:永久节点

EPHEMERAL:临时节点

PERSISTENT_SEQUENTIAL:永久节点、序列化

EPHEMERAL_SEQUENTIAL:临时节点、序列化

  • znode中的数据可以有多个版本,在查询该znode数据时就需要带上版本信息。如:set path version / delete path version
  • znode可以是临时znode,由create -e 生成的节点,一旦创建这个znode的client与server断开连接,该znode将被自动删除。
    client和server之间通过heartbeat来确认连接正常,这种状态称之为session,断开连接后session失效。
  • 临时znode不能有子znode。
  • znode可以自动编号,由create -s 生成的节点,例如在 create -s /app/node 已存在时,将会生成 /app/node00***001节点。
  • znode可以被监控,该目录下某些信息的修改,例如节点数据、子节点变化等,可以主动通知监控注册的client。事实上,通过这个特性,可以完成许多重要应用,例如配置管理、信息同步、分布式锁等等。

zookeeper服务中的操作

  • create 创建Znode(父Znode必须存在)
  • delete 删除Znode(Znode没有子节点)
  • exits 测试Znode是否存在,并获取该节点的元数据
  • getACL/setACL 为Znode获取/设置ACL
  • getChildren 获取Znode所有子节点的列表
  • getData/setDate 获取/设置Znode的相关数据
  • sync 使客户端的Znode视图与Zookeeper同步

更新ZooKeeper操作是有限制的。delete或setData必须明确要更新的Znode的版本号,我们可以调用exists找到。如果版本号不匹配,更新将会失败。
更新ZooKeeper操作是非阻塞式的。因此客户端如果失去了一个更新(由于另一个进程在同时更新这个Znode),他可以在不阻塞其他进程执行的情况下,选择重新尝试或进行其他操作。

接下来我们完成一个小例子

新建工程

导入依赖包

这里写图片描述

测试类

package main;

import java.util.List;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;

public class ZkTest {

    private static final String path = "/testRootPath";
    private static final String oneChildPath = "/testRootPath/testChildPathOne";
    private static final String twoChildPath = "/testRootPath/testChildPathTwo";

    public static void main(String[] args) throws Exception {
        ZooKeeper zooKeeper = new ZooKeeper("192.168.6.175:12181", 3000, new Watcher() {

            @Override
            public void process(WatchedEvent arg0) {
                System.out.println("已经触发了" + arg0.getType() + "事件!");
            }
        });
        // 创建一个持久化目录节点==>已经触发了 None 事件!
        zooKeeper.create(path, "testRootData".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        // 创建一个持久化子目录节点
        zooKeeper.create(oneChildPath, "testChildDataOne".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);

        // 此处watch=false 不触发事件
        System.out.println(new String(zooKeeper.getData(path, false, null)));

        // 取出子目录节点列表 ==>[testChildPathOne] 在节点/testRootPath的getChildren上设置观察
        System.out.println(zooKeeper.getChildren(path, true));

        // 修改子目录节点数据 由于上面的修改数据不触发观察 这边不执行事件
        zooKeeper.setData(oneChildPath, "modifyChildDataOne".getBytes(), -1);

        // 目录节点状态
        System.out.println("目录节点状态:" + zooKeeper.exists(path, true));

        // 创建另外一个子目录节点 ==>已经触发了 NodeChildrenChanged 事件!
        zooKeeper.create(twoChildPath, "testChildDataTwo".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);

        // testChildDataTwo
        System.out.println(new String(zooKeeper.getData(twoChildPath, true, null)));

        // 取出子目录节点列表 ==>[testChildPathTwo] 在节点/testRootPath的getChildren上设置观察
        System.out.println(zooKeeper.getChildren(path, true));

        // 修改子目录节点数据
        zooKeeper.setData(oneChildPath, "modifyChildDataTwo".getBytes(), -1);

        // 目录节点状态
        System.out.println("目录节点状态:" + zooKeeper.exists(path, true));
        delPath(path, zooKeeper);
        // 删除父目录
        zooKeeper.delete(path, -1);
        // 关闭连接
        zooKeeper.close();
    }

    // 删除父目录下所有节点
    private static void delPath(String path, ZooKeeper zk) throws Exception {
        System.out.println(path);
        List<String> paths = zk.getChildren(path, false);
        if (paths.size() > 0) {
            for (String p : paths) {
                delPath(path + "/" + p, zk);
            }
            for (String p : paths) {
                zk.delete(path + "/" + p, -1);
                System.out.println("节点"+path + "/" + p+"已删除");
            }
        } 

    }

}

运行结果

这里写图片描述

猜你喜欢

转载自blog.csdn.net/sinat_23324343/article/details/79000668
今日推荐