【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

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