1. 程式人生 > >ZooKeeper完全解析(七) 使用ZooKeeper實現分散式鎖之Java實現

ZooKeeper完全解析(七) 使用ZooKeeper實現分散式鎖之Java實現

  在上一節中,我們講了使用ZooKeeper來實現分散式鎖的原理,連結為  ZooKeeper完全解析(六) 使用ZooKeeper實現分散式鎖之實現原理 ,這一節我們來講一下如何使用Java來實現分散式鎖:

  在實現原理中,我們把使用ZooKeeper實現分散式鎖分成了3步,在Java實現中,我們將第1步寫成一個方法,第2、3步寫成一個方法:

  第一步:

    public void lock(String lockBasePath,final Runnable runnable) {

        // 先建立lock節點
        try {
            zooKeeper.create(lockBasePath, "".getBytes(), OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        } catch (KeeperException.NodeExistsException e) {

        }  catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        if (StringUtils.isEmpty(lockBasePath) || runnable == null) {
            return;
        }

        // 建立一個順序的、臨時的節點
        try {
            String childCurrentPath = zooKeeper.create(lockBasePath + LockConsts.LOCK_CHILD_PATH, "".getBytes(), OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
            String[] childPathArray = childCurrentPath.split("[/]");
            String childRelativePath = childPathArray[childPathArray.length - 1];

            // 開始lock
            doLock(lockBasePath, childRelativePath, runnable);

        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

  其中 ,傳入的引數中lockBasePath為鎖的基礎路徑,而裡面子節點的路徑由我們來定義即可,Runnable為當拿到分散式鎖之後,執行的程式碼塊。

  首先,我們建立了一個lockBasePath的基礎路徑,然後開始建立了順序的子節點。然後執行第2、3步的方法

第二步:

   private void doLock(String lockBasePath,final String childRelativePath, final Runnable runnable) throws KeeperException, InterruptedException {

        String childCurrentPath = lockBasePath + "/" + childRelativePath;
        // 遍歷子節點,
        List<String> masterChildRelativePathList = zooKeeper.getChildren(lockBasePath, false);
        // 將子節點置為一個有序的子節點,從小到大
        masterChildRelativePathList.sort(new ZooChildNodeComparator());

        logger.info(childRelativePath + " path " + JSON.toJSONString(masterChildRelativePathList));

        // 有兩種情況,一種是第一個節點,一種不是
        if (childRelativePath.equals(masterChildRelativePathList.get(0))) {

            logger.info(childRelativePath + " 搶到了分散式鎖,開始執行");

            // 說明搶到了分散式鎖,開始做分散式任務,完成之後,將節點刪除
            runnable.run();

            logger.info(childRelativePath + " 搶到了分散式鎖,執行完成");

            // 刪除節點
            zooKeeper.delete(childCurrentPath, -1);

            logger.info(childRelativePath + " 執行完成後刪除了節點\n\n\n");

        } else {

            // 監聽上一個節點,如果上一個節點刪除,再遍歷子節點
            int currentPathIndex = masterChildRelativePathList.indexOf(childRelativePath);

            // 監聽上一個節點
            String childPreRelativePath = masterChildRelativePathList.get(currentPathIndex - 1);
            String childPreCurrentPath = lockBasePath + "/" + childPreRelativePath;

            logger.info(childRelativePath + " 監聽 " + childPreRelativePath);

            zooKeeper.getData(childPreCurrentPath, event -> {
                if (Watcher.Event.EventType.NodeDeleted != event.getType()) {
                    logger.info("節點非刪除  " + event.getType().getIntValue());
                    return;
                }

                logger.info(childRelativePath + " 上一個節點被刪除,開始繼續lock");

                // 重複 doLock
                try {
                    doLock(lockBasePath, childRelativePath, runnable);
                } catch (KeeperException e) {
                    e.printStackTrace();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }, null);


        }
    }

  可以看到,在這步中,我們首先遍歷子節點,如果發現自己是第一個,那麼開始執行程式碼塊,如果沒有,那麼監聽上一個節點,如果上一個節點被刪除,重新走這個方法。 

 使用ZooKeeper實現分散式鎖的Java實現程式碼在 實現程式碼 ,歡迎大家提修改意見和star