Zookeeper--(六)原生Java api的使用(子节点查询,watch,ACL)

原生Java api的使用

  • 节点查询

1. watch监控机制

前面我们已经了解了节点查询的相关操作,现在我们来看看它的watch监控机制。

  1. 我们补全process方法利用CountDownLatch来使线程等待,方便我们观察,当节点值改变的时候便能监控到。
/**
 * @Date: 19-1-28
 * @version: V1.0
 * @Author: Chandler
 * @Description: ${todo}
 */
public class ZKGetNodeData implements Watcher {
    private ZooKeeper zooKeeper = null;

    public static final String zkServerPath = "127.0.0.1:2181";
    public static final Integer timeout = 5000;
    private static Stat stat = new Stat();

    public ZKGetNodeData() {
    }

    public ZKGetNodeData(String connectingString){
        try {
            zooKeeper = new ZooKeeper(connectingString,timeout,new ZKNodeOperator());
        } catch (IOException e){
            e.printStackTrace();
            if (zooKeeper != null) {
                try {
                    zooKeeper.close();
                } catch (InterruptedException e1){
                    e1.printStackTrace();
                }
            }
        }
    }
    private static CountDownLatch countDown = new CountDownLatch(1);

    public static void main(String[] args) throws KeeperException, InterruptedException {
        ZKNodeOperator zkServer = new ZKNodeOperator(zkServerPath);

        zkServer.getZookeeper().getData("/boboan", true,stat);
        countDown.await();

    }
    public ZooKeeper getZookeeper() {
        return zooKeeper;
    }
    public void setZookeeper(ZooKeeper zookeeper) {
        this.zooKeeper = zookeeper;
    }
    @Override
    public void process(WatchedEvent event) {
        try {
            if(event.getType() == Event.EventType.NodeDataChanged){
                ZKNodeOperator zkServer = new ZKNodeOperator(zkServerPath);
                byte[] resByte = zkServer.getZookeeper().getData("/boboan", true, stat);
                String result = new String(resByte);
                System.out.println("更改后的值:" + result);
                System.out.println("版本号变化dversion:" + stat.getVersion());
            } else if(event.getType() == Event.EventType.NodeCreated) {

            } else if(event.getType() == Event.EventType.NodeChildrenChanged) {

            } else if(event.getType() == Event.EventType.NodeDeleted) {

            }
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
  1. 第一次修改节点值为boboan后执行
2019-01-28 17:52:58,372 [main] [org.apache.zookeeper.ZooKeeper.<init>(ZooKeeper.java:441)] - [INFO] Initiating client connection, connectString=127.0.0.1:2181 sessionTimeout=5000 watcher=com.chandler.NativeDemo.zk.ZKNodeOperator@5eb5c224
2019-01-28 17:52:58,387 [main-SendThread(127.0.0.1:2181)] [org.apache.zookeeper.ClientCnxn$SendThread.logStartConnect(ClientCnxn.java:1035)] - [INFO] Opening socket connection to server 127.0.0.1/127.0.0.1:2181. Will not attempt to authenticate using SASL (unknown error)
2019-01-28 17:52:58,430 [main-SendThread(127.0.0.1:2181)] [org.apache.zookeeper.ClientCnxn$SendThread.primeConnection(ClientCnxn.java:877)] - [INFO] Socket connection established to 127.0.0.1/127.0.0.1:2181, initiating session
2019-01-28 17:52:58,457 [main-SendThread(127.0.0.1:2181)] [org.apache.zookeeper.ClientCnxn$SendThread.onConnected(ClientCnxn.java:1302)] - [INFO] Session establishment complete on server 127.0.0.1/127.0.0.1:2181, sessionid = 0x168922017280028, negotiated timeout = 5000
2019-01-28 17:53:07,833 [main-EventThread] [org.apache.zookeeper.ZooKeeper.<init>(ZooKeeper.java:441)] - [INFO] Initiating client connection, connectString=127.0.0.1:2181 sessionTimeout=5000 watcher=com.chandler.NativeDemo.zk.ZKNodeOperator@4703ac9e
2019-01-28 17:53:07,835 [main-SendThread(127.0.0.1:2181)] [org.apache.zookeeper.ClientCnxn$SendThread.logStartConnect(ClientCnxn.java:1035)] - [INFO] Opening socket connection to server 127.0.0.1/127.0.0.1:2181. Will not attempt to authenticate using SASL (unknown error)
2019-01-28 17:53:07,836 [main-SendThread(127.0.0.1:2181)] [org.apache.zookeeper.ClientCnxn$SendThread.primeConnection(ClientCnxn.java:877)] - [INFO] Socket connection established to 127.0.0.1/127.0.0.1:2181, initiating session
2019-01-28 17:53:07,838 [main-SendThread(127.0.0.1:2181)] [org.apache.zookeeper.ClientCnxn$SendThread.onConnected(ClientCnxn.java:1302)] - [INFO] Session establishment complete on server 127.0.0.1/127.0.0.1:2181, sessionid = 0x168922017280029, negotiated timeout = 5000
更改后的值:boboan
版本号变化dversion:1
  1. 第二修改节点值为123执行,控制台无任何变化,说明watch监控机制只能监控一次

2. 判断节点是否存在

  • 判断相关API
    public Stat exists(String path, boolean watch) throws KeeperException,InterruptedException
  • 参数:
    • path:节点路径
    • watch:watch
  • 判断节点相关操作
/**
 * @Date: 19-1-28
 * @version: V1.0
 * @Author: Chandler
 * @Description: ${todo}
 */
public class ZKNodeExist implements Watcher {
    private ZooKeeper zookeeper = null;

    public static final String zkServerPath = "127.0.0.1:2181";
    public static final Integer timeout = 5000;

    public ZKNodeExist() {}

    public ZKNodeExist(String connectString) {
        try {
            zookeeper = new ZooKeeper(connectString, timeout, new ZKNodeExist());
        } catch (IOException e) {
            e.printStackTrace();
            if (zookeeper != null) {
                try {
                    zookeeper.close();
                } catch (InterruptedException e1) {
                    e1.printStackTrace();
                }
            }
        }
    }

    private static CountDownLatch countDown = new CountDownLatch(1);

    public static void main(String[] args) throws Exception {

        ZKNodeExist zkServer = new ZKNodeExist(zkServerPath);

        Stat stat = zkServer.getZookeeper().exists("/boboan", true);
        if (stat != null) {
            System.out.println("查询的节点版本为dataVersion:" + stat.getVersion());
        } else {
            System.out.println("该节点不存在...");
        }

        countDown.await();
    }

    @Override
    public void process(WatchedEvent event) {
        if (event.getType() == Event.EventType.NodeCreated) {
            System.out.println("节点创建");
            countDown.countDown();
        } else if (event.getType() == Event.EventType.NodeDataChanged) {
            System.out.println("节点数据改变");
            countDown.countDown();
        } else if (event.getType() == Event.EventType.NodeDeleted) {
            System.out.println("节点删除");
            countDown.countDown();
        }
    }

    public ZooKeeper getZookeeper() {
        return zookeeper;
    }
    public void setZookeeper(ZooKeeper zookeeper) {
        this.zookeeper = zookeeper;
    }
}

控制台输出:

2019-01-28 18:03:50,896 [main] [org.apache.zookeeper.ZooKeeper.<init>(ZooKeeper.java:441)] - [INFO] Initiating client connection, connectString=127.0.0.1:2181 sessionTimeout=5000 watcher=com.chandler.NativeDemo.zk.ZKNodeExist@3b764bce
2019-01-28 18:03:50,910 [main-SendThread(127.0.0.1:2181)] [org.apache.zookeeper.ClientCnxn$SendThread.logStartConnect(ClientCnxn.java:1035)] - [INFO] Opening socket connection to server 127.0.0.1/127.0.0.1:2181. Will not attempt to authenticate using SASL (unknown error)
2019-01-28 18:03:50,948 [main-SendThread(127.0.0.1:2181)] [org.apache.zookeeper.ClientCnxn$SendThread.primeConnection(ClientCnxn.java:877)] - [INFO] Socket connection established to 127.0.0.1/127.0.0.1:2181, initiating session
2019-01-28 18:03:50,971 [main-SendThread(127.0.0.1:2181)] [org.apache.zookeeper.ClientCnxn$SendThread.onConnected(ClientCnxn.java:1302)] - [INFO] Session establishment complete on server 127.0.0.1/127.0.0.1:2181, sessionid = 0x16892201728002d, negotiated timeout = 5000
查询的节点版本为dataVersion:1

同时通过终端进行创建修改删除都能watch都能监控到相关事件输出日志。

3. 获取子节点数据

  • 获取子节点API
//同步
public List<String> getChildren(String path, boolean watch)
            throws KeeperException, InterruptedException
public List<String> getChildren(String path, boolean watch, Stat stat)
            throws KeeperException, InterruptedException
//异步
public void getChildren(final String path, Watcher watcher,
            ChildrenCallback cb, Object ctx)
public void getChildren(final String path, Watcher watcher,
            Children2Callback cb, Object ctx)
  • 参数:
    • path:节点路径
    • watch:自定义watcher/true或者false,注册一个watch事件
    • stat:特定节点的数据和统计信息
    • ChildrenCallback:用于检索节点的子节点的回调函数
    • Children2Callback:用于检索节点的子节点的回调函数
  • 同步获取子节点
/**
 * @Date: 19-1-29
 * @version: V1.0
 * @Author: Chandler
 * @Description: ${todo}
 */
public class ZKGetChildrenList implements Watcher {
    private ZooKeeper zookeeper = null;

    public static final String zkServerPath = "127.0.0.1:2181";
    public static final Integer timeout = 5000;

    public ZKGetChildrenList() {
    }

    public ZKGetChildrenList(String connectString) {
        try {
            zookeeper = new ZooKeeper(connectString, timeout, new ZKGetChildrenList());
        } catch (IOException e) {
            e.printStackTrace();
            if (zookeeper != null) {
                try {
                    zookeeper.close();
                } catch (InterruptedException e1) {
                    e1.printStackTrace();
                }
            }
        }
    }

    private static CountDownLatch countDown = new CountDownLatch(1);

    public static void main(String[] args) throws Exception {

        ZKGetChildrenList zkServer = new ZKGetChildrenList(zkServerPath);
        
		List<String> strChildList = zkServer.getZookeeper().getChildren("/testnode", true);
		for (String s : strChildList) {
			System.out.println(s);
		}
        countDown.await();
    }

    @Override
    public void process(WatchedEvent event) {
        try {
            if (event.getType() == Event.EventType.NodeChildrenChanged) {
                System.out.println("NodeChildrenChanged");
                ZKGetChildrenList zkServer = new ZKGetChildrenList(zkServerPath);
                List<String> strChildList = zkServer.getZookeeper().getChildren(event.getPath(), false);
                for (String s : strChildList) {
                    System.out.println(s);
                }
                countDown.countDown();
            } else if (event.getType() == Event.EventType.NodeCreated) {
                System.out.println("NodeCreated");
            } else if (event.getType() == Event.EventType.NodeDataChanged) {
                System.out.println("NodeDataChanged");
            } else if (event.getType() == Event.EventType.NodeDeleted) {
                System.out.println("NodeDeleted");
            }
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public ZooKeeper getZookeeper() {
        return zookeeper;
    }

    public void setZookeeper(ZooKeeper zookeeper) {
        this.zookeeper = zookeeper;
    }
}
  • 输出结果:
    终端查询结果:
[zk: localhost:2181(CONNECTED) 17] ls /testnode
[grandsonnode, sonnode]

控制台输出:

grandsonnode
sonnode

但是我们sonnode下面还有子节点,却查询不出来,必须要把结点路径修改为/testnode/sonnode才能查询出sonnode的子节点grandnode。

  • 异步子节点查询
  1. 分别创建子节点查询回调函数ChildrenCallBack和Children2CallBack
public class ChildrenCallBack implements AsyncCallback.ChildrenCallback {
    @Override
    public void processResult(int rc, String path, Object ctx, List<String> children) {
        for (String s : children) {
            System.out.println(s);
        }
        System.out.println("ChildrenCallback:" + path);
        System.out.println((String)ctx);
    }
}

public class Children2CallBack implements AsyncCallback.Children2Callback {

    @Override
    public void processResult(int rc, String path, Object ctx, List<String> children, Stat stat) {
        for (String s : children) {
            System.out.println(s);
        }
        System.out.println("ChildrenCallback:" + path);
        System.out.println((String)ctx);
        System.out.println(stat.toString());
    }
}
  1. 将上面main方法里的同步查询方法替换为异步查询方法后执行
// 异步调用
String ctx = "{'callback':'ChildrenCallback'}";
zkServer.getZookeeper().getChildren("/testnode", true, new ChildrenCallBack(), ctx);
zkServer.getZookeeper().getChildren("/testnode", true, new Children2CallBack(), ctx);
  1. 结果输出:
    Children2CallBack和接口比ChildrenCallBack的接口多了个stat参数。
grandsonnode
sonnode
ChildrenCallback:/testnode
{'callback':'ChildrenCallback'}
grandsonnode
sonnode
ChildrenCallback:/testnode
{'callback':'ChildrenCallback'}
73,130,1548654392089,1548668480828,8,2,0,0,12,2,172

4. 操作节点ACL

我们先来重温一下基本概念

  • zk的acl通过[scheme][ip][ermissions]来构成权限列表
    • scheme:代表采用的某种权限机制
    • id:代表允许访问的用户
    • permissions:权限组合字符串
  • scheme
    • world:world下只有一个id,即一个用户,也就是任何人,world:anyone:[permissions]
    • auth:代表认证登录,需要注册用户有权限就可以,auth:user:password:[permissions]
    • :需要对密码加密才能访问,digest:username:BASE64(SHA1(password)):[permissions]
    • auth是明文,digest是密文
  • ip:设置为ip指定的ip地址,此时限制ip进行访问,ip:192.168.1.1:[permissions]
  • super:代表超级管理员,拥有所有权限
  • permissions(字符串缩写crdwa)
    • create:创建子节点
    • read:获取节点/子节点
    • write:设置节点数据
    • delete:删除子节点
    • admin:设置权限
    • auth读取会以第一次的值为准

自定义用户认证访问

public class ZKNodeAcl implements Watcher {
    private ZooKeeper zookeeper = null;

    public static final String zkServerPath = "127.0.0.1:2181";
    public static final Integer timeout = 5000;

    public ZKNodeAcl() {}

    public ZKNodeAcl(String connectString) {
        try {
            zookeeper = new ZooKeeper(connectString, timeout, new ZKNodeAcl());
        } catch (IOException e) {
            e.printStackTrace();
            if (zookeeper != null) {
                try {
                    zookeeper.close();
                } catch (InterruptedException e1) {
                    e1.printStackTrace();
                }
            }
        }
    }

    public void createZKNode(String path, byte[] data, List<ACL> acls) {

        String result = "";
        try {
            result = zookeeper.create(path, data, acls, CreateMode.PERSISTENT);
            System.out.println("创建节点:\t" + result + "\t成功...");
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws Exception {

        ZKNodeAcl zkServer = new ZKNodeAcl(zkServerPath);
        /**
         * ======================  创建node start  ======================
         */
        // 自定义用户认证访问
		List<ACL> acls = new ArrayList<ACL>();
		Id boboan1 = new Id("digest", AclUtils.getDigestUserPwd("boboan1:123456"));
		Id boboan2 = new Id("digest", AclUtils.getDigestUserPwd("boboan2:123456"));
		acls.add(new ACL(ZooDefs.Perms.ALL, boboan1));
		acls.add(new ACL(ZooDefs.Perms.READ, boboan2));
		acls.add(new ACL(ZooDefs.Perms.DELETE | ZooDefs.Perms.CREATE, boboan2));
		zkServer.createZKNode("/aclboboan/testdigest", "testdigest".getBytes(), acls);

    }

    public ZooKeeper getZookeeper() {
        return zookeeper;
    }
    public void setZookeeper(ZooKeeper zookeeper) {
        this.zookeeper = zookeeper;
    }

    @Override
    public void process(WatchedEvent event) {

    }
}

在创建节点的时候,我们把自定义的acl权限组给设置进去。

然后我们在终端进行验证:

[zk: localhost:2181(CONNECTED) 2] ls /aclboboan
[testdigest]

上面可以看到节点已经创建出来了,继续查看发现没有权限

[zk: localhost:2181(CONNECTED) 3] ls /aclboboan/testdigest     
Authentication is not valid : /aclboboan/testdigest

通过getAcl查看节点权限就是我们之前自定义的

[zk: localhost:2181(CONNECTED) 4] getAcl /aclboboan/testdigest  
'digest,'boboan1:kz2PchXgqmQ1LkTXoe4grQ5Af3o=
: cdrwa
'digest,'boboan2:ZQCbAk/+Fb6ewzXEsW27e1kfbe0=
: r
'digest,'boboan2:ZQCbAk/+Fb6ewzXEsW27e1kfbe0=
: cd

然后我们这边进行节点认证后在进行访问,发现已经ok,说明我们的自定义用户认证访问成功~

[zk: localhost:2181(CONNECTED) 5] addauth digest boboan1:123456 
[zk: localhost:2181(CONNECTED) 6] get /aclboboan/testdigest   
testdigest
cZxid = 0xcb
ctime = Tue Jan 29 11:08:36 CST 2019
mZxid = 0xcb
mtime = Tue Jan 29 11:08:36 CST 2019
pZxid = 0xcb
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 10
numChildren = 0
  • 注册过的用户必须通过addAuthInfo才能操作节点
    如果我们之前已经定义了一些自定义用户认证访问,那么我们在操作的时候必须先通过addAuthInfo才能访问,否则会抛出异常:
org.apache.zookeeper.KeeperException$NoAuthException: KeeperErrorCode = NoAuth for /aclboboan/testdigest/childtest
	at org.apache.zookeeper.KeeperException.create(KeeperException.java:116)
	at org.apache.zookeeper.KeeperException.create(KeeperException.java:54)
	at org.apache.zookeeper.ZooKeeper.create(ZooKeeper.java:786)
	at com.chandler.NativeDemo.zk.ZKNodeAcl.createZKNode(ZKNodeAcl.java:60)
	at com.chandler.NativeDemo.zk.ZKNodeAcl.main(ZKNodeAcl.java:89)
Exception in thread "main" org.apache.zookeeper.KeeperException$NoAuthException: KeeperErrorCode = NoAuth for /aclboboan/testdigest
	at org.apache.zookeeper.KeeperException.create(KeeperException.java:116)
	at org.apache.zookeeper.KeeperException.create(KeeperException.java:54)
	at org.apache.zookeeper.ZooKeeper.getData(ZooKeeper.java:1215)
	at org.apache.zookeeper.ZooKeeper.getData(ZooKeeper.java:1244)
	at com.chandler.NativeDemo.zk.ZKNodeAcl.main(ZKNodeAcl.java:91)

我们重新修改的类如下:

public class ZKNodeAcl implements Watcher {
    private ZooKeeper zookeeper = null;

    public static final String zkServerPath = "127.0.0.1:2181";
    public static final Integer timeout = 5000;

    public ZKNodeAcl() {}

    public ZKNodeAcl(String connectString) {
        try {
            zookeeper = new ZooKeeper(connectString, timeout, new ZKNodeAcl());
        } catch (IOException e) {
            e.printStackTrace();
            if (zookeeper != null) {
                try {
                    zookeeper.close();
                } catch (InterruptedException e1) {
                    e1.printStackTrace();
                }
            }
        }
    }

    public void createZKNode(String path, byte[] data, List<ACL> acls) {

        String result = "";
        try {
            result = zookeeper.create(path, data, acls, CreateMode.PERSISTENT);
            System.out.println("创建节点:\t" + result + "\t成功...");
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws Exception {

        ZKNodeAcl zkServer = new ZKNodeAcl(zkServerPath);
        /**
         * ======================  创建node start  ======================
         */
        // 注册过的用户必须通过addAuthInfo才能操作节点
		zkServer.getZookeeper().addAuthInfo("digest", "boboan1:123456".getBytes());
		zkServer.createZKNode("/aclboboan/testdigest/childtest", "childtest".getBytes(), ZooDefs.Ids.CREATOR_ALL_ACL);
		Stat stat = new Stat();
		byte[] data = zkServer.getZookeeper().getData("/aclboboan/testdigest", false, stat);
		System.out.println(new String(data));
		zkServer.getZookeeper().setData("/aclboboan/testdigest", "now".getBytes(), 1);
    }

    public ZooKeeper getZookeeper() {
        return zookeeper;
    }
    public void setZookeeper(ZooKeeper zookeeper) {
        this.zookeeper = zookeeper;
    }

    @Override
    public void process(WatchedEvent event) {

    }
}

最终的终端查询结果和我们代码中修改的一致:

[zk: localhost:2181(CONNECTED) 27] get /aclboboan/testdigest 
now
cZxid = 0xcb
ctime = Tue Jan 29 11:08:36 CST 2019
mZxid = 0xec
mtime = Tue Jan 29 13:31:27 CST 2019
pZxid = 0xeb
cversion = 3
dataVersion = 2
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 3
numChildren = 1
[zk: localhost:2181(CONNECTED) 28] get /aclboboan/testdigest/childtest
childtest
cZxid = 0xeb
ctime = Tue Jan 29 13:31:26 CST 2019
mZxid = 0xeb
mtime = Tue Jan 29 13:31:26 CST 2019
pZxid = 0xeb
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 9
numChildren = 0

ip方式的acl以及验证ip是否有权限

后面这2种方式只需要在上面ZKNodeAcl类的main方法中替换执行就能看到效果

  • ip方式的acl
    这个ip字段之外的地址都无法访问创建的该节点
List<ACL> aclsIP = new ArrayList<ACL>();
Id ipId1 = new Id("ip", "127.0.0.1");
aclsIP.add(new ACL(ZooDefs.Perms.ALL, ipId1));
zkServer.createZKNode("/aclboboan/iptest6", "iptest".getBytes(), aclsIP);
  • 验证ip是否有权限
    通过该方式我们可以判断当前ip是否能够访问一些节点
zkServer.getZookeeper().setData("/aclboboan/iptest6", "now".getBytes(), 1);
Stat stat = new Stat();
byte[] data = zkServer.getZookeeper().getData("/aclboboan/iptest6", false, stat);

5. 总结

通过上面的学习,我们可以利用自定义的acl权限来进行不同用户的限制,固定ip的访问等等。

也发现了原生API的一些不足,获取子节点的时候不能递归获取,只能获取当前节点的字节。

它的一些缺点导致使用的局限性,我们后面要继续学习更加完善的一些zk开源软件~~~

猜你喜欢

转载自blog.csdn.net/ouzhuangzhuang/article/details/86688962