原生Java api的使用
- 节点查询
1. watch监控机制
前面我们已经了解了节点查询的相关操作,现在我们来看看它的watch监控机制。
- 我们补全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();
}
}
}
- 第一次修改节点值为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
- 第二修改节点值为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。
- 异步子节点查询
- 分别创建子节点查询回调函数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());
}
}
- 将上面main方法里的同步查询方法替换为异步查询方法后执行
// 异步调用
String ctx = "{'callback':'ChildrenCallback'}";
zkServer.getZookeeper().getChildren("/testnode", true, new ChildrenCallBack(), ctx);
zkServer.getZookeeper().getChildren("/testnode", true, new Children2CallBack(), ctx);
- 结果输出:
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开源软件~~~