Curator分布式锁源码讲解

本文详细解析了Curator实现Zookeeper分布式锁的过程,包括无终止加锁与指定时间内加锁两种方式,以及释放锁的过程。文章深入源码,介绍了如何通过Zookeeper客户端创建临时有序节点并实现加锁。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Curator加锁源码走读

如果对Curator或zookeeper加锁不明白的可以参考我的这两篇文章:
Redis分布式锁与Zk分布式锁的详解与选型
zookeeper使用过程中需要注意的坑

1. 加锁过程


// 一直尝试加锁,简称无终止
public void acquire() throws Exception {
    if (!this.internalLock(-1L, (TimeUnit)null)) {
        throw new IOException("Lost connection while trying to acquire lock: " + this.basePath);
    }
}

// 指定时间内加锁,超过指定时间如果加锁失败则放弃加锁
public boolean acquire(long time, TimeUnit unit) throws Exception {
    return this.internalLock(time, unit);
}

private boolean internalLock(long time, TimeUnit unit) throws Exception {
    // 获取当前线程名称;
    Thread currentThread = Thread.currentThread();

    // ConcurrentMap<Thread, InterProcessMutex.LockData> threadData
    // 从map中获取当前线程对应的锁相关数据;
    InterProcessMutex.LockData lockData = (InterProcessMutex.LockData)this.threadData.get(currentThread);

    //如果lockData不为空,说明当前线程已经对该key加过锁
    if (lockData != null) {
        // 加锁次数+1,表示锁重入功能;
        lockData.lockCount.incrementAndGet();
        // 返回成功;
        return true;
    } else {
        // this.getLockNodeBytes()=null;
        // 此方法尝试去加锁;
        String lockPath = this.internals.attemptLock(time, unit, this.getLockNodeBytes());
        if (lockPath != null) {
            // 返回不为null,表示加锁成功,将当前线程和LockData存到threadData map中;
            InterProcessMutex.LockData newLockData = new InterProcessMutex.LockData(currentThread, lockPath);
            this.threadData.put(currentThread, newLockData);
            return true;
        } else {
            return false;
        }
    }
}

String attemptLock(long time, TimeUnit unit, byte[] lockNodeBytes) throws Exception {
    long startMillis = System.currentTimeMillis();
    // 转换时间
    Long millisToWait = unit != null ? unit.toMillis(time) : null;
    byte[] localLockNodeBytes = this.revocable.get() != null ? new byte[0] : lockNodeBytes;
    int retryCount = 0;
    String ourPath = null;
    boolean hasTheLock = false;
    boolean isDone = false;

    while(!isDone) {
        isDone = true;

        try {
            // 这两行代码算是加锁的核心了;
            // 创建加锁节点
            ourPath = this.driver.createsTheLock(this.client, this.path, localLockNodeBytes);
            // 循环加锁
            hasTheLock = this.internalLockLoop(startMillis, millisToWait, ourPath);
        } catch (KeeperException.NoNodeException var14) {
            if (!this.client.getZookeeperClient().getRetryPolicy().allowRetry(retryCount++, System.currentTimeMillis() - startMillis, RetryLoop.getDefaultRetrySleeper())) {
                throw var14;
            }

            isDone = false;
        }
    }

    return hasTheLock ? ourPath : null;
}

// 创建加锁节点
public String createsTheLock(CuratorFramework client, String path, byte[] lockNodeBytes) throws Exception {
    String ourPath;
    if (lockNodeBytes != null) {
        ourPath = (String)((ACLBackgroundPathAndBytesable)client.create().creatingParentContainersIfNeeded().withProtection().withMode(CreateMode.EPHEMERAL_SEQUENTIAL)).forPath(path, lockNodeBytes);
    } else {
        // 判断父节点是否存在,比如我们加锁的路径:/test/lock/key
        // 会先判断key是否创建,如果没有创建,则先创建key,然后在key的路径下面创建一个临时有序节点
        ourPath = (String)((ACLBackgroundPathAndBytesable)client.create().creatingParentContainersIfNeeded().withProtection().withMode(CreateMode.EPHEMERAL_SEQUENTIAL)).forPath(path);
    }

    return ourPath;
}

