91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

ZooKeeper分布式鎖的實現方式

發布時間:2021-07-01 09:00:02 來源:億速云 閱讀:130 作者:chen 欄目:開發技術

本篇內容介紹了“ZooKeeper分布式鎖的實現方式”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

目錄
  • 一、分布式鎖方案比較

  • 二、ZooKeeper實現分布式鎖

    • 2.1、方案一

    • 2.2、方案二

一、分布式鎖方案比較

方案實現思路優點缺點
利用 MySQL 的實現方案利用數據庫自身提供的鎖機制實現,要求數據庫支持行級鎖實現簡單性能差,無法適應高并發場景;容易出現死鎖的情況;無法優雅的實現阻塞式鎖
利用 Redis 的實現方案使用 Setnx 和 lua 腳本機制實現,保證對緩存操作序列的原子性性能好實現相對復雜,有可能出現死鎖;無法優雅的實現阻塞式鎖
利用 ZooKeeper 的實現方案基于 ZooKeeper 節點特性及 watch 機制實現性能好,穩定可靠性高,能較好地實現阻塞式鎖實現相對復雜

二、ZooKeeper實現分布式鎖

這里使用 ZooKeeper 來實現分布式鎖,以50個并發請求來獲取訂單編號為例,描述兩種方案,第一種為基礎實現,第二種在第一種基礎上進行了優化。

2.1、方案一

流程描述:

ZooKeeper分布式鎖的實現方式

具體代碼:

OrderNumGenerator:

/**
 * @Description 生成隨機訂單號
 */
public class OrderNumGenerator {

    private static long count = 0;

    /**
     * 使用日期加數值拼接成訂單號
     */
    public String getOrderNumber() throws Exception {
        String date = DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(LocalDateTime.now());
        String number = new DecimalFormat("000000").format(count++);
        return date + number;
    }
}

Lock:

/**
 * @Description 自定義鎖接口
 */
public interface Lock {

    /**
     * 獲取鎖
     */
    public void getLock();

    /**
     * 釋放鎖
     */
    public void unLock();
}

AbstractLock:

/**
 * @Description 定義一個模板,具體的方法由子類來實現
 */
public abstract class AbstractLock implements Lock {

    /**
     * 獲取鎖
     */
    @Override
    public void getLock() {

        if (tryLock()) {
            System.out.println("--------獲取到了自定義Lock鎖的資源--------");
        } else {
            // 沒拿到鎖則阻塞,等待拿鎖
            waitLock();
            getLock();
        }

    }

    /**
     * 嘗試獲取鎖,如果拿到了鎖返回true,沒有拿到則返回false
     */
    public abstract boolean tryLock();

    /**
     * 阻塞,等待獲取鎖
     */
    public abstract void waitLock();
}

ZooKeeperAbstractLock:

/**
 * @Description 定義需要的服務連接
 */
public abstract class ZooKeeperAbstractLock extends AbstractLock {

    private static final String SERVER_ADDR = "192.168.182.130:2181,192.168.182.131:2181,192.168.182.132:2181";

    protected ZkClient zkClient = new ZkClient(SERVER_ADDR);

    protected static final String PATH = "/lock";
}

ZooKeeperDistrbuteLock:

/**
 * @Description 真正實現鎖的細節
 */
public class ZooKeeperDistrbuteLock extends ZooKeeperAbstractLock {
    private CountDownLatch countDownLatch = null;

    /**
     * 嘗試拿鎖
     */
    @Override
    public boolean tryLock() {
        try {
            // 創建臨時節點
            zkClient.createEphemeral(PATH);
            return true;
        } catch (Exception e) {
            // 創建失敗報異常
            return false;
        }
    }

    /**
     * 阻塞,等待獲取鎖
     */
    @Override
    public void waitLock() {
        // 創建監聽
        IZkDataListener iZkDataListener = new IZkDataListener() {
            @Override
            public void handleDataChange(String s, Object o) throws Exception {

            }

            @Override
            public void handleDataDeleted(String s) throws Exception {
                // 釋放鎖,刪除節點時喚醒等待的線程
                if (countDownLatch != null) {
                    countDownLatch.countDown();
                }
            }
        };

        // 注冊監聽
        zkClient.subscribeDataChanges(PATH, iZkDataListener);

        // 節點存在時,等待節點刪除喚醒
        if (zkClient.exists(PATH)) {
            countDownLatch = new CountDownLatch(1);
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        // 刪除監聽
        zkClient.unsubscribeDataChanges(PATH, iZkDataListener);
    }

    /**
     * 釋放鎖
     */
    @Override
    public void unLock() {
        if (zkClient != null) {
            System.out.println("釋放鎖資源");
            zkClient.delete(PATH);
            zkClient.close();
        }
    }
}

測試效果:使用50個線程來并發測試ZooKeeper實現的分布式鎖

/**
 * @Description 使用50個線程來并發測試ZooKeeper實現的分布式鎖
 */
public class OrderService {

