之前对zk的应用场景比较好奇,后来发现hive当中已经有使用zk来实现分布式锁的实现。

为了更好的理解分布式锁的实现,所以对hive这块的代码进行阅读,提升了对技术实现的理解。

hive使用的是ZooKeeperHiveLockManager类来进行处理的。

hive锁目前有两个类型,代码如下:

public enum HiveLockMode{

  SHARED, EXCLUSIVE;

}


ZooKeeperHiveLockManager此类获取锁主要是这个方法如下:

private ZooKeeperHiveLock lockPrimitive(HiveLockObject key,

      HiveLockMode mode, boolean keepAlive, boolean parentCreated)

      throws KeeperException, InterruptedException

调用路径如下:




通过此图可以发现来做查询的时候会获取读写锁,另一个是在对表做相关操作时会有锁表的获取。


第一步:首先是创建父节点列表。节点的形式:/parent1/parent2/parent3/lastname/

       代码如下:

       for (String name : names) {

           try {

                 res = createChild(name, new byte[0], CreateMode.PERSISTENT);        

           } catch (KeeperException e) {

               if (e.code() != KeeperException.Code.NODEEXISTS) {

                  //if the exception is not 'NODEEXISTS', re-throw it

                  throw e;

               }

           }

       }

       其中names是一个list类型,将String类转换成集合来进行,存储的数据是["/parent1","/parent1/parent2","/parent1/parent2/parent3",

       "/parent1/parent2/parent3/lastname"];

第二步:创建lock节点,

       res = createChild(getLockName(lastName, mode), key.getData().toString()

             .getBytes(), keepAlive ? CreateMode.PERSISTENT_SEQUENTIAL : CreateMode.EPHEMERAL_SEQUENTIAL);

       getlockName返回的其实是一个子节点名称,还是以之前的节点为例,那么这里创建的是/parent1/parent2/parent3/lastname/LOCK-{mode}-

       其中mode为锁的模式(排他锁还是共享锁)。

       CreateMode取值说明如下:

    /**

     * The znode will not be automatically deleted upon client's disconnect.

     */

    PERSISTENT (0, false, false),

    /**

    * The znode will not be automatically deleted upon client's disconnect,

    * and its name will be appended with a monotonically increasing number.

    */

    PERSISTENT_SEQUENTIAL (2, false, true),

    /**

     * The znode will be deleted upon the client's disconnect.

     */

    EPHEMERAL (1, true, false),

    /**

     * The znode will be deleted upon the client's disconnect, and its name

     * will be appended with a monotonically increasing number.

     */

    EPHEMERAL_SEQUENTIAL (3, true, true);

    这里就是利用到了2和3类型的节点的特性,会在前缀的后面使用自增数字(这也是实现分布式锁的关键)。这样在下面的代码片段中,才能截取数字出来。

       

       res的值当中是包含一个返回的序列号,把这个序列号截取出来,代码如下:

       int seqNo = getSequenceNumber(res, getLockName(lastName, mode));

                 然后再次遍历lastName的子节点,代码如下:

                 List<String> children = zooKeeper.getChildren(lastName,false);

       

       String exLock = getLockName(lastName, HiveLockMode.EXCLUSIVE);

       String shLock = getLockName(lastName, HiveLockMode.SHARED);


       for (String child : children) {

            child = lastName + "/" + child;

           // Is there a conflicting lock on the same object with a lower sequence

           // number

           int childSeq = seqNo;

           if (child.startsWith(exLock)) {

               childSeq = getSequenceNumber(child, exLock);

           }

           if ((mode == HiveLockMode.EXCLUSIVE) && child.startsWith(shLock)) {

               childSeq = getSequenceNumber(child, shLock);

           }


           if ((childSeq >= 0) && (childSeq < seqNo)) {

                zooKeeper.delete(res, -1);

                console.printError("conflicting lock present for "

                      + key.getDisplayName() + " mode " + mode);

                return null;

           }

        }

       这段代码就是处理锁的核心业务,首先判断是否是排他锁,如果是获取该锁的序列号;再次判断获取锁的模式为排他锁并且当前child的锁为共享锁,也获取

       该锁的序列号。 然后判断语句childSeq<SeqNo,获取锁表明是否靠前,如果有childSeq<SeqNo为true,表示前面有人获取锁,那么这次获取锁是失败

       的。换句话说,也就是根据这个自增数字来判断谁先获取到锁。

       这也是如何实现分布式锁的核心。当然删除一个锁很简单,就是将对应的lock节点删除即可。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