zookeeper的数据结构
zookeeper的观察器
- 可设置观察器的3个方法:getData();getChildren();exists();
- 节点数据发生变化,发送给客户端
- 观察器只能监控一次,再监控重新设置
zookeeper分布式锁原理
- 利用zookeeper的瞬时有序节点的特性
- 多线程并发创建瞬时节点时,得到有序的序列
- 序号最小的线程获得锁
- 其他的线程则监听自己序号的前一个序号
- 前一个线程执行完成,删除自己序号的节点
- 下一个序号的线程得到通知,继续执行
- 以此类推
- 创建节点时,已经确定了线程的执行顺序
代码实现
@Slf4j
public class ZkLock implements Watcher,AutoCloseable {
private ZooKeeper zooKeeper;
private String businessName;
private String znode;
public ZkLock(String connectString,String businessName) throws IOException {
this.zooKeeper = new ZooKeeper(connectString,30000,this);
this.businessName = businessName;
}
public boolean getLock() throws KeeperException, InterruptedException {
Stat existsNode = zooKeeper.exists("/" + businessName, false);
if (existsNode == null){
// 创建持久节点,用于区分业务
zooKeeper.create("/" + businessName,businessName.getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT);
}
// 创建瞬时节点
znode = zooKeeper.create("/" + businessName + "/" + businessName + "_", businessName.getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE,
CreateMode.EPHEMERAL_SEQUENTIAL);
// 获取瞬时节点名称
znode = znode.substring(znode.lastIndexOf("/")+1);
// 获取持久节点下的所有瞬时节点
List<String> childrenNodes = zooKeeper.getChildren("/" + businessName, false);
// 瞬时节点排序
Collections.sort(childrenNodes);
// 获取第一个瞬时节点
String firstNode = childrenNodes.get(0);
if (!firstNode.equals(znode)){
String lastNode = firstNode;
for (String node:childrenNodes){
if (!znode.equals(node)){
lastNode = node;
}else {
zooKeeper.exists("/"+businessName+"/"+lastNode,true);
break;
}
}
synchronized (this){
wait();
}
}
return true;
}
@Override
public void process(WatchedEvent watchedEvent) {
if (watchedEvent.getType() == Event.EventType.NodeDeleted){
synchronized (this){
notify();
}
}
}
@Override
public void close() throws Exception {
zooKeeper.delete("/"+businessName+"/"+znode,-1);
zooKeeper.close();
log.info("我释放了锁");
}
}
测试
@RequestMapping("zkLock")
public String zkLock(){
log.info("我进入了方法!");
try (ZkLock zkLock = new ZkLock("192.168.2.251:2181","order")){
if (zkLock.getLock()) {
log.info("我进入了锁!!");
Thread.sleep(15000);
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
log.info("方法执行完成");
return "方法执行完成";
}
localhost:9998/zkLock
localhost:9999/zkLock