private boolean internalLockLoop(long startMillis, Long millisToWait, String ourPath) throws Exception {
    boolean haveTheLock = false;
    boolean doDelete = false;

    try {
        if (this.revocable.get() != null) {
            ((BackgroundPathable)this.client.getData().usingWatcher(this.revocableWatcher)).forPath(ourPath);
        }

        // 每次循环时判断client的状态是否正常和加锁是否成功;
        while(this.client.getState() == CuratorFrameworkState.STARTED && !haveTheLock) {
            // 获取当前key下所有临时节点并升序排序;
            List<String> children = this.getSortedChildren();
            // 截取当前加锁的序号是多少
            String sequenceNodeName = ourPath.substring(this.basePath.length() + 1);
            // 判断是否当前节点是否是最小节点,如果不是则返回上线级
            PredicateResults predicateResults = this.driver.getsTheLock(this.client, children, sequenceNodeName, this.maxLeases);

            // 如果为true,说明是最小节点,表示加锁成功;
            if (predicateResults.getsTheLock()) {
                haveTheLock = true;
            } else {
                // 拼装比当前节点小1的全路径
                String previousSequencePath = this.basePath + "/" + predicateResults.getPathToWatch();
                // 至于这个synchronized在这有何意义,需要各位自行搜索:wait为什么必须要synchronized中使用;
                synchronized(this) {
                    try {
                        // 监听上一个节点
                        // 这个watcher里面会有一个方法:
                        // 如果有节点删除事件,会触发notifyAll,通知下面的wait事件;
                        // private synchronized void notifyFromWatcher() {
                        //        this.notifyAll();
                        // }

                        ((BackgroundPathable)this.client.getData().usingWatcher(this.watcher)).forPath(previousSequencePath);
                        // 如果没有传入锁获取时长,则一直等待获取;
                        if (millisToWait == null) {
                            this.wait();
                        } else {
                            // 指定了waitTime时,则超过waitTime时则不再等待;
                            // 超时后,会返回false,因为haveTheLock的值没有设置为true;
                            millisToWait = millisToWait - (System.currentTimeMillis() - startMillis);
                            startMillis = System.currentTimeMillis();
                            if (millisToWait > 0L) {
                                this.wait(millisToWait);
                            } else {
                                // 设置删除节点标识,在下面的finally只删除节点;
                                doDelete = true;
                                break;
                            }
                        }
                    } catch (KeeperException.NoNodeException var19) {
                    }
                }
            }
        }
    } catch (Exception var21) {
        ThreadUtils.checkInterrupted(var21);
        doDelete = true;
        throw var21;
    } finally {
        if (doDelete) {
            this.deleteOurPath(ourPath);
        }

    }

    return haveTheLock;
}

public PredicateResults getsTheLock(CuratorFramework client, List<String> children, String sequenceNodeName, int maxLeases) throws Exception {
    // 获取当前节点在children的位置;
    int ourIndex = children.indexOf(sequenceNodeName);
    validateOurIndex(sequenceNodeName, ourIndex);
    // 当前ourIndex下标是否<1;如果小于1,表示当前节点是最小节点;
    boolean getsTheLock = ourIndex < maxLeases;
    // 如果它不是最小节点,则返回比它小1的节点;
    String pathToWatch = getsTheLock ? null : (String)children.get(ourIndex - maxLeases);
    return new PredicateResults(pathToWatch, getsTheLock);
}

2. 释放锁过程

// 释放锁的代码相对于说就简单很多
public void release() throws Exception {
    // 获取当前线程,去map中判断当前线程是否存在;
    Thread currentThread = Thread.currentThread();
    InterProcessMutex.LockData lockData = (InterProcessMutex.LockData)this.threadData.get(currentThread);
    if (lockData == null) {
        throw new IllegalMonitorStateException("You do not own the lock: " + this.basePath);
    } else {
        // 判断锁重入
        int newLockCount = lockData.lockCount.decrementAndGet();
        if (newLockCount <= 0) {
            if (newLockCount < 0) {
                throw new IllegalMonitorStateException("Lock count has gone negative for lock: " + this.basePath);
            } else {
                try {
                    // 删除节点,删除节点时,下一个等待加锁节点会收到watcher通知
                    this.internals.releaseLock(lockData.lockPath);
                } finally {
                    this.threadData.remove(currentThread);
                }

            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值