文章目录
ZooKeeper的安装
在文章【Spring Boot】Dubbo和Zookeeper中已经说明了zookeeper的安装,这里直接copy过来。
-
下载地址:http://mirror.bit.edu.cn/apache/zookeeper
-
下载好后解压,找到bin目录下的zkServer.cmd双击运行。
报错: -
报错解决方案:
- 没有zoo.cfg配置文件
找到conf文件下zoo_sample.cfg,这就是我们zoo.cfg的配置样本,简单操作:复制一份zoo_sample.cfg在这个目录下改名为zoo.cfg。
- 找不到或无法加载主类:
下载:下面文件并解压,复制里面的lib文件到我们的apache-zookeeper-3.6.2目录下即可。
再次双击zkServer.cmd:
同时双击zkCli.cmd,可以看到已经连接到了
ZooKeeper常用shell命令
查询节点
get /hadoop
查看结点的数据和属性 stat /hadoop
查看结点的属性
新增节点
创建结点并写入数据:
create [-s] [-e] path data
其中 -s 为有序结点,-e 临时结点(默认是持久结点)
更新节点
更新结点的命令是set
,可以直接进行修改,如下:
set path data [version]
删除节点
删除结点的语法如下:
delete path [version]
和 set
方法相似,也可以传入版本号
要想删除某个结点及其所有后代结点,可以使用递归删除,命令为 rmr path
查看结点列表
ls path
# 可以查看结点的列表
ls2 path
# 可以查看结点的列表以及目标结点的信息
ls /
# 根节点
授权的权限
权限模式:
- IP:通过ip进行权限控制,比如设置了ip:192.168.109.133那么表示权限控制都是针对这个ip来的。
- Digest:最常用。形如”username:password”形式的权限标识进行控制。
- world:最开放.对所有用户开放,所有用户都不需要权限验证就可以操作ZK。可以认为这模式没啥作用
- super:超级用户可以对ZK进行任何操作。
权限 | ACL简写 | 描述 |
---|---|---|
create | c | 可以创建子结点 |
delete | d | 可以删除子结点(仅下一级结点) |
read | r | 可以读取结点数据以及显示子结点列表 |
write | w | 可以设置结点数据 |
admin | a | 可以设置结点访问控制权限列表 |
create、delete、read、writer、admin
也就是 增、删、查、改、管理权限,这5种权限简写为 c d r w a,注意:
这五种权限中,有的权限并不是对结点自身操作的例如:delete是指对子结点的删除权限
可以试图删除父结点,但是子结点必须删除干净,所以delete
的权限也是很有用的
授权命令:
命令 | 使用方式 | 描述 |
---|---|---|
getAcl | getAcl | 读取ACL权限 |
setAcl | setAcl | 设置ACL权限 |
addauth | addauth | 添加认证用户 |
getAcl /node
// 读取权限信息setAcl /node world:anyone:drwa
// 设置权限(禁用创建子结点的权限)addauth digest <user>:<password>
JavaAPI操作ZooKeeper
znode是zooKeeper集合的核心组件,zookeeper API提供了一小组方法使用zookeeper集合来操纵znode的所有细节。
- 客户端应该遵循以步骤,与zookeeper服务器进行清晰和干净的交互。
- 连接到zookeeper服务器。zookeeper服务器为客户端分配会话ID。
- 定期向服务器发送心跳。否则,zookeeper服务器将过期会话ID,客户端需要重新连接。只要会话ID处于活动状态,就可以获取/设置znode。
- 所有任务完成后,断开与zookeeper服务器的连接。如果客户端长时间不活动,则zookeeper服务器将自动断开客户端。
环境搭配
- 创建一个Maven项目
- 导入zookeeper相应的依赖:
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.14</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
连接到zookeeper
主要使用到为我们的Zookeeper类
Zookeeper(String connectionString, int sessionTimeout, watcher watcher)
connectionString
-zookeeper
主机sessionTimeout
- 会话超时watcher
- 实现"监听器" 对象。zookeeper
集合通过监视器对象返回连接状态
public class ZooKeeperConnection {
public static void main(String[] args) {
//计数器对象
CountDownLatch countDownLatch = new CountDownLatch(1);
try {
//new ZooKeeper(服务器的ip和端口,客户端与服务器之间的会话超时时间以毫秒为单位的,监视器对象)
ZooKeeper zooKeeper = new ZooKeeper("127.0.0.1:2181", 5000, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
if (watchedEvent.getState() == Watcher.Event.KeeperState.SyncConnected) {
System.out.println("连接成功");
countDownLatch.countDown();
}
}
});
//主线程阻塞,等待连接对象创建成功
countDownLatch.await();
//输出会话ID
System.out.println(zooKeeper.getSessionId());
zooKeeper.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
新增节点
创建方式
- 同步方式:
create(String path, byte[] data, List<ACL> acl, CreateMode createMode)
- 异步方式
create(String path, byte[] data, List<ACL> acl, CreateMode createMode,
AsynCallback.StringCallback callBack, Object ctx)
参数 | 解释 |
---|---|
path | znode 路径 |
data | 数据 |
acl | 要创建的节点的访问控制列表。zookeeper API 提供了一个静态接口 ZooDefs.Ids 来获取一些基本的acl 列表。例如,ZooDefs.Ids.OPEN_ACL_UNSAFE 返回打开znode 的acl 列表 |
createMode | 节点的类型,这是一个枚举, 持久结点 or 临时节点 |
callBack | 异步回调接口 |
ctx | 传递上下文参数 |
创建节点Java代码:
public class ZKCreate {
ZooKeeper zooKeeper;
String IP = "127.0.0.1:2181";
/*
执行顺序:Before==》Test==》After
before:连接zookeeper
Test:创建节点
after:关闭zookeeper连接
*/
@Before
public void before() throws Exception {
//计数器对象
CountDownLatch countDownLatch = new CountDownLatch(1);
//new ZooKeeper(服务器的ip和端口,客户端与服务器之间的会话超时时间以毫秒为单位的,监视器对象)
zooKeeper = new ZooKeeper(IP, 5000, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
if (watchedEvent.getState() == Watcher.Event.KeeperState.SyncConnected) {
System.out.println("连接成功");
countDownLatch.countDown();
}
}
});
//主线程阻塞,等待连接对象创建成功
countDownLatch.await();
}
@After
public void after() throws Exception {
zooKeeper.close();
}
@Test
public void create() throws Exception {
//create(节点的路径,节点的数据,权限列表 worLd:anyone:cdrwa,节点类型持久化节点)
zooKeeper.create("/node/create","hello".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
}
在zkCli.cmd中创建一个/node
节点,然后执行java代码,看是否会创建/node/create
节点。
查看节点属性:
查看权限:world方式授权每个人anyone都有cdrwa权限
创建节点权限设置
- 先看我们前面代码创建节点的权限方式
Java代码创建节点ZooKeeper.create()
:
方法中的第三个参数ZooDefs.Ids.OPEN_ACL_UNSAFE
表示world方式授权每个人anyone都有cdrwa权限,
我们可以修改权限参数:
查看ZooDefs 类部分源码:
public class ZooDefs {
......
......
@Public
public interface Ids {
//world方式授权给每个人
Id ANYONE_ID_UNSAFE = new Id("world", "anyone");
//auth授权给特定的人
Id AUTH_IDS = new Id("auth", "");
@SuppressFBWarnings(
value = {"MS_MUTABLE_COLLECTION"},
justification = "Cannot break API"
)
//31=All:拥有全部权限
//ANYONE_ID_UNSAFE:world方式授权给anyone
ArrayList<ACL> OPEN_ACL_UNSAFE = new ArrayList(Collections.singletonList(new ACL(31, ANYONE_ID_UNSAFE)));
@SuppressFBWarnings(
value = {"MS_MUTABLE_COLLECTION"},
justification = "Cannot break API"
)
//31=All:拥有全部权限
//AUTH_IDS:auth方式授权给特定用户
ArrayList<ACL> CREATOR_ALL_ACL = new ArrayList(Collections.singletonList(new ACL(31, AUTH_IDS)));
@SuppressFBWarnings(
value = {"MS_MUTABLE_COLLECTION"},
justification = "Cannot break API"
)
//1=READ :拥有读权限
//ANYONE_ID_UNSAFE:world方式授权给anyone
ArrayList<ACL> READ_ACL_UNSAFE = new ArrayList(Collections.singletonList(new ACL(1, ANYONE_ID_UNSAFE)));
}
@Public
public interface Perms {
int READ = 1;
int WRITE = 2;
int CREATE = 4;
int DELETE = 8;
int ADMIN = 16;
int ALL = 31;
}
......
......
}
create
方法中第3个参数可以传ZooDefs类中3种参数:
ZooDefs.Ids.OPEN_ACL_UNSAFE
:world方式授权每个人anyone都有cdrwa权限ZooDefs.Ids.CREATOR_ALL_ACL
:auth方式授权特定用户,有cdrwa权限ZooDefs.Ids.READ_ACL_UNSAFE
:world方式授权给anyone, r 权限
- 我们可以采用上面源码的方式自己创建权限给节点
(a)world方式授权给anyone
@Test
public void create2() throws Exception {
List<ACL> acls = new ArrayList<>();
Id id = new Id("world","anyone");
// r 权限
acls.add(new ACL(ZooDefs.Perms.READ,id));
// w
acls.add(new ACL(ZooDefs.Perms.WRITE,id));
//其他权限,像上面一样加即可
zooKeeper.create("/node/create2","hello2".getBytes(), acls , CreateMode.PERSISTENT);
}
(b)ip方式授权
和上面一样,只需要修改创建Id的方式:Id id = new Id( "ip","192.168.60.130");
(c)digest方式授权
和上面一样,只需要修改创建Id的方式:Id id = new Id( "digest","gaolang:123456");
// 用户名:密码
(d)auth方式授权特定用户
全部授权的话,可以直接用 ZooDefs.Ids.CREATOR_ALL_ACL
@Test
public void create3() throws Exception {
//"gaolang:123456" : 用户名:密码
zooKeeper.addAuthInfo("digest","gaolang:123456".getBytes());
zooKeeper.create("/node/create3","hello3".getBytes(), ZooDefs.Ids.CREATOR_ALL_ACL , CreateMode.PERSISTENT);
}
其他权限:
@Test
public void create4() throws Exception {
zooKeeper.addAuthInfo("digest","gaolang:123456".getBytes());
List<ACL> acls = new ArrayList<>();
Id id = new Id("auth","gaolang");
// r 权限
acls.add(new ACL(ZooDefs.Perms.READ,id));
// w
acls.add(new ACL(ZooDefs.Perms.WRITE,id));
//其他权限,像上面一样加即可
zooKeeper.create("/node/create4","hello4".getBytes(), acls , CreateMode.PERSISTENT);
}
临时节点、持久节点
临时节点还是持久节点的设置在create
方法的第4个参数。我们上面创建节点的第4个参数都是CreateMode.PERSISTENT
,也就是持久节点。
有4种参数:
- 持久节点:
CreateMode.PERSISTENT
- 临时节点:
CreateMode.EPHEMERAL
- 持久化顺序节点:
CreateMode.PERSISTENT_SEQUENTIAL
- 临时顺序节点:
CreateMode.EPHEMERAL_SEQUENTIAL
创建的临时节点我们是在zkCil.cmd上是查询不到的,因为我们程序是在连接-创建-关闭后,相当于我们一次会话结束,临时节点消失。
同步和异步方式
上面全部采用同步的方式:create
方法执行完成后,且参数没有问题,客服端一定就会收到我们传来的信息,而异步执行完成后不代表客户端收到了。
异步方式:
@Test
public void create5() throws Exception{
zooKeeper.create("/node/create5", "hello5".getBytes(), ZooDefs.Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT, new AsyncCallback.StringCallback() {
@Override
public void processResult(int rc, String path, Object ctx, String name) {
// 0 代表创建创建成功
System.out.println(rc);
// 节点的路径
System.out.println(path);
// 节点的名字
System.out.println(ctx);
}
},"Context");
Thread.sleep(10000);
System.out.println("结束");
}
更新节点
更新方式
- 同步方式:
// 同步
setData(String path, byte[] data, int version)
- 异步方式:
// 异步
setData(String path, byte[] data, int version, StatCallback callBack, Object ctx)
- 参数含义:
参数 | 解释 |
---|---|
path | 节点路径 |
data | 数据 |
version | 数据的版本号, -1 代表不使用版本号,乐观锁机制 |
callBack | 异步回调 AsyncCallback.StatCallback ,和之前的回调方法参数不同,这个可以获取节点状态 |
ctx | 传递上下文参数 |
- 修改某个节点的值:
public class ZKUpdate {
ZooKeeper zooKeeper;
String IP = "127.0.0.1:2181";
/*
执行顺序:Before==》Test==》After
before:连接zookeeper
Test:更新节点
after:关闭zookeeper连接
*/
@Before
public void before() throws Exception {
//计数器对象
CountDownLatch countDownLatch = new CountDownLatch(1);
//new ZooKeeper(服务器的ip和端口,客户端与服务器之间的会话超时时间以毫秒为单位的,监视器对象)
zooKeeper = new ZooKeeper(IP, 5000, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
if (watchedEvent.getState() == Watcher.Event.KeeperState.SyncConnected) {
System.out.println("连接成功");
countDownLatch.countDown();
}
}
});
//主线程阻塞,等待连接对象创建成功
countDownLatch.await();
}
@After
public void after() throws Exception {
zooKeeper.close();
}
@Test
public void update() throws Exception{
//第一个参数:节点的路径
//第二个参数:修改的数据
//第三个参数:数据版本号 -1代表版本号不参与匹配
zooKeeper.setData("/node/create","helloworld".getBytes(),-1);
}
}
版本号更新
更新版本号在setDate
方法的第3个参数:
(a)-1:代表版本号不参与匹配
(b)随机写一个数,比如 2,会版本号错误:org.apache.zookeeper.KeeperException$BadVersionException: KeeperErrorCode = BadVersion for /node/create
这是因为版本号不匹配的原因:
查看我们该节点的版本号:是 1 ,而我们匹配的版本号为2,自然报错
(c)版本号更新采用的是乐观锁机制,版本号匹配成功或者参数为-1不参与匹配的话,执行完后版本号加1
异步方式
异步方式修改:
@Test
public void update2() throws Exception {
zooKeeper.setData("/node/create", "helloworld2".getBytes(), -1, new AsyncCallback.StatCallback() {
@Override
public void processResult(int rc, String path, Object ctx, Stat stat) {
//0代表修改成功
System.out.println(rc);
//节点的路径
System.out.println(path);
//上下文参数对象
System.out.println(ctx);
//属性描述对象
System.out.println(stat.getVersion());
}
},"Context");
Thread.sleep(10000);
System.out.println("结束");
}
删除节点
删除方式
- 同步方式
// 同步
delete(String path, int version)
- 异步方式
// 异步
delete(String path, int version, AsyncCallback.VoidCallback callBack, Object ctx)
- 参数含义
参数 | 解释 |
---|---|
path | 节点路径 |
version | 版本 |
callBack | 数据的版本号, -1 代表不使用版本号,乐观锁机制 |
ctx | 传递上下文参数 |
- 删除某个节点
public class ZKDelete {
ZooKeeper zooKeeper;
String IP = "127.0.0.1:2181";
/*
执行顺序:Before==》Test==》After
before:连接zookeeper
Test:删除节点
after:关闭zookeeper连接
*/
@Before
public void before() throws Exception {
//计数器对象
CountDownLatch countDownLatch = new CountDownLatch(1);
//new ZooKeeper(服务器的ip和端口,客户端与服务器之间的会话超时时间以毫秒为单位的,监视器对象)
zooKeeper = new ZooKeeper(IP, 5000, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
if (watchedEvent.getState() == Watcher.Event.KeeperState.SyncConnected) {
System.out.println("连接成功");
countDownLatch.countDown();
}
}
});
//主线程阻塞,等待连接对象创建成功
countDownLatch.await();
}
@After
public void after() throws Exception {
zooKeeper.close();
}
@Test
public void delete() throws Exception {
//第一个参数:节点路径
//第二个参数:版本信息 -1:不考虑版本信息
zooKeeper.delete("/node/create",-1);
}
}
异步方式
删除一个节点:
@Test
public void delete2() throws Exception {
zooKeeper.delete("/node/create", -1, new AsyncCallback.VoidCallback() {
@Override
public void processResult(int rc, String path, Object ctx) {
//0代表修改成功
System.out.println(rc);
//节点的路径
System.out.println(path);
//上下文参数对象
System.out.println(ctx);
}
},"Context");
Thread.sleep(10000);
System.out.println("结束");
}
查看节点
- 同步方式:
// 同步
getData(String path, boolean watch, Stat stat)
getData(String path, Watcher watcher, Stat stat)
- 异步方式
// 异步
getData(String path, boolean watch, DataCallback callBack, Object ctx)
getData(String path, Watcher watcher, DataCallback callBack, Object ctx)
3.参数含义
参数 | 解释 |
---|---|
path | 节点路径 |
boolean | 是否使用连接对象中注册的监听器 |
stat | 元数据 |
callBack | 异步回调接口,可以获得状态和数据 |
ctx | 传递上下文参数 |
- 代码实现
public class ZKGetData {
ZooKeeper zooKeeper;
String IP = "127.0.0.1:2181";
/*
执行顺序:Before==》Test==》After
before:连接zookeeper
Test:获取节点数据
after:关闭zookeeper连接
*/
@Before
public void before() throws Exception {
//计数器对象
CountDownLatch countDownLatch = new CountDownLatch(1);
//new ZooKeeper(服务器的ip和端口,客户端与服务器之间的会话超时时间以毫秒为单位的,监视器对象)
zooKeeper = new ZooKeeper(IP, 5000, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
if (watchedEvent.getState() == Watcher.Event.KeeperState.SyncConnected) {
System.out.println("连接成功");
countDownLatch.countDown();
}
}
});
//主线程阻塞,等待连接对象创建成功
countDownLatch.await();
}
@After
public void after() throws Exception {
zooKeeper.close();
}
//同步方式:
@Test
public void getData() throws Exception {
//第一个参数:节点路径
//第三个参数:节点属性对象
Stat stat = new Stat();
byte[] data = zooKeeper.getData("/node/create3", false, stat);
//打印数据:
System.out.println(new String(data));
//版本信息:
System.out.println(stat.getVersion());
}
// 异步方式:
@Test
public void getData2() throws Exception {
zooKeeper.getData("/node/create3", false, new AsyncCallback.DataCallback() {
@Override
public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) {
//0代表修改成功
System.out.println(rc);
//节点的路径
System.out.println(path);
//上下文参数对象
System.out.println(ctx);
//数据
System.out.println(new String(data));
//属性对象
System.out.println(stat.getVersion());
}
},"Context");
Thread.sleep(10000);
System.out.println("结束");
}
}
查看子节点
- 同步方式
// 同步
getChildren(String path, boolean watch)
getChildren(String path, Watcher watcher)
getChildren(String path, boolean watch, Stat stat)
getChildren(String path, Watcher watcher, Stat stat)
- 异步方式
// 异步
getChildren(String path, boolean watch, ChildrenCallback callBack, Object ctx)
getChildren(String path, Watcher watcher, ChildrenCallback callBack, Object ctx)
getChildren(String path, Watcher watcher, Children2Callback callBack, Object ctx)
getChildren(String path, boolean watch, Children2Callback callBack, Object ctx)
- 参数含义
参数 | 解释 |
---|---|
path | 节点路径 |
boolean | 是否使用连接对象中注册的监听器 |
callBack | 异步回调,可以获取节点列表 |
ctx | 传递上下文参数 |
- 代码实现
public class ZKGetChildren {
ZooKeeper zooKeeper;
String IP = "127.0.0.1:2181";
/*
执行顺序:Before==》Test==》After
before:连接zookeeper
Test:获取子节点
after:关闭zookeeper连接
*/
@Before
public void before() throws Exception {
//计数器对象
CountDownLatch countDownLatch = new CountDownLatch(1);
//new ZooKeeper(服务器的ip和端口,客户端与服务器之间的会话超时时间以毫秒为单位的,监视器对象)
zooKeeper = new ZooKeeper(IP, 5000, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
if (watchedEvent.getState() == Watcher.Event.KeeperState.SyncConnected) {
System.out.println("连接成功");
countDownLatch.countDown();
}
}
});
//主线程阻塞,等待连接对象创建成功
countDownLatch.await();
}
@After
public void after() throws Exception {
zooKeeper.close();
}
//获取子节点
//同步方式
@Test
public void get() throws Exception {
//获取“/node”节点下的子节点
List<String> children = zooKeeper.getChildren("/node", false);
for (String child : children) {
System.out.println(child);
}
}
//异步方式
@Test
public void get2() throws Exception {
zooKeeper.getChildren("/node", false, new AsyncCallback.ChildrenCallback() {
@Override
public void processResult(int rc, String path, Object ctx, List<String> children) {
//0代表修改成功
System.out.println(rc);
//节点的路径
System.out.println(path);
//上下文参数对象
System.out.println(ctx);
//子节点信息
for (String child : children) {
System.out.println(child);
}
}
}, "Context");
Thread.sleep(10000);
System.out.println("结束");
}
}
检查节点是否存在
- 同步方式:
// 同步
exists(String path, boolean watch)
exists(String path, Watcher watcher)
- 异步方式:
// 异步
exists(String path, boolean watch, StatCallback cb, Object ctx)
exists(String path, Watcher watcher, StatCallback cb, Object ctx)
- 参数解释
参数 | 解释 |
---|---|
path | 节点路径 |
boolean | 是否使用连接对象中注册的监听器 |
callBack | 异步回调,可以获取节点列表 |
ctx | 传递上下文参数 |
- 代码实现
public class ZKExistence {
ZooKeeper zooKeeper;
String IP = "127.0.0.1:2181";
/*
执行顺序:Before==》Test==》After
before:连接zookeeper
Test:更新节点
after:关闭zookeeper连接
*/
@Before
public void before() throws Exception {
//计数器对象
CountDownLatch countDownLatch = new CountDownLatch(1);
//new ZooKeeper(服务器的ip和端口,客户端与服务器之间的会话超时时间以毫秒为单位的,监视器对象)
zooKeeper = new ZooKeeper(IP, 5000, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
if (watchedEvent.getState() == Watcher.Event.KeeperState.SyncConnected) {
System.out.println("连接成功");
countDownLatch.countDown();
}
}
});
//主线程阻塞,等待连接对象创建成功
countDownLatch.await();
}
@After
public void after() throws Exception {
zooKeeper.close();
}
//检查节点是否存在
//同步方式:
@Test
public void existence() throws Exception {
Stat stat = zooKeeper.exists("/node", false);
if (stat == null){
System.out.println("/node节点不存在");
}else {
System.out.println("/node节点存在,版本号为:"+stat.getVersion());
}
}
//异步方式
@Test
public void existence2() throws Exception {
zooKeeper.exists("/node", false, new AsyncCallback.StatCallback() {
@Override
public void processResult(int rc, String path, Object ctx, Stat stat) {
//0代表修改成功
System.out.println(rc);
//节点的路径
System.out.println(path);
//上下文参数对象
System.out.println(ctx);
//判断节点是否存在
if (stat == null){
System.out.println("/node节点不存在");
}else {
System.out.println("/node节点存在,版本号为:"+stat.getVersion());
}
}
},"Context");
Thread.sleep(10000);
System.out.println("结束");
}
}