Zookeeper分布式锁

Zookeeper分布式锁

为什么要用分布式锁

​ 随着微服务,分布式系统的时代,这种线程间的锁机制就不能跨系统,跨服务,跨多个jvm起作用了,因为系统可能会有多分或者布置到多态服务器上,多个系统访问共享资源,如果还是采用线程锁是无法实现多个系统的互斥排他的,这是跨jvm的同步,为了解决跨服务,跨系统,跨多个jvm的锁同步,我们就会用到分布式锁.

什么是分布式锁

​ 是指在分布式部署环境下,控制分布式系统之间对共享资源的访问的一种方式,考虑到多个系统对共享资源的操作会出现数据的混乱,这是便引入分布式锁的概念,来解决多个系统下对共享资源操作出现数据混乱的问题.

实现分布式锁

第一种:基于数据库实现分布式锁(早期使用的)

思路:

1.创建一张表lock

create table lock(
	id
    method name (唯一的约束)
    ......
);

2.获得锁向表中插入数据,由于有唯一约束,相同的数据只能有一条,即只有一个线程会插入成功然后获得锁,可以继续操作,没有插入成功的就没有获得锁

3.删除数据释放锁

存在的问题:

1.可用性比较差,数据库挂了就没法玩了

2.数据库存在瓶颈,不适合高并发场景

3.锁的失效事件难以控制

第二种:基于redis的分布式锁

思路:

1.获得锁

setnx 命令加锁,并且设置有效时间(避免死锁),身份value值(解决自己加锁,自己解锁)

expire 命令设置过期时间

2.释放锁

delete:解锁

基于redis实现分布式锁,采用一个开源项目,里面其实是jar包,下载下来就能使用,简单的很.

第三种:基于zookeeper的分布式锁

核心思路

1.获得锁

2…释放锁

3.zookeeper的临时节点

​ 会话结束后,临时节点会自动自己删除节点,zookeeper的节点类型四种(短暂和持久各两种)

4.zookeeper的监听机制

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l7OGpJKk-1577065874668)(D:\总结\分布式事务和锁\img\监听机制.png)]

5.保证多个jvm只有一个jvm对共享资源的访问

6.读用共享锁(读锁),写用排他锁(写锁)

问题

1.避免死锁

2.自己加的锁自己解锁

3.高可用:即性能要求

代码实现

1.定义一个接口

public interface ZookeeperLock {
    //获得锁
    public void lock();
    //释放锁
    public void unlock();
}

2.创建一个抽象类去实现接口

public abstract class AbstractZookeeperLock implements ZookeeperLock {
    protected String connectStr = "39.97.252.228:2181,39.97.252.228:2182,39.97.252.228:2183";
    protected ZkClient zkClient = new ZkClient(connectStr);
    protected String lock = "/mylock";
    //倒计数器,例如4,3,2,1,0,只有到0才会执行
    protected CountDownLatch countDownLatch;
    /**
     * 获得锁
     */
    @Override
    public final void lock() {
        //尝试获得锁
        if(tryLock()){
            System.out.println("获得锁成功-------------");
        }else{
            //获得锁失败,等你带获取锁,阻塞
            waitLock();
            //如果此处不阻塞.递归
            lock();
        }
    }

    /**
     * 让子类去实现,等待获取锁
     */
    protected abstract void waitLock();

    /**
     * 尝试获取锁
     * @return
     */
    protected abstract boolean tryLock();
    /**
     * 释放锁
     */
    @Override
    public final void unlock() {
        //断开连接,自动删除短暂节点
        if(zkClient != null){
            zkClient.close();
        }
        System.out.println("释放锁成功!--------------");
    }
}

3.创建类继承AbstractZookeeperLock实现waitLock()和tryLock()方法

public class ZookeeperDistributedLock extends AbstractZookeeperLock {

    public ZookeeperDistributedLock(String lockName){
        super.lock = lockName;
    }
    /**
     * 等待获取锁
     */
    @Override
    protected void waitLock() {
        /**
         * 如果其他线程创建了临时节点,其他节点不能创建
         * 监听此节点的删除状态
         */
        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){
                    //到计数器减1
                    countDownLatch.countDown();
                }
            }
        };
        //订阅数据变化
        zkClient.subscribeDataChanges(lock, listener);

        //判断锁的节点是否存在
        if(zkClient.exists(lock)){
            //为倒计数器赋值
            countDownLatch = new CountDownLatch(1);
            try {
                countDownLatch.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

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

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

注意:倒计数器的使用

测试场景:凡是有高并发访问共享资源的,可以给原子操作加锁和释放锁
需要注意的是feign远程调用服务需要把全局事务带到下游的事务需要在feign的远程调用方法上加@Hmily注解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值