zookeeper——分布式锁的基本原理与基本实现(基于zookeeper自带Java客户端)

实现思路

实现的分布式锁的最常见方式是使用redis分布式锁(redis分布式锁参考我的专栏——分布式锁),基本原理是在各个服务执行到临界区(造作共享资源的代码块)之前,去redis中尝试添加一个带有当前贡献资源标识的redis数据,如果redis中没有该键值,添加成功,代表加锁成功,如果redis中已经存在该键值,添加失败,则加锁失败。

而zookeeper的原理就完全不一样了:

  1. 首先,每个客户端会去某个指定的znode(一般情况,一类分布式锁都创建在某一个node path下)下创建临时有序节点,所以每个客户端创建出来的临时节点都会按照序列分配序号如/locks/lock_000000001、/locks/lock_000000002、/locks/lock_000000003.......
  2. 客户端获取到/lock下的子节点,并进行排序,判断排在最前面的节点是否是自己创建的节点,如果是,那么就获取到锁。
  3. 如果自己创建的节点并不在第一个,则监听前一个节点
  4. 前一个节点的客户端执行完毕,释放锁之后,就会触发监听,通知到下一个节点的客户端
  5. 监听客户端重新执行第2步动作,尝试获取锁

基本实现

本文的demo示例仅作为zookeeper分布式锁的原理分析示例!!!

引入zookeeper自带Java客户端

<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.7.0</version>
</dependency>

编写示例

package org.leolee.zookeeper.distributedLock;

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

import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;

/**
 * @ClassName MyLock
 * @Description: 分布式锁
 * @Author LeoLee
 * @Date 2021/4/14
 * @Version V1.0
 **/
public class MyLock {

    private static final String IP = "127.0.0.1:2181";

    CountDownLatch countDownLatch = new CountDownLatch(1);

    ZooKeeper zooKeeper;

    private static final String LOCK_ROOT_PATH = "/locks";

    private static final String LOCK_NODE_NAME = "lock_";

    private String lockPath;

    //连接zookeeper
    public MyLock() {
        try {
            zooKeeper = new ZooKeeper(IP, 5000, new Watcher() {
                @Override
                public void process(WatchedEvent watchedEvent) {
                    if (watchedEvent.getState().equals(Event.KeeperState.SyncConnected)) {
                        System.out.println("connect success");
                        countDownLatch.countDown();
                    } else if (watchedEvent.getState().equals(Event.KeeperState.Disconnected)) {
                        System.out.println("connection break");
                    } else if (watchedEvent.getState().equals(Event.KeeperState.Expired)) {
                        System.out.println("session timeout");
                    } else if (watchedEvent.getState().equals(Event.KeeperState.AuthFailed)) {
                        System.out.println("auth failed");
                    }
                }
            });
            countDownLatch.await();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    Watcher watcher = new Watcher() {
        @Override
        public void process(WatchedEvent watchedEvent) {
            //监视的前一个节点被删掉后通知
            if (watchedEvent.getType().equals(Event.EventType.NodeDeleted)) {
                synchronized (watcher) {
                    watcher.notifyAll();
                }
            }
        }
    };

    /**
     * 功能描述: <br>
     * 〈〉获取锁
     * @Param: []
     * @Return: void
     * @Author: LeoLee
     * @Date: 2021/4/14 23:00
     */
    public void acquireLock() throws KeeperException, InterruptedException {
        createLock();
        tryLock();
    }

    private void createLock() throws KeeperException, InterruptedException {
        //判断锁根节点是否存在
        Stat stat = zooKeeper.exists(LOCK_ROOT_PATH, false);
        if (stat == null) {
            zooKeeper.create(LOCK_ROOT_PATH, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        }
        //创建锁节点(临时有效节点)
        lockPath = zooKeeper.create(LOCK_ROOT_PATH + "/" + LOCK_NODE_NAME, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
        System.out.println("lock node create success, lock path:" + lockPath);
    }

    private void tryLock() throws KeeperException, InterruptedException {
        List<String> children = zooKeeper.getChildren(LOCK_ROOT_PATH, false);
        //对子节点进行排序
        Collections.sort(children);
        int index = children.indexOf(lockPath.substring(LOCK_ROOT_PATH.length() + 1));
        if (index == 0) {
            System.out.println("try lock success");
            return;
        } else {
            //获取上一个节点
            String preNode = children.get(index - 1);
            //对上一个节点进行watch
            Stat preNodeStat = zooKeeper.exists(LOCK_ROOT_PATH + "/" + preNode, watcher);
            if (preNodeStat == null) {
                acquireLock();
            } else {
                synchronized (watcher) {
                    watcher.wait();
                }
            }
        }
    }

    /**
     * 功能描述: <br>
     * 〈〉释放锁
     * @Param: []
     * @Return: void
     * @Author: LeoLee
     * @Date: 2021/4/14 23:01
     */
    public void releaseLock() throws KeeperException, InterruptedException {
        //删除临时有序节点
        zooKeeper.delete(lockPath, -1);
        System.out.println("lock release");
        zooKeeper.close();
    }
}

 

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值