前言
GitHub:https://github.com/yihonglei/ZooKeeper-Study
本文采用zk原生客户端方式对zk进行操作,对应github的zk-native项目。
maven引入jar包:
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.14</version>
</dependency>
一 znode
1、 znode概述
ZooKeeper操作和维护的一个个数据称为znode,采用类似文件系统的层级树状结构进行管理。
2、znode四种类型
持久无序(PERSISTENT):节点创建后,如果不手动删除,节点一直存在,节点是无序的。
持久有序(PERSISTENT_SEQUENTIAL):持久,有序。
临时无序(EPHEMERAL):客户端session失效就会自动删除节点,节点无序。
临时有序(EPHEMERAL_SEQUENTIAL):临时,有序,
在创建节点时,需要指定节点类型。
3、znode节点数据
ZooKeeper的Stat对象中记录了节点数据,主要包括数据信息,版本,权限。
4、zkCli创建一个节点,查看数据
[zk: localhost:2181(CONNECTED) 33] create /lanhuigu 2019
Created /lanhuigu
[zk: localhost:2181(CONNECTED) 34] ls /lanhuigu
[]
[zk: localhost:2181(CONNECTED) 35] get /lanhuigu
2019
cZxid = 0x80000003b
ctime = Thu May 16 18:31:50 CST 2019
mZxid = 0x80000003b
mtime = Thu May 16 18:31:50 CST 2019
pZxid = 0x80000003b
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 4
numChildren = 0
5、Stat数据结构说明
状态属性 |
说明 |
czxid |
节点创建时的zxid |
mzxid |
节点最新一次更新发生时的zxid |
ctime |
节点创建时的时间戳 |
mtime |
节点最新一次更新发生时的时间戳 |
dataVersion |
节点数据的更新次数 |
cversion |
其子节点的更新次数 |
aclVersion |
节点ACL(授权信息)的更新次数 |
ephemeralOwner |
如果该节点为ephemeral节点, ephemeralOwner值表示与该节点绑定的session id。 如果该节点不是ephemeral节点,ephemeralOwner值为0。 |
dataLength |
节点数据的字节数 |
numChildren |
子节点个数 |
6、znode实例
ZooKeeper原生客户端对znode进行crud操作。
package com.lanhuigu.zookeeper.znode;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import java.io.IOException;
/**
* zookeeper使用原生方式连接,进行crud操作
*
* @auther: yihonglei
* @date: 2019-05-11 21:43
*/
public class ZooKeeperCrud {
private String connectString = "127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183";
private ZooKeeper zk;
public ZooKeeperCrud() {
try {
zk = new ZooKeeper(connectString, 5000, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
System.out.println("监听器....");
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 创建持久无序节点
*/
public String createPersistent(String path, String data) throws KeeperException, InterruptedException {
return zk.create(path, data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
/**
* 创建临时无序节点
*/
public String createEphemeral(String path, String data) throws KeeperException, InterruptedException {
return zk.create(path, data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
}
/**
* 获取信息
*/
public String getData(String path) throws KeeperException, InterruptedException {
byte[] data = zk.getData(path, false, null);
data = (data == null) ? "null".getBytes() : data;
return new String(data);
}
/**
* 更新信息
*/
public Stat setData(String path, String data) throws KeeperException, InterruptedException {
return zk.setData(path, data.getBytes(), -1);
}
/***
* 判断节点是否存在
*/
public Stat exists(String path) throws KeeperException, InterruptedException {
return zk.exists(path, false);
}
/***
* 删除节点
*/
public void delete(String path) throws KeeperException, InterruptedException {
zk.delete(path, -1);
}
/***
* 递归删除节点
*/
public void deleteRecursive(String path) throws KeeperException, InterruptedException {
ZKUtil.deleteRecursive(zk, path);
}
/**
* 测试代码
*/
public static void main(String[] args) throws KeeperException, InterruptedException {
ZooKeeperCrud crud = new ZooKeeperCrud();
if (null != crud.exists("/lanhuigu")) {
crud.delete("/lanhuigu");
// 如果节点下还有节点数据,需要递归删除
// crud.deleteRecursive("/lanhuigu");
}
crud.createPersistent("/lanhuigu", "2019");
System.out.println(crud.getData("/lanhuigu"));
}
}
7、代码说明
1)判断节点是否存在,如果存在,则删除掉;
2)创建一个持久节点/lanhuigu,节点数据为2019;
3)获取节点数据;
二 watcher
Watcher是ZooKeeper的事件监听器,ZooKeeper允许用户注册事件,当事件触发时服务端会通知到客户端,
客户端可以做相应的处理,该机制是Zookeeper实现分布式协调服务的重要特性。
KeeperState |
EventType |
触发条件 |
说明 |
操作 |
SyncConnected |
None |
客户端与服务端成功建立连接 |
此时客户端和服务器处于连接状态 |
|
NodeCreated(1) |
Watcher监听的对应数据节点被创建 |
Create |
||
NodeDeleted |
Watcher监听的对应数据节点被删除 |
Delete/znode |
||
NodeDataChanged |
Watcher监听的对应数据节点的数据内容发生变更 |
setDate/znode |
||
NodeChildChanged |
Wather监听的对应数据节点的子节点列表发生变更 |
Create/child |
||
Disconnected |
None |
客户端与ZooKeeper服务器断开连接 |
此时客户端和服务器处于断开连接状态 |
|
Expired |
None |
会话超时 |
此时客户端会话失效,通常同时也会受到SessionExpiredException异常 |
|
AuthFailed |
None |
通常有两种情况,1:使用错误的schema进行权限检查 2:SASL权限检查失败 |
通常同时也会收到AuthFailedException异常 |
1、watche实例
package com.lanhuigu.zookeeper.watcher;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import java.io.IOException;
/**
* zookeeper使用原生方式连接,watcher测试
*
* @auther: yihonglei
* @date: 2019-05-11 21:43
*/
public class ZooKeeperWatcher implements Watcher {
private String connectString = "127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183";
private ZooKeeper zk;
public ZooKeeperWatcher() {
try {
zk = new ZooKeeper(connectString, 5000, this);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 创建持久无序节点
*/
public String createPersistent(String path, String data) throws KeeperException, InterruptedException {
return zk.create(path, data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
/**
* 创建临时无序节点
*/
public String createEphemeral(String path, String data) throws KeeperException, InterruptedException {
return zk.create(path, data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
}
/**
* 获取信息
*/
public String getData(String path, boolean watcher) throws KeeperException, InterruptedException {
byte[] data = zk.getData(path, watcher, null);
data = (data == null) ? "null".getBytes() : data;
return new String(data);
}
/**
* 更新信息
*/
public Stat setData(String path, String data) throws KeeperException, InterruptedException {
return zk.setData(path, data.getBytes(), -1);
}
/***
* 判断节点是否存在
*/
public Stat exists(String path, boolean watcher) throws KeeperException, InterruptedException {
return zk.exists(path, watcher);
}
/***
* 删除节点
*/
public void delete(String path) throws KeeperException, InterruptedException {
zk.delete(path, -1);
}
/***
* 递归删除节点
*/
public void deleteRecursive(String path) throws KeeperException, InterruptedException {
ZKUtil.deleteRecursive(zk, path);
}
/**
* Watcher处理
*/
@Override
public void process(WatchedEvent event) {
// 连接状态
Event.KeeperState keeperState = event.getState();
// 事件类型
Event.EventType eventType = event.getType();
// 受影响的path
String path = event.getPath();
System.out.println("连接状态:" + keeperState + ",事件类型:" + eventType + ",受影响的path:" + path);
try {
if (null != this.exists("/lanhuigu", true)) {
System.out.println("内容:" + this.getData("/lanhuigu", true));
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("==================");
}
/**
* 测试代码
*/
public static void main(String[] args) throws KeeperException, InterruptedException {
ZooKeeperWatcher crud = new ZooKeeperWatcher();
// 判断节点是否存在,存在则删除
if (null != crud.exists("/lanhuigu", true)) {
Thread.sleep(1000);
crud.delete("/lanhuigu");
// 注意:如果节点下还有节点数据,需要递归删除
// crud.deleteRecursive("/lanhuigu");
}
// 创建持久无序节点
crud.createPersistent("/lanhuigu", "2019");
// 主线程休眠10秒,让子线程都执行完
Thread.sleep(1000 * 1000);
}
}
2、代码说明
1)ZooKeeperWatcher实现watcher接口,并实现process方法;
2)构造器创建ZooKeeper时注册监听;
3)当节点改变时,触发监听;
三 ACL
1、ACL(Access Control List)
内置的 ACL schemes:
world:默认方式,相当于全世界都能访问。
auth:代表已经认证通过的用户(cli中可以通过addauth digest user:pwd 来添加当前上下文中的授权用户)。
digest:即用户名:密码这种方式认证,这也是业务系统中最常用的。
ip:使用Ip地址认证。
2、ACL支持权限
CREATE: 能创建子节点。
READ:能获取节点数据和列出其子节点。
WRITE: 能设置节点数据。
DELETE: 能删除子节点。
ADMIN: 能设置权限。
3、查看节点所属权限
[zk: localhost:2181(CONNECTED) 13] getAcl /zookeeper
'world,'anyone
: cdrwa
4、ACL实例
package com.lanhuigu.zookeeper.acl;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import java.io.IOException;
/**
* zookeeper使用原生方式连接,进行acl测试
*
* @auther: yihonglei
* @date: 2019-05-11 21:43
*/
public class ZooKeeperAcl {
private String connectString = "127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183";
private ZooKeeper zk;
/**
* 认证类型
*/
private final static String scheme = "digest";
private final static String auth = "root:123456";
/**
* flag 为true权限认证,否则,非权限认证
*/
public ZooKeeperAcl(boolean flag) {
try {
zk = new ZooKeeper(connectString, 5000, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
System.out.println("监听器......");
}
});
if (flag) {
zk.addAuthInfo(scheme, auth.getBytes());
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 创建持久无序节点
*/
public String createPersistent(String path, String data) throws KeeperException, InterruptedException {
return zk.create(path, data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
/**
* 创建持久无序节点(权限创建)
*/
public String createPersistentAcl(String path, String data) throws KeeperException, InterruptedException {
return zk.create(path, data.getBytes(), ZooDefs.Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT);
}
/**
* 创建临时无序节点
*/
public String createEphemeral(String path, String data) throws KeeperException, InterruptedException {
return zk.create(path, data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
}
/**
* 获取信息
*/
public String getData(String path) throws KeeperException, InterruptedException {
byte[] data = zk.getData(path, false, null);
data = (data == null) ? "null".getBytes() : data;
return new String(data);
}
/**
* 更新信息
*/
public Stat setData(String path, String data) throws KeeperException, InterruptedException {
return zk.setData(path, data.getBytes(), -1);
}
/***
* 判断节点是否存在
*/
public Stat exists(String path) throws KeeperException, InterruptedException {
return zk.exists(path, false);
}
/***
* 删除节点
*/
public void delete(String path) throws KeeperException, InterruptedException {
zk.delete(path, -1);
}
/***
* 递归删除节点
*/
public void deleteRecursive(String path) throws KeeperException, InterruptedException {
ZKUtil.deleteRecursive(zk, path);
}
/**
* 测试代码
*/
public static void main(String[] args) throws KeeperException, InterruptedException {
// 1、权限认证(flag = true)
ZooKeeperAcl acl = new ZooKeeperAcl(true);
String path = "/lanhuiguAcl";
if (null != acl.exists(path)) {
acl.delete(path);
// 如果节点下还有节点数据,需要递归删除
// acl.deleteRecursive("/lanhuiguAcl");
}
acl.createPersistentAcl(path, "2019-acl");
System.out.println("权限认证读取:" + acl.getData(path));
// 2、非权限认证(flag = false)
ZooKeeperAcl noAcl = new ZooKeeperAcl(false);
System.out.println("非权限认证读取权限认证:" + noAcl.getData(path));
// 命令客户端权限认证访问
/**
* [zk: localhost:2181(CONNECTED) 12] addauth digest root:123456
* [zk: localhost:2181(CONNECTED) 13] get /lanhuiguAcl
* 2019-acl
* cZxid = 0x300000095
* ctime = Sun May 12 22:26:19 CST 2019
* mZxid = 0x300000095
* mtime = Sun May 12 22:26:19 CST 2019
* pZxid = 0x300000095
* cversion = 0
* dataVersion = 0
* aclVersion = 0
* ephemeralOwner = 0x0
* dataLength = 8
* numChildren = 0
*/
}
}
5、代码运行
6、代码说明
1)在构造器设置权限级别,用户名和密码;
2)权限认证创建节点,权限认证能读,但是非权限认证去读的时候会报NoAuth错误;
3)zkCli访问时需要授权访问;
[zk: localhost:2181(CONNECTED) 12] addauth digest root:123456
[zk: localhost:2181(CONNECTED) 13] get /lanhuiguAcl
2019-acl
cZxid = 0x300000095
ctime = Sun May 12 22:26:19 CST 2019
mZxid = 0x300000095
mtime = Sun May 12 22:26:19 CST 2019
pZxid = 0x300000095
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 8