在上一篇中,我们讲了使用ZooKeeper来实现分布式作业调度系统的原理与实现,链接为 ZooKeeper完全解析(四) 使用ZooKeeper实现分布式作业调度系统之实现原理 ZooKeeper完全解析(五) 使用ZooKeeper实现分布式作业调度系统之Java实现 这一节我们讲一下ZooKeeper实现分布式锁的原理。
一、使用Redis实现分布式锁的原理以及对应缺陷:
在讲解ZooKeeper实现分布式锁的原理之前,我们先回顾一下Redis来实现分布式锁的原理:
我们在获取锁的时候给一个key setNX一个value,如果setNX成功,那么说明我们拿到了分布式锁,如果没有set成功,那么说明没有拿到分布式锁,系统需要循环去setNX,如果setNX成功,那么说明拿到了。
之前写过相关的博客,链接为 分布式锁的原理与实现 一、分布式锁的原理 ,分布式锁的原理与实现 二、分布式锁的基础实现 。
那么,这样实现有什么问题呢?首先一点就是很难实现排队机制,即先入先出,即先开始竞争获取锁的,应该先拿到锁,而上述是每个线程都在循环setNX,很可以出现后来的竞争获取锁的,先拿到了锁。其次,如果多个线程都在一直setNX,对系统也是一个负担。
二、使用ZooKeeper实现分布式锁的原理:
ZooKeeper在设置节点的时候,允许我们来设置一个顺序节点,而且我们能监听某一个节点的删除状态以及数据更新状态。这就为我们事先分布式锁提供了基础。
比如我们要抢购一个商品,那么抢购这个商品的时候需要使用分布式锁,而且我们也想实现先抢的人先获取到分布式锁,就可以这样:
1、首先创建 /lock 节点,如果创建成功则不管, 然后创建 顺序-临时节点 /lock/goods
2、创建成功后,遍历 /lock 节点的子节点,如果发现自己的节点是顺序中的第一个,那么说明拿到了分布式锁,开始执行任务,执行完成后删除自己的子节点。如果不是顺序中的第一个,执行第3步
3、获取到子节点中在自己创建的节点的前一个节点,监听其状态变化,当监听到前一个节点删除的时候,重新进行第2步。
比如现在有4个线程 A、B、C、D 来获取锁,分别获取到 /lock/goods0000000000 、/lock/goods0000000001 、/lock/goods0000000002、 /lock/goods0000000003 。那么A开始执行, B监听A,C监听B,D监听C。这时候,如果A执行完成,删除了 /lock/goods0000000000 节点,那么B监听到A执行完成,开始执行。
有人可能说,如果C宕机了呢?如果C宕机了,那其创建的顺序-临时节点则会消失,D重走了第2步,则会监听B。
三、实现代码:
在下一篇中,我们将会使用Java代码来实现ZooKeeper分布式锁:ZooKeeper完全解析(七) 使用ZooKeeper实现分布式锁之Java实现