ZooKeeper - 分布式锁

 ZooKeeper实现分布式锁的优点:ZK可以创建持久节点、临时节点和顺序节点。临时节点在会话结束之后,自动被删除。即使发生宕机,只要超出心跳时间,就会断开会话,从而删除临时节点,不会造成死锁的现象。


  1.  指定竞争的资源,在ZK下生成持久节点。
  2. 在持久节点下,生成若干临时顺序节点,尝试获取锁。
  3. 判断该节点是否是序号最小的节点,若是,则获取锁;若不是,则阻塞。
  4. 通过ZK的Watcher监听上一个节点的事件,满足要求则解除阻塞。重复3操作。
  5. 最后所有的节点都获得了锁,再断开ZK的会话连接,删除所有临时节点。
package com.mzs.lock;

import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.logging.Logger;

public class DistributeLock implements Watcher, Lock {

    private static Logger logger = Logger.getLogger("com.mzs.lock.DistributeLock");
    // 根节点路径
    private final String path = "/root";
    private ZooKeeper zooKeeper;
    // 会话超时时间
    private final static int SESSION_TIME_OUT = 50000;
    // 当前锁
    private String currentLock;
    // 上一个锁
    private String waitLock;
    // 竞争的资源
    private String lockName;
    private CountDownLatch countDownLatch;

    public DistributeLock(String url, String lockName) {
        this.lockName = lockName;
        try {
            // 建立zookeeper会话
            zooKeeper = new ZooKeeper(url, SESSION_TIME_OUT, this);
            // 根节点是否存在
            Stat stat = zooKeeper.exists(path, false);
            if (stat == null) {
                // 创建持久节点,并拥有所有权
                zooKeeper.create(path, "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (KeeperException e) {
            e.printStackTrace();
        }
    }

    /**
     * 加锁(普通获取或者重入获取)
     */
    @Override
    public void lock() {
        // 成功获取
        if (tryLock())
            logger.info("[" + Thread.currentThread().getName() + "] --- get --- [" + lockName + "]");
        else {
            try {
                // 等待获取
                waitLock(waitLock, SESSION_TIME_OUT);
            } catch (KeeperException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 等待锁
     * @param waitTime 超时时间
     * @return 是否成功等待
     */
    private boolean waitLock(String waitLock, long waitTime) throws KeeperException, InterruptedException {
        Stat stat = zooKeeper.exists(path + "/" + waitLock, true);
        // 等待锁中有节点与之对应
        if (stat != null) {
            countDownLatch = new CountDownLatch(1);
            try {
                logger.info("[" + Thread.currentThread().getName() + "] --- wait --- [" + path + "/" + waitLock + "]");
                // 等待时长为waitTime毫秒
                boolean bool = countDownLatch.await(waitTime, TimeUnit.MILLISECONDS);
                String curentThreadName = Thread.currentThread().getName();
                logger.info("[" + curentThreadName + "] --- " + (!bool ? "wait for timeout,stop to get the lock" : "got the lock in the valid time"));
                // 等待结束
                countDownLatch = null;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return true;
    }

    /**
     * 加锁(优先考虑中断)
     *
     * @throws InterruptedException 被中断异常
     */
    @Override
    public void lockInterruptibly() throws InterruptedException {
        lock();
    }

    /**
     * 尝试获得锁
     * @return 是否获得锁
     */
    @Override
    public boolean tryLock() {
        String separateStr = "/";
        String separateStr2 = "-";
        // 临时顺序节点的路径
        String path_1 = path + separateStr + lockName + separateStr2;
        try {
            // 创建临时顺序节点
            currentLock = zooKeeper.create(path_1, "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
            logger.info("[" + currentLock + "] --- " + " was created");
            // 定义一个根节点下所有子节点组成的列表
            List<String> nodeChildren = zooKeeper.getChildren(path, false);
            // 定义一个存储包含lockName的节点的列表
            List<String> nodeList = new ArrayList<>();
            // 从nodeChildren中取出所有包含lockName的所有节点,加入到nodeList中
            for (String nodeElem : nodeChildren) {
                String node = nodeElem.split(separateStr2)[0];
                if (node.equals(lockName)) {
                    nodeList.add(nodeElem);
                }
            }
            // 排序nodeList(从小到大)
            Collections.sort(nodeList);
            logger.info("[" + Thread.currentThread().getName() + "] --- corresponding the lock [" + currentLock + "]");
            // 若当前锁对应的是最小节点,则认为取得了锁
            if (currentLock.equals(path + separateStr + nodeList.get(0))) {
                return true;
            }
            // 通过当前锁查看它所对应的节点
            String currentNode = currentLock.substring(currentLock.lastIndexOf(separateStr) + 1);
            // 二分查找法查找上一个节点
            int index = Collections.binarySearch(nodeList, currentNode) - 1;
            // 将上一个节点标记一个等待锁
            waitLock = nodeList.get(index);
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 表示获取锁失败
        return false;
    }

    /**
     * 超时时间内,尝试获得锁
     *
     * @param time 超时时间
     * @param unit 时间单位
     * @return 是否成功获得锁
     */
    @Override
    public boolean tryLock(long time, TimeUnit unit) {
        try {
            // 已经成功获取锁
            if (tryLock())
                return true;
            // 等待获取锁
            return waitLock(waitLock, time);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 释放锁
     */
    @Override
    public void unlock() {
        try {
            // 删除当前锁对应的节点
            zooKeeper.delete(currentLock, -1);
            logger.info("[" + Thread.currentThread().getName() + "] --- release --- [" + currentLock + "]");
            // 释放当前锁
            currentLock = null;
            zooKeeper.close();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (KeeperException e) {
            e.printStackTrace();
        }
    }

    @Override
    public Condition newCondition() {
        return null;
    }

    /**
     * 监听节点
     * @param event 节点事件
     */
    @Override
    public void process(WatchedEvent event) {
        if (countDownLatch != null) {
            countDownLatch.countDown();
        }
        /*if (Event.KeeperState.SyncConnected == event.getState()) {
            if (Event.EventType.None == event.getType() && event.getPath() == null) {
                logger.info("establish the session");
            } else if (Event.EventType.NodeCreated == event.getType()) {
                logger.info("node [" + event.getPath() + "] was created");
            } else if (Event.EventType.NodeDeleted == event.getType()) {
                logger.info("node [" + event.getPath() + "] was deleted");
            } else if (Event.EventType.NodeDataChanged == event.getType()) {
                logger.info("node [" + event.getPath() + "] data changed");
            } else if (Event.EventType.NodeChildrenChanged == event.getType()) {
                logger.info("node [" + event.getPath() + "] children node changed");
            }
        }*/
    }
}

package com.mzs.lock;

import java.util.logging.Logger;

public class TestDistributeLock {

    private static Logger logger = Logger.getLogger("com.mzs.lock.TestDistributeLock");

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    DistributeLock distributeLock = null;
                    try {
                        distributeLock = new DistributeLock("192.168.101.199:2181", "lock");
                        distributeLock.lock();
                        logger.info("[" + Thread.currentThread().getName() + "] --- running" );
                    } finally {
                        if (distributeLock != null)
                            distributeLock.unlock();
                    }
                }
            });
            thread.start();
        }
    }

}

代码选自 --- https://www.cnblogs.com/liuyang0/p/6800538.html 

猜你喜欢

转载自blog.csdn.net/qq_34561892/article/details/83991799