    private static class OrderNumGeneratorService implements Runnable {

        private OrderNumGenerator orderNumGenerator = new OrderNumGenerator();;
        private Lock lock = new ZooKeeperDistrbuteLock();

        @Override
        public void run() {
            lock.getLock();
            try {
                System.out.println(Thread.currentThread().getName() + ", 生成訂單編號:"  + orderNumGenerator.getOrderNumber());
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unLock();
            }
        }
    }

    public static void main(String[] args) {
        System.out.println("----------生成唯一訂單號----------");
        for (int i = 0; i < 50; i++) {
            new Thread(new OrderNumGeneratorService()).start();
        }
    }
}

2.2、方案二

方案二在方案一的基礎上進行優化,避免產生“羊群效應”,方案一一旦臨時節點刪除,釋放鎖,那么其他在監聽這個節點變化的線程,就會去競爭鎖,同時訪問 ZooKeeper,那么怎么更好的避免各線程的競爭現象呢,就是使用臨時順序節點,臨時順序節點排序,每個臨時順序節點只監聽它本身的前一個節點變化。

流程描述:

ZooKeeper分布式鎖的實現方式

具體代碼

具體只需要將方案一中的 ZooKeeperDistrbuteLock 改變,增加一個 ZooKeeperDistrbuteLock2,測試代碼中使用 ZooKeeperDistrbuteLock2 即可測試,其他代碼都不需要改變。

/**
 * @Description 真正實現鎖的細節
 */
public class ZooKeeperDistrbuteLock2 extends ZooKeeperAbstractLock {

    private CountDownLatch countDownLatch = null;
    /**
     * 當前請求節點的前一個節點
     */
    private String beforePath;
    /**
     * 當前請求的節點
     */
    private String currentPath;

    public ZooKeeperDistrbuteLock2() {
        if (!zkClient.exists(PATH)) {
            // 創建持久節點,保存臨時順序節點
            zkClient.createPersistent(PATH);
        }
    }

    @Override
    public boolean tryLock() {
        // 如果currentPath為空則為第一次嘗試拿鎖,第一次拿鎖賦值currentPath
        if (currentPath == null || currentPath.length() == 0) {
            // 在指定的持久節點下創建臨時順序節點
            currentPath = zkClient.createEphemeralSequential(PATH + "/", "lock");
        }
        // 獲取所有臨時節點并排序,例如:000044
        List<String> childrenList = zkClient.getChildren(PATH);
        Collections.sort(childrenList);

        if (currentPath.equals(PATH + "/" + childrenList.get(0))) {
            // 如果當前節點在所有節點中排名第一則獲取鎖成功
            return true;
        } else {
            int wz = Collections.binarySearch(childrenList, currentPath.substring(6));
            beforePath = PATH + "/" + childrenList.get(wz - 1);
        }
        return false;
    }

    @Override
    public void waitLock() {
        // 創建監聽
        IZkDataListener iZkDataListener = new IZkDataListener() {
            @Override
            public void handleDataChange(String s, Object o) throws Exception {

            }

            @Override
            public void handleDataDeleted(String s) throws Exception {
                // 釋放鎖,刪除節點時喚醒等待的線程
                if (countDownLatch != null) {
                    countDownLatch.countDown();
                }
            }
        };

        // 注冊監聽,這里是給排在當前節點前面的節點增加(刪除數據的)監聽,本質是啟動另外一個線程去監聽前置節點
        zkClient.subscribeDataChanges(beforePath, iZkDataListener);

        // 前置節點存在時,等待前置節點刪除喚醒
        if (zkClient.exists(beforePath)) {
            countDownLatch = new CountDownLatch(1);
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        // 刪除對前置節點的監聽
        zkClient.unsubscribeDataChanges(beforePath, iZkDataListener);
    }

    /**
     * 釋放鎖
     */
    @Override
    public void unLock() {
        if (zkClient != null) {
            System.out.println("釋放鎖資源");
            zkClient.delete(currentPath);
            zkClient.close();
        }
    }
}

“ZooKeeper分布式鎖的實現方式”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

瑞丽市| 长寿区| SHOW| 剑阁县| 桐梓县| 遂溪县| 宜黄县| 庆元县| 德令哈市| 西平县| 嘉禾县| 上饶市| 保德县| 临安市| 永丰县| 定州市| 陆河县| 岚皋县| 瑞丽市| 巴林右旗| 寿阳县| 石棉县| 金华市| 乡城县| 保山市| 云南省| 洱源县| 格尔木市| 永和县| 拜城县| 繁昌县| 双城市| 通海县| 桐柏县| 锦屏县| 儋州市| 鸡东县| 紫阳县| 余姚市| 新兴县| 迭部县|