# zookeeper——分布式锁原理

在单体的应用开发场景中涉及并发同步的时候,大家往往采用Synchronized(同步)或者其他同一个JVM内Lock机制来解决多线程间的同步问题。在分布式集群工作的开发场景中,就需要一种更加高级的锁机制来处理跨机器的进程之间的数据同步问题。这种跨机器的锁就是分布式锁。在单体的应用开发场景中涉及并发同步的时候,大家往往采用Synchronized(同步)或者其他同一个JVM内Lock机制来解决多线程间的同步问题。在分布式集群工作的开发场景中,就需要一种更加高级的锁机制来处理跨机器的进程之间的数据同步问题。这种跨机器的锁就是分布式锁。

# 公平锁和可重入锁的原理

最经典的分布式锁是可重入的公平锁。

  • 什么是可重入的公平锁呢?

    直接讲解概念和原理会比较抽象难懂,还是从具体的实例入手吧!这里尽量用一个简单的故事来类比,估计就简单多了。故事发生在一个没有自来水的古代,在一个村子里有一口井,水质非常的好,村民们都抢着取井里的水。井就那么一口,村里的人很多,村民为争抢取水打架斗殴,甚至头破血流。

    问题总是要解决,于是村长绞尽脑汁最终想出了一个凭号取水的方案。井边安排一个看井人,维护取水的秩序。取水秩序很简单:

    1. 取水之前,先取号。
    2. 号排在前面的人,就可以先取水。
    3. 先到的人排在前面,那些后到的人,一个一个挨着在井边排成一队。

    这种排队取水模型就是一种锁的模型。排在最前面的号拥有取水权,就是一种典型的独占锁。另外,先到先得,号排在前面的人先取到水,取水之后就轮到下一个号取水,挺公平的,说明它是一种公平锁。

  • 什么是可重入锁呢?

    假定取水时以家庭为单位,家庭的某人拿到号,其他的家庭成员过来打水,这时候不用再取号

# ZooKeeper分布式锁的原理

理解了经典的公平可重入锁的原理后,再来看在分布式场景下的公平可重入锁的原理。

通过前面的分析基本可以判定:ZooKeeper的临时顺序节点,天生就有一副实现分布式锁的胚子。为什么呢?

  • ZooKeeper的每一个节点都是一个天然的顺序发号器

    在每一个节点下面创建临时顺序节点(EPHEMERAL_SEQUENTIAL)类型,新的子节点后面会加上一个顺序编号。这个顺序编号是在上一个生成的顺序编号加1。

    例如,用一个发号的节点“/test/lock”为父亲节点,可以在这个父节点下面创建相同前缀的临时顺序子节点,假定相同的前缀为“/test/lock/seq-”。如果是第一个创建的子节点,那么生成的子节点为/test/lock/seq-0000000000,下一个节点则为/test/lock/seq-0000000001,依次类推。 An image

  • ZooKeeper节点的递增有序性可以确保锁的公平

    一个ZooKeeper分布式锁,首先需要创建一个父节点,尽量是持久节点(PERSISTENT类型),然后每个要获得锁的线程都在这个节点下创建个临时顺序节点。由于Zk节点是按照创建的顺序依次递增的,为了确保公平,可以简单地规定,编号最小的那个节点表示获得了锁。因此,每个线程在尝试占用锁之前,首先判断自己的排号是不是当前最小的,如果是,则获取锁。

  • ZooKeeper的节点监听机制可以保障占有锁的传递有序而且高效

    每个线程抢占锁之前,先抢号创建自己的ZNode。同样,释放锁的时候,就需要删除抢号的ZNode。在抢号成功之后,如果不是排号最小的节点,就处于等待通知的状态。等谁的通知呢?不需要其他人,只需要等前一个ZNode的通知即可。当前一个ZNode被删除的时候,就是轮到了自己占有锁的时候。第一个通知第二个、第二个通知第三个,击鼓传花似的依次向后传递。

    ZooKeeper的节点监听机制能够非常完美地实现这种击鼓传花似的信息传递。具体的方法是,每一个等通知的ZNode节点,只需要监听或者监视排号在自己前面那个且紧挨在自己前面的那个节点。只要上一个节点被删除了,就再进行一次判断,看看自己是不是序号最小的那个节点,如果是,则获得锁。

    ZooKeeper内部优越的机制,能保证由于网络异常或者其他原因造成集群中占用锁的客户端失联时,锁能够被有效释放。一旦占用锁的ZNode客户端与ZooKeeper集群服务器失去联系,这个临时ZNode也将自动删除。排在它后面的那个节点也能收到删除事件,从而获得锁。所以,在创建取号节点的时候,尽量创建临时ZNode节点。

  • ZooKeeper的节点监听机制能避免羊群效应

    ZooKeeper这种首尾相接,后面监听前面的方式,可以避免羊群效应。所谓羊群效应就是一个节点挂掉了,所有节点都去监听,然后作出反应,这样会给服务器带来巨大压力,所以有了临时顺序节点,当一个节点挂掉,只有它后面的那一个节点才作出反应。

Last Updated: 10/31/2020, 5:38:49 PM