目录
本文将是对zookeeper基础知识的补充,其中涉及到了java的相关内容。与利用jdbc连接数据库一样,我们可以使用idea远程连接zookeeper,然后使用java提供的API操作zk的znode。同样连接zookeeper需要依赖。zookeeper的连接依赖在安装包(linux系统下:/export/server/zookeeper/lib/)里就有:
然后下载netty依赖:netty-3.10.5.Final.jar
后续会使用Junit进行单元测试,高阶JDK已经自动集成。此外还需要log4j的配置文件log4j.properties
下载链接:https://pan.baidu.com/s/1Y_watqwNYC_9iH_X2pi6Ig
提取码:zxyd
然后将上述依赖全部整合到我们的项目中:
一、zookeeper Java API
1、连接zookeeper
ZooKeeper(String connectionString, int sessionTimeout, Watcher watcher):ZooKeeper类的有参构造器
connectionString:是一个字符串,与使用jdbc连接数据库一样,需要指定连接的zookeeper在哪里(ip:客户端端口2181)
sessionTimeout:会话超时(以毫秒为单位),客户端在限定时间内连接zookeeper服务端,如果超时,就会取消当次连接
watcher:一个监视器对象,zookeeper通过监视器对象返回连接状态。Watcher是一个接口,使用匿名内部类创建对象。当客户端与服务器端的连接成功时,服务器端会给客户端发一个消息,该消息会被watcher监视器事件监听到,一旦监听到会自动回调process方法。
注意如果zookeeper是集群环境connectString参数一般需要填写所有zookeeper服务器ip和端口(必须保证超过半数以上zk服务器)。这里与直接在linux系统上启动zk的客户端不同。
注意:由于zookeeper对象的创建是异步的(执行到new语句时会自动执行一个线程来创建zookeeper对象),因此可能出现new Zookeeper语句执行完主线程往下执行其他代码的时候,zookeeper对象还没创建出来呢!此时需要让主线程等待zookeeper对象创建成功后再向下执行。需要用到计数器对象CountDownLatch(int n)(它是一种同步辅助工具,允许一个或多个线程等待,直到其他n个线程执行的任务完成)。利用watcher监听器通知CountDownLatch什么时候对象创建成功,继续执行。
集群环境下出现以下报错是因为开启的zk服务器没有超过半数以上。
开启的zk服务器超过半数以上了,但每次都是先建立连接成功后又报错,这是正常现象,因为当执行zookeeper.close()释放zookeeper资源时是一个一个释放,释放连接的瞬间一定会产生zk服务器小于半数以上的情况。
2、新增节点
异步方式需要使用回调接口返回是否创建成功,ctx可以为callBack传入参数。acl:ACL权限控制,系统提供有三种方式:(支持自定义)
ZooDefs.Ids.OPEN_ACL_UNSAFE:完全开放的API,任何连接的客户端都可以操作该节点等同于:world:anyone:cdrwa
ZooDefs.Ids.CREATOR_ALL_ACL:只有创建者才有ACL权限,用于auth授权模式
ZooDefs.Ids.READ_ACL_UNSAFE:只能读取等同于:world:anyone:r
createMode用于设置节点类型是有序还是无序呢?是持久还是临时呢?
CreateMode.PERSISTENT:持久化节点
CreateMode.EPHEMERAL:临时节点
CreateMode.EPHEMERAL_SEQUENTIAL :临时有序节点
CreateMode.PERSISTENT_SEQUENTIAL:持久有序节点
同步方式:
注意:IP虽然设置的所有zk服务器的ip和端口,但是create4方法在实际执行过程中本质上还是在192.168.11.141的客户端上执行的,因此只有通过192.168.11.143远程连接192.168.11.141客户端才有权访问。
首先添加授权用户才有权访问:
由于执行完成后会话自动关闭,其实创建出的临时节点已经不存在了。
异步方式:
3、更新节点
version填写-1表示不检测数据版本
同理提前写好@After和@Before。先提前创建出/set/node1节点,内容是node1同步方式:
setData有返回值是Stat类型的对象,用于返回该节点修改后的元数据:
异步方式:
4、删除节点
注意:这里的delete方法只能删除没有子节点的节点。非空节点无法删除。原生zookeeper javaAPI无法删除非空节点
5、查看节点
6、查看子节点
7、判断节点是否存在
可以先判断stat是否等于null,等于null节点不存在,如果不等于null,则返回版本号说明节点存在
二、zookeeper的监听机制
zookeeper提供了数据的发布与订阅功能(Watcher接口),多个订阅者(客户端)可同时监听某一特定主题对象(znode节点),当该主题对象的自身状态发生变化时(例如节点内容,节点的子节点发生变化),监听器会实时的监听到,并以异步方式通知所有订阅者。
java实现zk的监听是使用Watcher接口实现的。首先需要在java代码中获得一个实现了Watcher接口的实现类对象(该对象就是监听器),然后将该对象注册到zk的服务器上。一旦服务器上的某个znode节点发生变化时这个监听器就会捕获到这个事件,并自动调用自己的process回调方法通知客户端相关节点发生变化。
zk监听机制的特征:
本质上:客户端创建了监听器对象后会将该对象注册到zk的服务器上,同时会将该对象保存到一个WatchManager容器内,当服务端的某个znode节点发生变化,监听器对象会监听到该事件,然后会将监听到的事件信息提交给WatchManager容器内的监听器对象。客户端想要得到事件信息,必须回调WatchManager容器内监听器对象的process方法。这就是客户端的顺序回调。
Watcher接口:
WatchedEvent就是捕获到的事件对象,其内部保存了所有的事件信息(比如事件类型和连接状态)。事件类型(EventType)和连接状态(KeeperState)都是枚举类用于标记有那些事件类型和连接状态。事件类型有5种分别是:
None:无
NodeCreate:Watcher监听的数据节点被创建
NodeDeleted:Watcher监听的数据节点被删除
NodeDataChanged:Watcher监听的数据节点内容发生变化
NodeChildrenChanged:Watcher监听的数据节点的子节点列表发生变化
KeeperStat是客户端与服务端连接状态发生变化时对应的通知类型。有4种:
SyncConnected:客户端与服务端正常连接
Disconneted:客户端与服务端异常断开连接
Expired:会话session失效
AuthFailed:添加授权用户失败(也称认证失败),在添加授权用户时addauth digest itcast:123456, digest命令输入错误。比如addauth digest1 itcast:123456而导致的添加授权用户失败。
注意:只有客户端与服务器端正常连接以后Watcher才可以进行监听服务端znode节点的变化,即当KeeperStat发生变化的时候,EventType永远为None,当EventType发生变化时KeeperStat永远处于SyncConnected成功连接状态。下面是所有可以创建Watcher对象的api(共有两大类)
在与zk服务端连接时可以设置一个Watcher对象,一旦建立连接成功Watcher会监听到服务端发来的成功事件WatchedEvent,并自动回调process方法通知客户端连接创建成功(主要用于监听KeeperStat,EventType=None)。
这些api主要监听数据节点的变化:EventType,KeeperStat=SyncConnected。
1、连接状态捕获
右键运行会直接捕获到连接创建成功,当我们把vmware8网卡关闭时,客户端与服务端的连接会异常断开,Watcher会捕获到断开连接,断开连接后系统会保留zookeeper对象5s(sessionTimeout),当我们在5秒之内打开网卡系统又会自动连接上。但是当断开连接的时间超过5s,zookeeper对象就会被销毁,此时再次打开网卡由于会话超时当前的会话已经不可用了,服务端给客户端发来的会话超时事件被Watcher捕获到。
默认情况下AuthFailed只会被监听器监听到, 不会像异常断开连接、会话超时一样会报异常。想要让其报出异常,需要对某个节点进行操作,这样系统才会识别到AuthFailed异常并报出。
2、exists的监听机制
刚开始path=null,eventType=None
启动以后在另一个客户端创建/watcher1节点,修改数据,删除节点:
同理......。注意Watcher是一次性的,先创建/watcher1,然后不停的对节点进行修改,只能监听到创建/watcher1。如果我就想让Watcher能监听到每次操作,这怎么实现呢?
针对同一个节点,我们可以注册多个监听器对象,一旦该节点发生变化,服务端会以异步方式给所有客户端发消息,所有的监听器都会监听到对应的事件信息,然后回调process方法。
3、getData、getChildren的监听机制
后面同exists一样......。针对getData方法,如果想让Watcher能监听到每次修改,只有是节点内容发生变化时才重新监听,如果节点都删除了就没必要监听了(会报错)。
getChildren与getData一模一样,这里省略。(这里只能监听到子节点列表发生变化,子节点内容的变化监听不到)
三、zookeeper作为配置中心案例
场景:将连接Mysql数据库的ip,端口、用户名和密码存储在配置中心(本质就是将ip、端口、用户名、密码存储在zk的znode节点下),客户端连接zk服务器读取配置信息存入本地变量,并注册Watcher监听器监听配置中心信息的变化。当配置信息发生变化时,通过Watcher监听器的回调process方法捕获数据变化事件,然后客户端重新获取全新的配置信息,并且再次开启监听。
首先初始化配置中心:
package com.itcast.example;
import java.util.concurrent.CountDownLatch;
import com.itcast.watcher.ZKConnectionWatcher;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.ZooKeeper;
public class MyConfigCenter implements Watcher {
// zk的连接串
String IP = "192.168.11.141:2181,192.168.11.142:2181,192.168.11.143:2181";
// 计数器对象
CountDownLatch countDownLatch = new CountDownLatch(1);
// 连接对象
static ZooKeeper zooKeeper;
// 用于本地化存储配置信息
private String url;
private String username;
private String password;
@Override
public void process(WatchedEvent event) {
try {
// 捕获事件状态
if (event.getType() == Event.EventType.None) {
if (event.getState() == Event.KeeperState.SyncConnected) {
System.out.println("连接成功");
countDownLatch.countDown();
} else if (event.getState() == Event.KeeperState.Disconnected) {
System.out.println("连接断开!");
} else if (event.getState() == Event.KeeperState.Expired) {
System.out.println("连接超时!");
// 超时后服务器端已经将连接释放,需要重新连接服务器端
zooKeeper = new ZooKeeper(IP, 6000,
new MyConfigCenter());
} else if (event.getState() == Event.KeeperState.AuthFailed) {
System.out.println("验证失败!");
}
// 当配置信息发生变化时
} else if (event.getType() == Event.EventType.NodeDataChanged) {
this.url = new String(zooKeeper.getData("/config/url", true, null));
this.username = new String(zooKeeper.getData("/config/username", true, null));
this.password = new String(zooKeeper.getData("/config/password", true, null));
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
// 构造方法
public MyConfigCenter() {
initValue();
}
// 连接zookeeper服务器,读取配置信息
public void initValue() {
try {
// 创建连接对象
zooKeeper = new ZooKeeper(IP, 5000, this);
// 阻塞线程,等待连接的创建成功
countDownLatch.await();
// 读取配置信息
this.url = new String(zooKeeper.getData("/config/url", true, null));
this.username = new String(zooKeeper.getData("/config/username", true, null));
this.password = new String(zooKeeper.getData("/config/password", true, null));
} catch (Exception ex) {
ex.printStackTrace();
}
}
public static void main(String[] args) {
try {
MyConfigCenter myConfigCenter = new MyConfigCenter();
for (int i = 1; i <= 20; i++) {
Thread.sleep(5000);
System.out.println("url:"+myConfigCenter.getUrl());
System.out.println("username:"+myConfigCenter.getUsername());
System.out.println("password:"+myConfigCenter.getPassword());
System.out.println("########################################");
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
我们可以多开实例模拟多个客户端访问配置中心。 一旦配置中心的配置信息发生变化所有的客户端都可以监听到,并可以获取最新的配置信息。
四、利用zookeeper生成分布式唯一ID
场景:在单库单表型系统中,通常可以使用数据库自带的auto_increment关键字来自动为每条记录生成唯一ID。但是分库分表后auto_increment就无法使用了,此时就需要利用zk在分布式环境下生成全局唯一ID。
package com.itcast.example;
import java.util.concurrent.CountDownLatch;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
public class GloballyUniqueId implements Watcher {
// zk的连接串
String IP = "192.168.11.141:2181,192.168.11.142:2181,192.168.11.143:2181";
// 计数器对象
CountDownLatch countDownLatch = new CountDownLatch(1);
// 用户生成序号的节点
String defaultPath = "/uniqueId";
// 连接对象
ZooKeeper zooKeeper;
@Override
public void process(WatchedEvent event) {
try {
// 捕获事件状态
if (event.getType() == Watcher.Event.EventType.None) {
if (event.getState() == Watcher.Event.KeeperState.SyncConnected) {
System.out.println("连接成功");
countDownLatch.countDown();
} else if (event.getState() == Watcher.Event.KeeperState.Disconnected) {
System.out.println("连接断开!");
} else if (event.getState() == Watcher.Event.KeeperState.Expired) {
System.out.println("连接超时!");
// 超时后服务器端已经将连接释放,需要重新连接服务器端
zooKeeper = new ZooKeeper(IP, 6000,
new GloballyUniqueId());
} else if (event.getState() == Watcher.Event.KeeperState.AuthFailed) {
System.out.println("验证失败!");
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
// 构造方法
public GloballyUniqueId() {
try {
//打开连接
zooKeeper = new ZooKeeper(IP, 5000, this);
// 阻塞线程,等待连接的创建成功
countDownLatch.await();
} catch (Exception ex) {
ex.printStackTrace();
}
}
// 生成id的方法
public String getUniqueId() {
String path = "";
try {
//创建临时有序节点
path = zooKeeper.create(defaultPath, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
} catch (Exception ex) {
ex.printStackTrace();
}
// /uniqueId0000000001
return path.substring(9); // 0000000001
}
public static void main(String[] args) {
GloballyUniqueId globallyUniqueId = new GloballyUniqueId();
for (int i = 1; i <= 5; i++) {
String id = globallyUniqueId.getUniqueId();
System.out.println(id);
}
}
}
我们可以模拟出多个客户端进行测试
只要所有的客户端连接的是同一个zookeeper,那么在zk上面创建的临时有序节点就会按照顺序创建,即使比如客户端1执行了zookeeper.close方法,zookeeper也只是将创建的00000001,00000004,00000008临时节点进行删除,编号还是从00000010开始。
注意上述代码是线程安全的,因为zk的特点就是为每一个操作生成唯一的事务id,zk会严重按照事务id顺序执行。
五、利用zookeeper生成分布式锁
什么是分布式锁:假设有3个对象分别是01,02,03,一个共享资源test表。当01访问test表时首先向分布式锁申请访问权限,分布式锁看到目前test表没有人操作则允许01访问test表,并对test表上锁,同一时刻当02想对test表修改时首先向分布式锁申请权限,此时分布式锁发现test表正在被01所访问,且01并没有释放锁,因此不允许02访问,同理03申请也不允许,02、03需要等待01操作完才可访问test,保证了数据的一致性。
package com.zxy.zookeeper;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
public class MyLock {
// zk的连接串
String IP = "192.168.11.141:2181,192.168.11.142:2181,192.168.11.143:2181";
// 计数器对象
CountDownLatch countDownLatch = new CountDownLatch(1);
//ZooKeeper配置信息
ZooKeeper zooKeeper;
private static final String LOCK_ROOT_PATH = "/Locks";
private static final String LOCK_NODE_NAME = "Lock_";
private String lockPath; // 存储创建的临时有序节点路径
// 打开zookeeper连接
public MyLock() {
try {
zooKeeper = new ZooKeeper(IP, 5000, new Watcher() {
@Override
public void process(WatchedEvent event) {
if (event.getType() == Event.EventType.None) {
if (event.getState() == Event.KeeperState.SyncConnected) {
System.out.println("连接成功!");
countDownLatch.countDown();
}
}
}
});
countDownLatch.await();
} catch (Exception ex) {
ex.printStackTrace();
}
}
//获取锁
public void acquireLock() throws Exception {
//创建锁节点
createLock();
//尝试获取锁
attemptLock();
}
//创建锁节点
private void createLock() throws Exception {
//判断Locks是否存在,不存在创建
Stat stat = zooKeeper.exists(LOCK_ROOT_PATH, false);
if (stat == null) {
zooKeeper.create(LOCK_ROOT_PATH, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
// 创建临时有序节点
lockPath = zooKeeper.create(LOCK_ROOT_PATH + "/" + LOCK_NODE_NAME, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
System.out.println("节点创建成功:" + lockPath);
}
//监视器对象,监视上一个节点是否被删除
Watcher watcher = new Watcher() {
@Override
public void process(WatchedEvent event) {
if (event.getType() == Event.EventType.NodeDeleted) {
synchronized (this) {
notifyAll();
}
}
}
};
//尝试获取锁
private void attemptLock() throws Exception {
// 获取Locks节点下的所有子节点
List<String> list = zooKeeper.getChildren(LOCK_ROOT_PATH, false);
// 对子节点进行排序
Collections.sort(list);
// /Locks/Lock_000000001
int index = list.indexOf(lockPath.substring(LOCK_ROOT_PATH.length() + 1));
if (index == 0) {
System.out.println("获取锁成功!");
return;
} else {
// 上一个节点的路径
String path = list.get(index - 1);
Stat stat = zooKeeper.exists(LOCK_ROOT_PATH + "/" + path, watcher);
if (stat == null) {
attemptLock();
} else {
synchronized (watcher) {
watcher.wait();
}
attemptLock();
}
}
}
//释放锁
public void releaseLock() throws Exception {
//删除临时有序节点
zooKeeper.delete(this.lockPath,-1);
zooKeeper.close();
System.out.println("锁已经释放:"+this.lockPath);
}
}
package com.zxy.zookeeper;
// 测试类
public class TicketSeller {
private void sell(){
System.out.println("售票开始");
// 线程随机休眠数毫秒,模拟现实中的费时操作
int sleepMillis = 5000;
try {
//代表复杂逻辑执行了一段时间
Thread.sleep(sleepMillis);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("售票结束");
}
public void sellTicketWithLock() throws Exception {
MyLock lock = new MyLock();
// 获取锁
lock.acquireLock();
sell();
//释放锁
lock.releaseLock();
}
public static void main(String[] args) throws Exception {
TicketSeller ticketSeller = new TicketSeller();
for(int i=0;i<10;i++){
ticketSeller.sellTicketWithLock();
}
}
}
同时开启多个客户端执行:
六、zookeeper开源客户端curator
curator框架在zookeeper原生API接口上进行了包装,解决了很多zookeeper客户端非常底层的细节问题。提供zookeeper各种应用场景(比如分布式锁,集群leader选举,共享计数器,缓存机制,分布式队列等)的抽象封装,实现了Fluent风格(就是xxx().xxxx().xxxxx().xxxxx(),流式编程风格)的API接口。
原生zookeeperAPI的不足:连接对象异步创建,需要开发人员自行编码等待;连接没有自动重连超时机制;watcher一次注册生效一次;不支持递归创建树形节点。
curator的特点:解决session话超时重连问题;watcher可以反复注册;简化开发API;遵循Fluent风格的API;提供了分布式锁服务,共享计数器,缓存机制等等功能的抽象封装,直接哪来用即可。
首先下载curator所需要的jar包:
curator-client-5.1.0.jar
curator-framework-5.1.0.jar
curator-recipes-5.1.0.jar
slf4j-api-1.7.25.jar
zookeeper-3.6.0.jar
zookeeper-jute-3.6.0.jar
下载地址:https://mvnrepository.com/
注意市面上所有第三方框架的jar包都可以在上述地址下载:
全部下载好以后,整合到项目中就可以使用了。
1、常见curator API
(1)连接zookeeper
如果出现以下报错说明缺少selenium-server-standalone的jar包:
下载地址:https://selenium-release.storage.googleapis.com/index.html
在curator中提供了4种常见的重连策略,分别是:RetryPolicy下面的四个实现类
new RetryOneTime(3000) : 3s后重连一次,只重连一次
new RetryNtimes(3,3000):每3s重连一次,重连三次
new RetryUntilElapsed(10000,3000):每3s重连一次,总等待时间超过10s停止重连
new ExponentialBackoffRety(baseSleepTimeMs:1000,maxRetries:3):maxRetries表示会重连三次,每次重连的间隔为baseSleepTimeMs * Math.max(1, random.nextInt(1 << (retryCount + 1))),这种策略的特点是随着重连次数的增加,重连间隔会变长。
(2)新增节点
注意:.forPath必须放最后面。
(3)更新节点
修改命名空间为set后,再开始测试:
(4)删除节点
修改命名空间为delete后,再开始测试:
(5)查看节点
修改命名空间为get后,再开始测试:
(6)查看子节点
不设置命名空间,使用上述/get节点测试:
(7)判断节点是否存在
设置命名空间为get后,再开始测试:目前/get目录下只有node1,node2子节点。
2、curator的Watcher API(监听机制)
curator提供两种Cache(Watcher)来监听节点的变化:
NodeCache类 : 监听某个节点的变化(NodeCreate,NodeDataChanged,NodeDelete)
PathChildrenCache类:监听某个znode的子节点的变化(子节点NodeCreate,子节点NodeDeleted,子节点NodeDataChanged)
curator创建的监听器对象不是一次性的,可以重复监听。
cacheData:表示是否能够获取到子节点的数据,true表示如果监听到子节点变化,同时还可以获取子节点的数据。一般设置为true即可。
当cacheData为false后,再次创建/watcher1/node1 "node1":
3、curator的事务
事务的核心:程序要么全部执行,要么全部不执行
我们可以自己指定事务的开始和结束:
4、curator的分布式锁
curator封装了两种分布式锁:InterProcessMutex类(排它锁,只要上锁禁止任何其他客户端再上锁,又称为写锁),InterProcessReadWriteLock(读写锁,通过获得的读写锁对象分别可以获得读锁,和写锁)。对共享资源加了写锁后不允许其他任何客户端再加任何锁,对共享资源加了读锁后只允许其他客户添加读锁。
开启两个lock2客户端,可以同时加锁。先开启lock2然后开启lock3,lock2加了读锁后不允许其他客户端加写锁。先启动lock3,任务没有执行完成之前不允许任何其他客户端加任何锁。
七、zookeeper的监控命令详解
zookeeper支持一些监控命令,通过这些命令可以获得zookeeper服务的当前状态及相关信息。用户在linux下通过执行nc命令向zookeeper提交相应的监控命令,然后zookeeper会反馈一下相关信息。
# telnet命令
telnet是一个远程登录命令,可以通过它远程登录来控制别的计算机,telnet因为采用明文传送报文,安全性不好,很多Linux服务器都不开放telnet服务,而改用更安全的ssh方式了。目前telnet命令一般不用于远程连接了,一般是用于查看某个服务的相关信息。
yum -y install telnet # 安装telnet
telnet ip 端口 # 远程连接指定服务器的指定端口
通过该命令也可以得到zk的状态但不太好用(不推荐使用)
# nc命令
安装:wget http://vault.centos.org/6.6/os/x86_64/Packages/nc-1.84-22.e16.x86_64.rpm
rpm -iUv nc-1.84-22.e16.x86_64.rpm
使用:echo 监控命令 | nc zk地址 2181
如果出现以下错误:
需要在zoo.cfg文件中添加41w.commands.whitelist=*
还需要对zkServer.sh进行配置,然后重启zk服务器:
ZOOMAIN="-Dzookeeper.41w.commands.whitelist=*${ZOOMAIN}"
(1)conf命令
显示zk的配置信息,就是zoo.cfg中记录的配置信息:
(2)cons命令
显示所有连接当前zk服务器的客户端(会话)信息。
(3)crst、dump、envi命令
crst命令会重置与当前zk服务器连接的所有客户端会话信息:
dump命令用于显示所有连接到zk服务器的客户端会话id,以及各个客户端创建的临时节点路径:
envi命令显示当前zk服务器的环境配置信息:
(4)ruok、stat、srvr、srst命令
ruok命令可以测试zk服务器的运行状态:imok表示运行正常
stat命令可以显示zk服务器的详细信息:
srvr命令与stat类似:srvr不显示Clients
srst命令可以重置当前zk服务器的统计信息:
(5)wchs、wchc、wchp命令
wchs命令可以列出当前服务器上的相应监听信息:
首先创建两个客户端连接同一个zk服务器,然后在每个客户端上分别注册一个监听器。
wchc命令可以列出当前服务器上的相应监听信息,监听信息按照会话分组:首先创建两个客户端连接同一个zk服务器,然后在每个客户端上分别注册两个监听器监听两个节点。
wchp命令与wchc命令很相似,可以列出当前服务器上的相应监听信息,监听信息按照路径分组。
(6)mntr命令
mntr命令与stat相似,可以显示服务器的详细信息(健康状态)
在新版zookeeper中,上述四字命令显示的信息更多。