【zookeeper 原生api 实现分布式锁】

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章