All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.zoi7.component.zookeeper.ZkDistributedLock Maven / Gradle / Ivy

There is a newer version: 2.2.0
Show newest version
package com.zoi7.component.zookeeper;

import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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;

/**
 *
 * 基于Zookeeper实现的分布式锁
 *
 * @author yjy
 * 2018-10-23 11:15
 */
public class ZkDistributedLock implements Lock, Watcher {

    private static final Logger log = LoggerFactory.getLogger(ZkDistributedLock.class);

    private static final String ROOT_NAME = "/lockRoot"; // 分布式锁根节点
    private static final  String FIX = "_seq_";
    private ZooKeeper zooKeeper;
    private String lockName; // 锁节点
    private String waitingLock; // 等待锁
    private String currentLock = null; // 当前锁
    private CountDownLatch countDownLatch = null;

    public ZkDistributedLock(String lockName) throws InterruptedException, IOException, KeeperException {
        try {
            zooKeeper = ZkUtils.getInstance().getZooKeeper(this);
            Stat stat = zooKeeper.exists(ROOT_NAME, false);
            if (stat == null) {
                zooKeeper.create(ROOT_NAME, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
            }
            this.lockName = lockName;
        } catch (InterruptedException | KeeperException | IOException e) {
            log.error("ZkDistributedLock Instantiation failed", e);
            if (zooKeeper != null) {
                zooKeeper.close();
            }
            throw e;
        }
    }

    /**
     * 获取锁, 如果当前非空闲, 则等待锁释放
     *
     * 由于 lock(); 方法如果在遇到阻塞情况将会一直等待下去(等待时间为Long.MAX_VALUE毫秒), 这可能会导致意想不到的情况
     * 建议使用 tryLock(time, unit);
     */
    @Override
    public void lock() {
        if (this.tryLock()) {
            log.debug("ZkDistributedLock lock > {} success", this.lockName);
            return;
        }
        try {
            waitLock(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
        } catch (KeeperException | InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 尝试获取锁, 如果当前非空闲, 则等待, 如果在指定时间内还是未获取到锁, 则获取锁失败
     * @param time 时间
     * @param unit 时间单位
     * @return 是否获取成功
     * @throws InterruptedException e
     */
    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        if (tryLock()) {
            return true;
        }
        try {
            return waitLock(time, unit);
        } catch (KeeperException e) {
            log.error("tryLock > waitLock error", e);
        }
        return false;
    }

    /**
     * 尝试获取锁, 直接返回是否成功
     * @return 是否成功获取锁
     */
    @Override
    public boolean tryLock() {
        if (lockName.contains(FIX)) {
            throw new IllegalArgumentException("wrong lockName: " + lockName + ", can not contains chars : _seq_");
        }
        try {
            // 创建临时节点
            String node = ROOT_NAME + "/" + lockName + FIX;
            currentLock = zooKeeper.create(node, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
            // 查看前方是否有锁
            waitingLock = getWaitingLock();
            if (waitingLock == null) {
                return true;
            }
        } catch (KeeperException | InterruptedException e) {
            log.error("tryLock error", e);
        }
        return false;
    }

    /**
     * 等待锁释放
     * @param waitTime 等待时间
     * @param unit 时间单位
     * @return 是否成功等到锁释放
     * @throws KeeperException e
     * @throws InterruptedException e1
     */
    private boolean waitLock(long waitTime, TimeUnit unit) throws KeeperException, InterruptedException {
        if (waitingLock != null) {
            log.info("waitLock > currentLock: {}, waitingLock: {}, waitTime: {}, timeUnit: {}",
                    currentLock, waitingLock, waitTime, unit);
            Stat stat = zooKeeper.exists(ROOT_NAME + "/" + waitingLock, true);
            if (stat != null) {
                countDownLatch = new CountDownLatch(1);
                // if 前方锁释放
                if (countDownLatch.await(waitTime, unit)) {
                    // 还要判断当前前方是否有锁
                    waitingLock = getWaitingLock();
                    return waitLock(waitTime, unit);
                } else {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * 找出排在当前锁前方的锁, 如果当前锁已经在最前, 则返回null
     * @return 锁名称
     * @throws KeeperException
     * @throws InterruptedException
     */
    private String getWaitingLock() throws KeeperException, InterruptedException {
        // 获取根节点的所有子节点
        List children = zooKeeper.getChildren(ROOT_NAME, false);
        // 找出所有当前锁(lockName)的顺序节点
        List curChildren = new ArrayList<>();
        for (String child : children) {
            if (lockName.equals(child.split(FIX)[0])) {
                curChildren.add(child);
            }
        }
        // 排序
        Collections.sort(curChildren);
        // 找出本次锁所在的位置
        int index = curChildren.indexOf(currentLock.substring(ROOT_NAME.length() + 1));
        // 如果在第一, 那么获取锁成功
        if (index == 0) {
            return null;
        }
        // 否则记录下排在它前面的锁
        return curChildren.get(index - 1);
    }

    @Override
    public void lockInterruptibly() {
        this.lock();
    }

    /**
     * 释放当前锁
     */
    @Override
    public void unlock() {
        try {
            if (currentLock != null) {
                zooKeeper.delete(currentLock, -1);
                log.info("unlock > currentLock: {}", currentLock);
            }
            if (zooKeeper != null) {
                zooKeeper.close();
            }
        } catch (InterruptedException | KeeperException e) {
            log.error("unlock error, currentLock: " + currentLock, e);
        }
    }

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

    /**
     * 监听锁释放事件
     * @param event 事件
     */
    @Override
    public void process(WatchedEvent event) {
        if (event.getType().equals(Event.EventType.NodeDeleted)
                && event.getPath().equals(ROOT_NAME + "/" + waitingLock)) {
            if (this.countDownLatch != null) {
                this.countDownLatch.countDown();
            }
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy