- 环境准备
1.1 准备三台装有zookeeper集群的主机(192.168.61.134,192.168.61.130,192.168.61.135),并启动zk。
1.2 开发环境springboot,jkd1.8。 - 代码实现
2.1 连接zk工具类
package com.my.zk.zookeeperdemo.conf;
import org.apache.zookeeper.ZooKeeper;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
public class ZkUtils {
private static ZooKeeper zk;
// private static String zk_address = "192.168.61.134:2181,192.168.61.130:2181,192.168.61.135:2181/testConf";
private static String zk_address = "192.168.61.134:2181,192.168.61.130:2181,192.168.61.135:2181/testLock";
private static DefaultWatch defaultWatch = new DefaultWatch();
//countDonwLatch是为了保证zk连接成功后返回可用的zk对象,连接成功之前阻塞
private static CountDownLatch init = new CountDownLatch(1);
public static ZooKeeper getZk(){
try {
zk = new ZooKeeper(zk_address,1000,defaultWatch);
defaultWatch.setCc(init);
init.await();
} catch (Exception e) {
e.printStackTrace();
}
return zk;
}
}
2.2 监控zk连接的Watch
package com.my.zk.zookeeperdemo.conf;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import java.util.concurrent.CountDownLatch;
public class DefaultWatch implements Watcher {
CountDownLatch cc;
public void setCc(CountDownLatch cc){
this.cc = cc;
}
@Override
public void process(WatchedEvent watchedEvent) {
switch(watchedEvent.getState()){
case Unknown:
break;
case Disconnected:
break;
case NoSyncConnected:
break;
case SyncConnected:
//连接成功后减一,执行Zkutils中的await()方法
cc.countDown();
break;
case AuthFailed:
break;
case ConnectedReadOnly:
break;
case SaslAuthenticated:
break;
case Expired:
break;
}
}
}
2.3 监控节点状态的Watch
package com.my.zk.zookeeperdemo.lock;
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 WatchCallBack implements Watcher, AsyncCallback.StringCallback, AsyncCallback.ChildrenCallback , AsyncCallback.StatCallback {
ZooKeeper zk;
String threadName;
CountDownLatch cc = new CountDownLatch(1);
String childrenPathName;//格式如:/lock0000000010
public String getChildrenPathName() {
return childrenPathName;
}
public void setChildrenPathName(String childrenPathName) {
this.childrenPathName = childrenPathName;
}
public String getThreadName() {
return threadName;
}
public void setThreadName(String threadName) {
this.threadName = threadName;
}
public ZooKeeper getZk() {
return zk;
}
public void setZk(ZooKeeper zk) {
this.zk = zk;
}
public void tryLock(){
try {
//创建有序的临时节点
zk.create("/lock",threadName.getBytes(),ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL,this,"aaa");
cc.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void unLock(){
try {
zk.delete(childrenPathName,-1);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (KeeperException e) {
e.printStackTrace();
}
}
@Override
public void process(WatchedEvent watchedEvent) {
switch (watchedEvent.getType()) {
case None:
break;
case NodeCreated:
break;
case NodeDeleted:
zk.getChildren("/",false,this,"jij");
break;
case NodeDataChanged:
break;
case NodeChildrenChanged:
break;
}
}
// create callback
@Override
public void processResult(int i, String s, Object o, String s1) {
System.out.println(this.getThreadName()+ " node:"+s1);
// 如果节点创建成功
childrenPathName = s1;
if (null != s1){
// 检索/testLock根节点下的孩子,即每个/lock
// 父节点不需要监控,所以第二个参数用false,这里仅仅获取children就足够了
zk.getChildren("/",false,this,"bbb");
}
}
// getChildren callback
@Override
public void processResult(int i, String s, Object o, List<String> list) {
// zookeeper返回的序列节点的集合是乱序的,所以需要排序
Collections.sort(list);
// list中的每个children的格式如lock0000000010,没有"/",所以要截取再比较
int index = list.indexOf(childrenPathName.substring(1));
if (0 == index){
System.out.println(threadName+" i am first.....");
cc.countDown();
}else {
zk.exists("/"+list.get(index-1),this,this,"sss");
}
}
// exists callback
@Override
public void processResult(int i, String s, Object o, Stat stat) {
}
}
2.4 测试类
package com.my.zk.zookeeperdemo;
import com.my.zk.zookeeperdemo.conf.ZkUtils;
import com.my.zk.zookeeperdemo.lock.WatchCallBack;
import org.apache.zookeeper.ZooKeeper;
import org.junit.After;
import org.junit.Test;
import static org.springframework.test.context.transaction.TestTransaction.start;
public class ZkLockTests {
ZooKeeper zk;
@After
public void over(){
try {
zk.close();
System.out.println("关闭zk连接。。。。。。");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Test
public void testZkLock(){
zk = ZkUtils.getZk();
//模拟抛出十个线程
for (int i = 0; i < 10; i++) {
new Thread(()->{
WatchCallBack watchCallBack = new WatchCallBack();
watchCallBack.setZk(zk);
String threadName = Thread.currentThread().getName();
System.out.println(threadName+"线程已被创建————————————");
watchCallBack.setThreadName(threadName);
//抢锁
watchCallBack.tryLock();
//干活
System.out.println(threadName +"working......");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//释放锁
watchCallBack.unLock();
}).start();
}
while (true){
}
}
}
- 监控根节点/testLock下十个临时有序节点从有到无(锁释放)的过程,实际上刚开始是十个临时有序节点