zkCli.sh使用

使用zkCli.sh与服务器建立连接的基本语法:
cd /usr/zookeeper/bin
./zkCli.sh -timeout 0 -r -server ip:port
timeout:超时时间,如果在该时间内没收到服务器的心跳信息,则认为该服务器失效了。
-r:只读模式
-server : zookeeper的ip和端口号
./zkCli.sh -timeout 5000 -server 192.168.137.132:2181
可以看到如下客户端提示:

[zk: 192.168.137.132:2181(CONNECTED) 0]

输入h:

ZooKeeper -server host:port cmd args
    stat path [watch] 获取节点的状态信息 输出如下:
        cZxid = 0x0 --事物操作的id
        ctime = Thu Jan 01 08:00:00 CST 1970
        mZxid = 0x0 --最后更新的事物id
        mtime = Thu Jan 01 08:00:00 CST 1970
        pZxid = 0x0 --子节点列表
        cversion = 0 --子节点版本号
        dataVersion = 0 -- 数据版本号
        aclVersion = 0 --权限版本号
        ephemeralOwner = 0x0 --创建该临时节点的事物ip(非临时节点为0)
        dataLength = 0 --节点存储数据的长度
        numChildren = 1 --子节点个数

    set path data [version]--修改
    ls path [watch]  --列出某个节点下的所以子节点信息
    delquota [-n|-b] path --删除配额
    ls2 path [watch] --列出当前节点的所以子节点和状态信息
    setAcl path acl --设置权限
    setquota -n|-b val path --设置配额
        -n 限制子节点个数  val -子节点个数
        -b 限制数据值的长度 val - 数据值的长度
        path 数据节点
    history  --执行指令的历史
    redo cmdno
    printwatches on|off
    delete path [version] --删除不包含子节点的节点
    rmr path [version]--删除包含子节点的节点
    sync path
    listquota path  --查看配额信息
    get path [watch] --获取数据内容和状态信息
    create [-s] [-e] path data acl 
        -s 表示顺序节点
        -e 表示临时节点 在客户端会话结束后 节点会自动删除掉  
        两个可以一起使用
    addauth scheme auth
    quit 
    getAcl path
    close  连接到其他的服务器后关闭
    connect host:port  连接到其他的服务器

可以看到zookeeper的常用命令,这些命令大致可以分为增删改查,在说明这些命令之前,可以先回顾一下zookeeper的数据结构:

Zookeeper客户端ZkClient zookeeper客户端连接ip_zookeeper

它是一个树状的结构:
以node_1为例,他的完整路径是:/node_1
大多数时候使用zookeeper,都是跟这些节点打交道。

使用JAVA API操作 zookeeper

ZK实现一个层次命名空间的数据模型,可以认为他是一个小型的、精简的文件系统。他的每个节点称之为znode,znode除了自身可以包含一些数据外,还能拥有子节点。当节点中的数据发生变化、或者子节点发生变化时,基于watcher机制,会发送相应的通知给订阅状态的客户端。
ZK依赖的jar包在下载的zk包中可以找到。
首先要实例化ZooKeeper对象,指定其中的三个参数,url为ZooKeeper服务器地址,sessionTimeout为会话超时时间。ZooKeeper的会话超时时间由客户端决定。但是ZooKeeper的server端会有两个配置,minSessionTimeout和maxSessionTimeout,前者默认为2倍的tickTime,后者默认20倍tickTime。tickTime也是服务器端的配置项。是Server内部不控制时间逻辑的最小时间单元。如果由客户端发来的sessionTimeout超过minSessionTimeout-maxSessionTimeout这个范围,Server会自动获取二者之一作为sessionTime,然后为这个Client新建一个session对象。最后一个参数为默认的watcher。
此处暂定为null;

ZooKeeper zk = new ZooKeeper("192.168.137.128:2181", 5000, null);

创建节点

通过ZooKeeper的API新增一个znode节点,节点在被创建的时候,需要指定节点的路径(此处为/root)包含字节数据(这里指定”root data”这个字符串)、访问权限(如果不想设置权限,则指定为Ids.OPEN_ACL_UNSAFE),以及创建节点的类型,关于节点的类型参见下述说明:

Zookeeper客户端ZkClient zookeeper客户端连接ip_zookeeper_02

//创建root节点,其包含的数据为“root data” ,权限访问为开放,所有人都可以访问 节点为持久化节点
        zk.create("/root", "root data".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);

删除节点

当不需要某个节点,或者某个节点上的数据已经失效,使用delete方法将节点删除。删除时需要指定节点的版本号version,如果设置-1,则匹配所有版本,ZooKeeper会比较删除节点的版本和服务器上的版本是否一致。如果不一致则会抛出异常。

zk.delete("/root", -1);

设置和获取节点的内容

如果想在已有节点中保存数据,可以通过ZooKeeper的setData方法,将数据保存到节点上。也可以通过ZooKeeper的getData方法来获取该节点保存的数据,一个znde中最多保存1MB的数据

// 设置/root的数据,版本号为-1,如果匹配不到相应的节点,则会抛出异常
zk.setData("/root", "hello".getBytes(), -1);
//取得/root节点的数据,并返回其状态。
Stat stat = new Stat();
byte[] data = zk.getData("/root", false, stat);

setData方法设置/root节点的数据为”hello”,getData方法获取root节点上保存的字节数据。false表示不使用默认的watcher。第三个参数stat,是一个传参数,将会返回节点的状态信息

添加子节点

zk支持在已有节点上添加子节点,同样也使用create方法,但是父节点必须存在,否则会抛出异常。

zk.create("/root", "root data".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
zk.create("/root/child", "child data".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);

判断节点是否存在

stat = zk.exists("/root/child", false);
if(stat==null){
    //不存在
}else{
    //存在
}

watcher实现

当节点状态发生变化时,通过watcher机制,可以让客户端得到通知,watcher主要实现org.apache.ZooKeeper.Watcher接口。节点状态变化主要如下表:

Zookeeper客户端ZkClient zookeeper客户端连接ip_客户端_03

public class MyWatcher implements Watcher {
    @Override
    public void process(WatchedEvent event) {
        if(event.getType()==EventType.NodeCreated){
            System.out.println("node created");
        }else if(event.getType()==EventType.NodeDataChanged){
            System.out.println("node data changed");
        }else if(event.getType()==EventType.NodeChildrenChanged){
            System.out.println("node children changed");
        }else if(event.getType()==EventType.NodeDeleted){
            System.out.println("node deleted");
        }
    }
}

需要注意,ZooKeeper的watcher是一次性的,也就是说,每次在处理完状态变化事件之后,需要重新注册watcher,这一点很让人抓狂。这个特性使得在处理事件和重新加载watcher这段时间发生节点状态的变化无法被感知。

还有两个细节的问题需要关注,ZooKeeper经常发生两种系统异常。

  • org.apache.ZooKeeper.KeeperException.ConnectionLossException 客户端与其中一台服务器socket连接出现异常,连接丢失。
  • org.apache.ZooKeeper.KeeperException.SessionExpiredException 客户端的session已经超过sessionTimeout,未做任何操作

ConnectionLossException可以通过重试进行处理,客户端根据初始化ZooKeeper时传递服务列表,自动尝试下一个服务端点,而这段时间内,服务端节点变更事件就会丢失
SessionExpiredException 需要应用重新创建一个新的客户端(new ZooKeeper())这时所有的watcher和EPHEMERAL都将失效