因为我用的是Eclipse,。就下载了zookeeper的插件,可以显示集群的节点情况,根据编译工具的不同自行下载
下面部分程序来自Zookeeper API,会有少许不同
java中关于zookeeper的操作方法大体分为下面这些
- connect - 连接到ZooKeeper集合
- create- 创建znode
- exists- 检查znode是否存在及其信息
- getData - 从特定的znode获取数据
- setData - 在特定的znode中设置数据
- getChildren - 获取特定znode中的所有子节点
- delete - 删除特定的znode及其所有子项
- close - 关闭连接
1、java客户端连接zookeepr集群
需要的jar包
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.14</version>
</dependency>
连接类,也算是工具类,为下面的创建节点,删除节点等做铺垫
public class ZooKeeperConnection {
// 定义zookeeper的对象
private ZooKeeper zoo;
final CountDownLatch connectedSignal = new CountDownLatch(1);
// 连接的方法
public ZooKeeper connect(String host) throws IOException,InterruptedException {
zoo = new ZooKeeper("192.168.137.131:2181",5000,new Watcher() {//这个是先连接验证的zookeeper的IP:端口
public void process(WatchedEvent we) {
if (we.getState() == KeeperState.SyncConnected) {//判定是否连接成功,执行完就会被删除,
//还有很多其他getState(),下面表格有几种
connectedSignal.countDown();//解锁
}
}
});
connectedSignal.await();//等待连接成功解锁
return zoo;
}
//关闭连接的方法
public void close() throws InterruptedException {
zoo.close();
}
}
下面是百度翻译外加自己理解,来自Enum Watcher.Event.KeeperState
连接状态 | 解释 |
---|---|
Disconnected | 断开连接(不管任何原因)返回 |
Expired | 集群已过期 |
SyncConnected | 处于连接状态(意味着连接成功,只要连接到集群的一个节点就可以) |
AuthFailed | 身份验证失败 |
2、创建节点
public class ZKCreate {
// 创建zookeeper对象
private static ZooKeeper zk;
// 创建连接对象
private static ZooKeeperConnection conn;
// 创建节点的方法
public static void create(String path, byte[] data) throws
KeeperException,InterruptedException {
//参数为地址,数据,ACL:访问控制列表,节点类型:临时顺序或两者。后两个可以参考下面的表格
zk.create(path, data, ZooDefs.Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT);
}
public static void main(String[] args) {
// 节点的路径
String path = "/MyFirstZnode";
// 数据转成byte数组
byte[] data = "My first zookeeper app".getBytes();
try {
conn = new ZooKeeperConnection();
zk = conn.connect("192.168.137.131");//在连接中想往哪个zookeeper的IP添加,
//不要以为工具类里写了这里就不用写,如果不写是不行的
create(path, data); // 根据指定节点创建节点,填充数据
conn.close(); //关闭连接
} catch (Exception e) {
System.out.println(e.getMessage()); //Catch error message
}
}
}
ZooDefs.Ids 的参数
参数名 | 解释 |
---|---|
OPEN_ACL_UNSAFE | 完全开放的ACL,任何连接的客户端都可以操作该属性znode |
CREATOR_ALL_ACL | 只有创建者才有ACL权限 |
READ_ACL_UNSAFE | 只能读取ACL |
CreateMode:节点的类型,即临时,顺序或两者
下面是参数名和及时(百度翻译请见谅)
参数名 | 解释 |
---|---|
EPHEMERAL | 当客户端断开连接时,znode将被删除。 |
EPHEMERAL_SEQUENTIAL | znode将在客户机断开连接时被删除,其名称将添加一个单调递增的数字。 |
PERSISTENT | 当客户端断开连接时,znode不会自动删除。 |
PERSISTENT_SEQUENTIAL | 当客户机断开连接时,znode不会自动删除,其名称将添加一个单调递增的数字。 |
3、检查节点是否存在
public class ZKExists {
private static ZooKeeper zk;
private static ZooKeeperConnection conn;
//检查节点是否存在,返回Stat元数据结构,这个数据结构是每个节点都有的
public static Stat znode_exists(String path) throws
KeeperException,InterruptedException {
return zk.exists(path, true); //这个查询的最重要的语句
}
public static void main(String[] args) throws InterruptedException,KeeperException {
String path = "/MyFirstZnode"; // 想要查询是否存在的节点
try {
conn = new ZooKeeperConnection();
zk = conn.connect("192.168.137.131"); //zookeeper集群的某个节点的IP
Stat stat = znode_exists(path); // 接收查询节点是否存在方法的返回值
if(stat != null) {//判定是否为空,为空说明该节点不存在
System.out.println("Node exists and the node version is " +
stat.getVersion());
} else {
System.out.println("Node does not exists");
}
} catch(Exception e) {
System.out.println(e.getMessage()); // Catches error messages
}
}
}
这里注意zk.exists(path, true)
,
有两种,这只是一种
还有一种exists(String path, Watcher watcher)
boolean类型的是是否监视这个znode,而new一个新的是覆盖前一个Watcher,肯定会监视
4、获取节点的数据data
这个是在上面判定节点是否存在的基础商行加了一些东西而已,因为上面已经获取到元数据了
public class ZKGetData {
private static ZooKeeper zk;
private static ZooKeeperConnection conn;
public static Stat znode_exists(String path) throws
KeeperException,InterruptedException {
return zk.exists(path,true);
}
public static void main(String[] args) throws InterruptedException, KeeperException {
String path = "/MyFirstZnode";
final CountDownLatch connectedSignal = new CountDownLatch(1);
try {
conn = new ZooKeeperConnection();
zk = conn.connect("192.168.137.131");
Stat stat = znode_exists(path); // 到这都没什么变化
if(stat != null) {
byte[] b = zk.getData(path, new Watcher() { //这里面可以先不用管,这是监听设置,当触发监听时里面才能执行 ,创建,删除,修改触发监听
public void process(WatchedEvent we) {
if (we.getType() == Event.EventType.None) {//连接状态发生变更
switch(we.getState()) {
case Expired: //表示客户端和服务端超过timeout了,彻底断开连接了
connectedSignal.countDown();
break;
}
} else {
String path = "/MyFirstZnode";
try {
byte[] bn = zk.getData(path,
false, null);
String data = new String(bn,
"UTF-8");//字符编码
System.out.println(data);
connectedSignal.countDown();
} catch(Exception ex) {
System.out.println(ex.getMessage());
}
}
}
}, null);
String data = new String(b, "UTF-8");
System.out.println("正常逻辑data输出的是这");
System.out.println(data);
connectedSignal.await();
} else {
System.out.println("Node does not exists");
}
} catch(Exception e) {
System.out.println(e.getMessage());
}
}
}
5、插入data(会把原来的覆盖)
public class ZKSetData {
private static ZooKeeper zk;
private static ZooKeeperConnection conn;
// 节点信息插入方法
public static void update(String path, byte[] data) throws
KeeperException,InterruptedException {
zk.setData(path, data, zk.exists(path,true).getVersion());//插入信息:路径,数据,版本
//这里的版本也可以是-1;它是递增的,不过如果你要是想模仿原子操作,就需要判定版本问题了,否则-1就可以
}
public static void main(String[] args) throws InterruptedException,KeeperException {
String path= "/MyFirstZnode";
byte[] data = "First".getBytes(); //把字符串First转成byte数组
try {
conn = new ZooKeeperConnection();
zk = conn.connect("192.168.137.131");
update(path, data); // 调用上面的修改方法
} catch(Exception e) {
System.out.println(e.getMessage());
}
}
}
这注意Version(版本)问题,更新节点对版本有要求吗?
6、获取子节点
首先创建一个父节点和子节点
下面是获得子节点的程序
public class ZKGetChildren {
private static ZooKeeper zk;
private static ZooKeeperConnection conn;
// 节点的元数据结构
public static Stat znode_exists(String path) throws
KeeperException,InterruptedException {
return zk.exists(path,true);
}
public static void main(String[] args) throws InterruptedException,KeeperException {
String path = "/MyFirstZnode"; // 父节点的路径
try {
conn = new ZooKeeperConnection();
zk = conn.connect("192.168.137.131");
Stat stat = znode_exists(path); // 调用上面的获得节点的元数据的方法
if(stat!= null) { //先判定父节点是否存在
List <String> children = zk.getChildren(path, false);//获得孩子节点的集合
for(int i = 0; i < children.size(); i++) {
System.out.println(children.get(i)); //父节点的孩子节点
}
} else {
System.out.println("Node does not exists");
}
} catch(Exception e) {
System.out.println(e.getMessage());
}
}
}
在这需要注意是三点:
zk.getChildren(path, false)
有多种参数,可以true也可以new一个Watcher- 如果想获得孩子节点的data,这样的方法就是获得名字的,还是从getData 重新获取数据吧
- 这不要以为父节点没有数据了,当成文件夹啦,它还是节点
7、删除节点
public class ZKDelete {
private static ZooKeeper zk;
private static ZooKeeperConnection conn;
// 删除方法
public static void delete(String path) throws KeeperException,InterruptedException {
zk.delete(path,zk.exists(path,true).getVersion());
}
public static void main(String[] args) throws InterruptedException,KeeperException {
String path = "/MyFirstZnode"; //节点路径
try {
conn = new ZooKeeperConnection();
zk = conn.connect("192.168.137.131");//zookeeper集群的一个节点
delete(path); //调用上面的删除方法
} catch(Exception e) {
System.out.println(e.getMessage()); // catches error messages
}
}
}
这里需要注意的:
- 删除没有子节点的节点没有问题,但是删除一个有子节点的节点会报错
KeeperErrorCode = Directory not empty for (节点路径) - 这是java客户端,不像命令行有删除节点和子节点的,对于有子节点的可以先遍历是否有子节点,删除子节点再删父节点
- 临时节点不允许有子节点,所以有子节点的肯定是永久节点
8、上面的程序中有很多涉及到Watcher的
关于监听器你可以认为是一种一次性的监听器,用过一次就会被删除,当然如果需要,用过一次删除后你可以手动再添加上,
如果有必要就加,没有就不加,这个Watcher肯定会分发到所有节点,会造成浪费
关于监听器里面的事件什么的上面程序上有很多,表格上也有很多它的参数
还有很多时间类型没写,大家可以参考ZooKeeper的Watch事件类型