当前位置 博文首页 > 文章内容

    持续输出面试题之分布式锁

    作者: 栏目:未分类 时间:2020-09-22 9:00:37

    本站于2023年9月4日。收到“大连君*****咨询有限公司”通知
    说我们IIS7站长博客,有一篇博文用了他们的图片。
    要求我们给他们一张图片6000元。要不然法院告我们

    为避免不必要的麻烦,IIS7站长博客,全站内容图片下架、并积极应诉
    博文内容全部不再显示,请需要相关资讯的站长朋友到必应搜索。谢谢!

    另祝:版权碰瓷诈骗团伙,早日弃暗投明。

    相关新闻:借版权之名、行诈骗之实,周某因犯诈骗罪被判处有期徒刑十一年六个月

    叹!百花齐放的时代,渐行渐远!



    分布式锁

    1、在分布式系统环境下,一个方法在同一时间只能被一个机器的一个线程执行
    2、高可用的获取锁与释放锁
    3、高性能的获取锁与释放锁
    4、具备可重入特性(可理解为重新进入,由多于一个任务并发使用,而不必担心数据错误)
    5、具备锁失效机制,防止死锁
    6、具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失败

    基于zookeeper的分布式锁

    1.zookeeper的一些特性

    • 有序节点:假如当前有一个父节点为/lock,我们可以在这个父节点下面创建子节点;zookeeper提供了一个可选的有序特性,例如我们可以创建子节点“/lock/node-”并且指明有序,那么zookeeper在生成子节点时会根据当前的子节点数量自动添加整数序号,也就是说如果是第一个创建的子节点,那么生成的子节点为/lock/node-0000000000,下一个节点则为/lock/node-0000000001,依次类推。

    • 临时节点:客户端可以建立一个临时节点,在会话结束或者会话超时后,zookeeper会自动删除该节点。

    • 事件监听:在读取数据时,我们可以同时对节点设置事件监听,当节点数据或结构变化时,zookeeper会通知客户端。当前zookeeper有如下四种事件:
      1.节点创建;
      2.节点删除;
      3.节点数据修改;
      4.子节点变更。

    2.实现
    ①客户端连接zookeeper,并在/lock下创建临时的且有序的子节点,第一个客户端对应的子节点为/lock/lock-1,第二个为/lock/lock-2,以此类推。
    ②客户端获取/lock下的子节点列表,判断自己创建的子节点是否为当前子节点列表中序号最小的子节点,如果是则认为获得锁,否则监听/lock的子节点变更消息,获得子节点变更通知后重复此步骤直至获得锁;
    ③执行业务代码;
    ④完成业务流程后,删除对应的子节点释放锁。

    基于curator的zookeeper分布式锁实现

        public static void main(String[] args) throws Exception {
            //创建zookeeper的客户端
            RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
    
            CuratorFramework client = CuratorFrameworkFactory.newClient("10.21.41.181:2181,10.21.42.47:2181,10.21.49.252:2181", retryPolicy);
    
            client.start();
    
            //创建分布式锁, 锁空间的根节点路径为/curator/lock
            InterProcessMutex mutex = new InterProcessMutex(client, "/curator/lock");
    
            mutex.acquire();
    
            //获得了锁, 进行业务流程
            System.out.println("Enter mutex");
    
            //完成业务流程, 释放锁
            mutex.release();
    
            //关闭客户端
            client.close();
    
        }
    

    基于redis的分布式锁

    1.流程
    1、服务A为了获得锁,向Redis发起如下命令: SET productId:lock 0xx9p03001 NX PX 30000 其中,"productId"由自己定义,可以是与本次业务有关的id,"0xx9p03001"是一串随机值,必须保证全局唯一(原因在后文中会提到),“NX"指的是当且仅当key(也就是案例中的"productId:lock”)在Redis中不存在时,返回执行成功,否则执行失败。"PX 30000"指的是在30秒后,key将被自动删除。执行命令后返回成功,表明服务成功的获得了锁。
    2、服务B为了获得锁,向Redis发起同样的命令: SET productId:lock 0000111 NX PX 30000 由于Redis内已经存在同名key,且并未过期,因此命令执行失败,服务B未能获得锁。服务B进入循环请求状态,比如每隔1秒钟(自行设置)向Redis发送请求,直到执行成功并获得锁。
    3、服务A的业务代码执行时长超过了30秒,导致key超时,因此Redis自动删除了key。此时服务B再次发送命令执行成功,假设本次请求中设置的value值为0000222。
    4、服务A执行完毕,为了释放锁,服务A会主动向Redis发起删除key的请求。

    2.实现
    加锁
    客户端集成Redisson,在加锁之前,首先需要通过hash算法选定集群内某一个Redis Master,后续加锁、解锁等各种过程都是在这个Redis Master和与之绑定的slave节点之间。
    执行lua脚本实现加锁
    watch dog自动延期
    watch dog是一个后台线程,它会每隔10秒观察当前客户端是否仍然持有锁,如果持有,说明客户端可能仍然在使用锁,因此延长锁的剩余生存时间。
    释放锁机制 如果执行lock.unlock(),Redis会找到上方test数据结构,将加锁次数减一。如果减完后发现加锁次数为0,则说明当前客户端不再持有锁,因此执行: del test命令, 从Redis中删除这条key。

    // 准备为名为"test"的key加锁
    RLock lock = redisson.getLock("test");
    // 加锁
    lock.lock();
    // 解锁
    lock.unlock();