zookeeper实现分布式锁的原理及具体使用案例

zookeeper跟redis一样,也是基于内存的。

官网:

http://zookeeper.apache.org/

zookeeper是分布式系统的协调服务,提供配置管理、分布式协同、命名的中心化服务以及服务注册发现等。

zookeeper分布式锁的实现原理:

zookeeper实现分布式锁采用其提供的有序临时节点+监听来实现。临时节点只要客户端断开连接就会被删除,正好可以利用这一特性实现锁。

zookeeper是由java语言开发的。

zookeeper中的节点类型:

节点就是用于存储数据的,在zookeeper中,节点是类似于文件系统的目录的结构。

①永久节点。会进行持久化,重启不会丢失。

②临时节点。存储在内存中,不会持久化,重启服务或断开连接数据会丢失。

节点中存储的数据可以进行排序。

当节点中的数据有更新或是节点被删除时,会触发zookeeper的通知机制告知客户端,因为zookeeper一直在监听所有的节点。

领红包具体案例:

【具体业务逻辑是:先查询下数据库,判断用户的红包数据是否为空,是则插入红包数据,否则不让插入】

①引入zkclient依赖:

        <!--zookeeper客户端zkclient-->
        <!-- https://mvnrepository.com/artifact/com.101tec/zkclient -->
        <dependency>
            <groupId>com.101tec</groupId>
            <artifactId>zkclient</artifactId>
            <version>0.11</version>
        </dependency>

②代码实现:

接口:

package com.zhangxueliang.demo.springbootdemo.zookeeperLock;

public interface ZookeeperLock {

    //加锁
    void lock();

    //解锁
    void unlock();


}

 抽象类:

package com.zhangxueliang.demo.springbootdemo.zookeeperLock;

import org.I0Itec.zkclient.ZkClient;

import java.util.concurrent.CountDownLatch;

/**
 * @ProjectName springbootdemo_src
 * @ClassName AbstractZookeeperLock
 * @Desicription tryLock和waitLock方法使用到了模板方法设计模式
 * @Author Zhang Xueliang
 * @Date 2020/1/13 14:19
 * @Version 1.0
 **/
public abstract class AbstractZookeeperLock implements ZookeeperLock {

    protected String lock;
    protected CountDownLatch countDownLatch;//门闩(倒计数器)

    protected String zk_address = "127.0.0.1:2181";
    protected ZkClient zkClient = new ZkClient(zk_address);

    /**
     * 加锁
     */
    @Override
    public final void lock() {
        //尝试获取锁
        if (tryLock()){
            //已经拿到锁
            System.out.println("获取锁成功。。。");
        }else {
            //尝试获取锁未成功,等待获取锁,阻塞,如果此处已经不阻塞了,那么可以继续往下执行代码
            waitLock();
            //上面的阻塞结束了,那么我可以继续获取锁,递归一下
        }
    }

    /**
     * 释放锁
     */
    @Override
    public final void unlock() {
        //关闭连接就解锁了
        if(zkClient!=null){
            zkClient.close();//此处也可以使用zkClient.delete(临时节点)来实现解锁
            System.out.println("解锁成功。。。");
        }

    }

    //尝试获取锁
    protected abstract boolean tryLock();

    //等待获取锁
    protected abstract void waitLock();
}

锁实现类:

package com.zhangxueliang.demo.springbootdemo.zookeeperLock;

import org.I0Itec.zkclient.IZkDataListener;

import java.util.concurrent.CountDownLatch;

/**
 * @ProjectName springbootdemo_src
 * @ClassName ZookeeperDistributedLock
 * @Desicription TODO
 * @Author Zhang Xueliang
 * @Date 2020/1/13 14:32
 * @Version 1.0
 **/
public class ZookeeperDistributedLock extends AbstractZookeeperLock {

    public ZookeeperDistributedLock(String lockName){
        lock=lockName;
    }

    /**
     * 尝试获取锁
     * @return
     */
    @Override
    protected boolean tryLock() {
        try {
            //创建一个临时节点
            zkClient.createEphemeral(lock);
            return true;
        }catch (Exception e){
            return false;
        }
    }

    /**
     * 等待获取锁
     */
    @Override
    protected void waitLock() {
        //如果已经有线程创建了临时节点,那么其他线程就只能等待,不能再创建该临时节点
        //此时我就监听(就是订阅数据的改变)你这个临时节点,如果该节点被删除了我就等待结束,就又可以创建临时节点

        //1.订阅数据改变,就是指定监听参数中所指定的那个节点
        IZkDataListener listener = new IZkDataListener() {
            //监听改变
            @Override
            public void handleDataChange(String s, Object o) throws Exception {

            }

            //监听删除
            @Override
            public void handleDataDeleted(String s) throws Exception {
                if (countDownLatch!=null){
                    countDownLatch.countDown();
                }
            }
        };
        zkClient.unsubscribeDataChanges(lock, listener);

        //2.判断锁的节点是否存在
        if (zkClient.exists(lock)){
             countDownLatch = new CountDownLatch(1);
            try {
                //当计数器从1变成0之后,等待就会结束了
                countDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        //3.取消订阅
        zkClient.unsubscribeDataChanges(lock,listener);
    }
    
}

zookeeper分布式锁代码测试:

    //测试
    public static void main(String[] args) {
        ZookeeperDistributedLock zookeeperDistributedLock = new ZookeeperDistributedLock("/addRedPacket");//一定要带/ 不然节点添加不成功
        try{
            zookeeperDistributedLock.lock();
            //TODO 具体要锁住的业务代码
            //查询用户是否已经领过红包
            //添加红包给用户
        }finally {
            zookeeperDistributedLock.unlock();
        }
    }

测试时需要将zoo_sample.cfg配置文件中的zookeeper最大连接数调大些:

maxClientCnxns=1000 #默认60

启动两个端口:

配置nginx.conf实现负载均衡

使用jmeter测试时访问的是nginx的80端口:

使用jmeter模拟高并发场景:

相关参数配置--

发布了744 篇原创文章 · 获赞 338 · 访问量 77万+

猜你喜欢

转载自blog.csdn.net/a772304419/article/details/103956973
今日推荐