package com.zzhijian.zookeeperdemo.lock;
import lombok.extern.slf4j.Slf4j;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
/**
* 1: 创建持久化根节点-'/lock'
* 2:多个客户端请求时,创建临时有序节点,将当前的节点与根节点lock的最小子节点比较,
* 如果相同的话,则获取锁,否则监听比自己小的子节点,等待锁
* 3:处理业务
* 4:释放锁
* @author zhijian.zheng
* @package: com.zzhijian.zookeeperdemo.lock
* @date: 2019-08-29 09:47
**/
@Slf4j
public class ZookeeperDistributorLock implements Lock{
private static String ZK_ADDRESS = "zkServer:2181,zkServer:2182,zkServer:2183";
private static ZooKeeper ZK = null;
private static String ROOT ="/locks";
private String LOCK;
private ConcurrentHashMap<Thread,String> preNodeMap= new ConcurrentHashMap<>();
private static String CHILDREN_NODE = ROOT+"/lock_";
public ZookeeperDistributorLock(){
// 初始化zk客户端
initZookeeper(ZK_ADDRESS);
// 创建根节点持久化节点
createRootNode();
}
@Override
public void lock() {
if(tryLock()){
log.error("【{}获取锁成功!】",Thread.currentThread().getName());
// 正常来说要自己主动去释放锁,为了演示效果,获取锁之后自动释放锁
try{
Thread.sleep(1000* 3);
unlock();
}catch (Exception e){
}
}else {
// 等待锁
waitingLock(preNodeMap.get(Thread.currentThread()));
}
}
@Override
public void lockInterruptibly() throws InterruptedException {
}
@Override
public boolean tryLock() {
try {
//创建临时有序节点
String currentNode = ZK.create(CHILDREN_NODE, "0".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
log.error("[{}-开始尝试获取锁-{}]",Thread.currentThread().getName(),currentNode);
// 获取根节点子节点信息
List<String> childrenNodes = ZK.getChildren(ROOT,false);
// 排序
SortedSet<String> sortedSet = new TreeSet<>();
childrenNodes.forEach(node ->{
sortedSet.add(ROOT+"/"+node);
});
//获取最小子节点
String firstNode = sortedSet.first();
if(firstNode.equals(currentNode)){
// 如果当前节点跟最小节点相同,则获取锁
log.error("-----{}---成功获取锁--",currentNode);
LOCK = currentNode;
return true;
}
//获取当前节点中所有比自己更小的节点
SortedSet<String> lessThenMe = sortedSet.headSet(currentNode);
//如果当前所有节点中有比自己更小的节点
if (!CollectionUtils.isEmpty(lessThenMe)){
//获取比自己小的节点中的最后一个节点,设置为等待锁
preNodeMap.put(Thread.currentThread(),lessThenMe.last());
}
return false;
}catch (Exception e){
log.error(e.getMessage());
}
return false;
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return false;
}
@Override
public void unlock() {
log.error("--------释放锁--------------");
log.error("[{}->释放{}锁]",Thread.currentThread().getName(),LOCK);
try{
ZK.delete(LOCK,-1);
LOCK = null;
ZK.close();
}catch (Exception e){
log.error(e.getMessage());
}
}
@Override
public Condition newCondition() {
return null;
}
/**
* TODO: 初始化zk
* * @param address
* @author zhijian.zheng
* @return org.apache.zookeeper.ZooKeeper
* @version 1.0
* @date 2019/8/15 上午9:51
*/
public static void initZookeeper(String address){
String connectString = address;
// 会话超时时间
int sessionTimeout = 3000;
log.error("zookeeper start connecting");
try {
ZK = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent event) {
log.error("【事件被触发了 --- 连接状态:{},事件:{}】",event.getState(),event.getType());
}
});
}catch (Exception e){
log.error(e.getMessage());
}
log.error("zookeeper connection success!");
}
/**
* TODO: 创建根节点
* @author zhijian.zheng
* @return void
* @version 1.0
* @date 2019/8/15 下午1:59
*/
public static void createRootNode() {
try {
if (ZK != null) {
//判断节点是否存在
Stat stat = ZK.exists(ROOT, false);
if(ObjectUtils.isEmpty(stat)){
// 创建根节点
log.error(ZK.create(ROOT, "0".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT));
}
}
} catch (Exception e) {
log.error("【创建根节点异常-{}】",e.getMessage());
}
}
/**
* 等待锁
* @author zhijian.zheng
* @return void
* @version 1.0
* @date 2019/8/29 上午10:28
*/
private static void waitingLock(String prevNode){
try{
if(ZK != null){
CountDownLatch countDownLatch = new CountDownLatch(1);
Stat stat = ZK.exists(prevNode, new Watcher() {
@Override
public void process(WatchedEvent event) {
log.error("节点变更,要去释放锁");
countDownLatch.countDown();
}
});
if (stat!=null){
//即如果上一个节点依然存在的话
log.error(Thread.currentThread().getName()+"-->等待锁-"+prevNode+"释放。");
countDownLatch.await();
}
log.error("【{}-获取锁成功!】",Thread.currentThread().getName());
// 释放锁
}
}catch (Exception e){
log.error(e.getMessage());
}
}
public static void main(String[] args) throws Exception{
ZookeeperDistributorLock lock = new ZookeeperDistributorLock();
for(int i=0;i<10;i++){
new Thread(new Runnable() {
@Override
public void run() {
try {
lock.lock();
}catch (Exception e){
System.out.println(e.getMessage());
}
}
}).start();
}
Thread.sleep(1000 * 60 * 2);
}
}
先记录下原生zookeeper 如何处理分布式锁,后续在关注下curator 客户端是如何使用分布式锁的!
参考:https://blog.csdn.net/dongguabai/article/details/83271601