在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

用Zookeeper怎么實現一個分布式鎖?

jf_78858299 ? 來源:JAVA旭陽 ? 作者:JAVA旭陽 ? 2023-05-11 11:02 ? 次閱讀

概述

提到鎖,想必大家可能最先想到的是Java JUC中的synchronized關鍵字或者可重入鎖ReentrantLock。它能夠保證我們的代碼在同一個時刻只有一個線程執行,保證數據的一致性和完整性。但是它僅限于單體項目,也就是說它們只能保證單個JVM應用內線程的順序執行。

如果你部署了多個節點,也就是分布式場景下如何保證不同節點在同一時刻只有一個線程執行呢?場景的業務場景比如秒殺、搶優惠券等,這就引入了我們的分布式鎖,本文我們主要講解利用Zookeeper的特性如何來實現我們的分布式鎖。

Zookeeper分布式鎖實現原理

利用Zookeeper的臨時順序節點和監聽機制兩大特性,可以幫助我們實現分布式鎖。

圖片

  1. 首先得有一個持久節點/locks, 路徑服務于某個使用場景,如果有多個使用場景建議路徑不同。
  2. 請求進來時首先在/locks創建臨時有序節點,所有會看到在/locks下面有seq-000000000, seq-00000001 等等節點。
  3. 然后判斷當前創建得節點是不是/locks路徑下面最小的節點,如果是,獲取鎖,不是,阻塞線程,同時設置監聽器,監聽前一個節點。
  4. 獲取到鎖以后,開始處理業務邏輯,最后delete當前節點,表示釋放鎖。
  5. 后一個節點就會收到通知,喚起線程,重復上面的判斷。

大家有沒有想過為什么要設置對前一個節點的監聽?

主要為了避免羊群效應。所謂羊群效應就是一個節點掛掉,所有節點都去監聽,然后做出反應,這樣會給服務器帶來巨大壓力,所以有了臨時順序節點,當一個節點掛掉,只有它后面的那一個節點才做出反應。

原生Zookeeper客戶端實現分布式鎖

通過原生zookeeper api方式的實現,可以加強我們對zk實現分布式鎖原理的理解。

public class DistributedLock {

    private String connectString = "10.100.1.176:2281";

    private int sessionTimeout = 2000;

    private ZooKeeper zk;

    private String rootNode = "lock";

    private String subNode = "seq-";

    private String waitPath;

    // 當前client創建的子節點
    private String currentNode;

    private CountDownLatch countDownLatch = new CountDownLatch(1);

    private CountDownLatch waitDownLatch = new CountDownLatch(1);

    public DistributedLock() throws IOException, InterruptedException, KeeperException {
        zk = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                // 如果連接建立時,喚醒 wait 在該 latch 上的線程
                if(event.getState() == Event.KeeperState.SyncConnected) {
                    countDownLatch.countDown();
                }

                //  發生了 waitPath 的刪除事件
                if(event.getType() == Event.EventType.NodeDeleted && event.getPath().equals(waitPath)) {
                    waitDownLatch.countDown();
                }
            }
        });

        // 等待連接建立,因為連接建立時異步過程
        countDownLatch.await();
        // 獲取根節點
        Stat stat = zk.exists("/" + rootNode, false);
        // 如果根節點不存在,則創建根節點
        if(stat == null) {
            System.out.println("創建根節點");
            zk.create("/" + rootNode, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        }
    }

    public void zkLock() {
        try {
            // 在根節點創建臨時順序節點
            currentNode = zk.create("/" + rootNode + "/" + subNode, null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);

            // 獲取子節點
            List<String> childrenNodes = zk.getChildren("/" + rootNode, false);
            // 如果只有一個子節點,說明是當前節點,直接獲得鎖
            if(childrenNodes.size() == 1) {
                return;
            } else {
                //對根節點下的所有臨時順序節點進行從小到大排序
                Collections.sort(childrenNodes);
                //當前節點名稱
                String thisNode = currentNode.substring(("/" + rootNode + "/").length());
                //獲取當前節點的位置
                int index = childrenNodes.indexOf(thisNode);
                if (index == -1) {
                    System.out.println("數據異常");
                } else if (index == 0) {
                    // index == 0, 說明 thisNode 在列表中最小, 當前client 獲得鎖
                    return;
                } else {
                    // 獲得排名比 currentNode 前 1 位的節點
                    this.waitPath = "/" + rootNode + "/" + childrenNodes.get(index - 1);
                    // 在 waitPath節點上注冊監聽器, 當 waitPath 被刪除時,zookeeper 會回調監聽器的 process 方法
                    zk.getData(waitPath, true, new Stat());
                    //進入等待鎖狀態
                    waitDownLatch.await();
                }
            }
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void zkUnlock() {
        try {
            zk.delete(this.currentNode, -1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (KeeperException e) {
            e.printStackTrace();
        }
    }
}

測試代碼如下:

public class DistributedLockTest {

    public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
        DistributedLock lock1 = new DistributedLock();
        DistributedLock lock2 = new DistributedLock();

        new Thread(() -> {
            // 獲取鎖對象
            try {
                lock1.zkLock();
                System.out.println("線程 1 獲取鎖");
                Thread.sleep(5 * 1000);
                System.out.println("線程 1 釋放鎖");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock1.zkUnlock();
            }
        }).start();

        new Thread(() -> {
            // 獲取鎖對象
            try {
                lock2.zkLock();
                System.out.println("線程 2 獲取鎖");
                Thread.sleep(5 * 1000);

                System.out.println("線程 2 釋放鎖");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock2.zkUnlock();
            }
        }).start();
    }
}

測試結果:

線程 2 獲取鎖
線程 2 釋放鎖
線程 1 獲取鎖
線程 1 釋放鎖

獲取鎖和釋放鎖成對出現,說明分布式鎖生效了。

Curator框架實現分布式鎖

在實際的開發鐘,我們會直接使用成熟的框架Curator客戶端,它里面封裝了分布式鎖的實現,避免我們去重復造輪子。

  1. pom.xml添加如下依賴
<dependency>
            <groupId>org.apache.curator<span class="hljs-name"groupId>
            <artifactId>curator-recipes<span class="hljs-name"artifactId>
            <version>5.2.1<span class="hljs-name"version>
        <span class="hljs-name"dependency>
  1. 通過InterProcessLock實現分布式鎖
public class CuratorLockTest {

    private String connectString = "10.100.1.14:2181";

    private String rootNode = "/locks";

    public static void main(String[] args) {

        new CuratorLockTest().testLock();

    }

    public void testLock() {
        // 分布式鎖1
        InterProcessLock lock1 = new InterProcessMutex(getCuratorFramework(), rootNode);
        // 分布式鎖2
        InterProcessLock lock2 = new InterProcessMutex(getCuratorFramework(), rootNode);
        // 第一個線程
        new Thread(() -> {
            // 獲取鎖對象
            try {
                lock1.acquire();
                System.out.println("線程 1 獲取鎖");
                // 測試鎖重入
                lock1.acquire();
                System.out.println("線程 1 再次獲取鎖");
                Thread.sleep(5 * 1000);
                lock1.release();
                System.out.println("線程 1 釋放鎖");
                lock1.release();
                System.out.println("線程 1 再次釋放鎖");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();

        // 第二個線程
        new Thread(() -> {
            // 獲取鎖對象
            try {
                lock2.acquire();
                System.out.println("線程 2 獲取鎖");
                // 測試鎖重入
                lock2.acquire();
                System.out.println("線程 2 再次獲取鎖");
                Thread.sleep(5 * 1000);
                lock2.release();
                System.out.println("線程 2 釋放鎖");
                lock2.release();
                System.out.println("線程 2 再次釋放鎖");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();
    }

    public CuratorFramework getCuratorFramework() {
        CuratorFramework client = CuratorFrameworkFactory.builder()
                .connectString(connectString).connectionTimeoutMs(2000)
                .sessionTimeoutMs(2000)
                .retryPolicy(new ExponentialBackoffRetry(3000, 3)).build();
        // 連接
        client.start();
        System.out.println("zookeeper 初始化完成...");
        return client;
    }
}
  1. 結果展示
線程 1 釋放鎖
線程 1 再次釋放鎖
線程 2 獲取鎖
線程 2 再次獲取鎖
線程 2 釋放鎖
線程 2 再次釋放鎖

有興趣的看下源碼,它是通過wait、notify來實現阻塞。

代碼https://github.com/alvinlkk/awesome-java-full-demo/tree/master/zookeeper-demo/zookeeper-lock

總結

ZooKeeper分布式鎖(如InterProcessMutex),能有效的解決分布式鎖問題,但是性能并不高。

因為每次在創建鎖和釋放鎖的過程中,都要動態創建、銷毀瞬時節點來實現鎖功能。大家知道,ZK中創建和刪除節點只能通過Leader服務器來執行,然后Leader服務器還需要將數據同不到所有的Follower機器上,這樣頻繁的網絡通信,性能的短板是非常突出的。

在高性能,高并發的場景下,不建議使用ZooKeeper的分布式鎖,可以使用Redis的分布式鎖。而由于ZooKeeper的高可用特性,所以在并發量不是太高的場景,推薦使用ZooKeeper的分布式鎖。

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • JAVA
    +關注

    關注

    19

    文章

    2972

    瀏覽量

    104855
  • 代碼
    +關注

    關注

    30

    文章

    4802

    瀏覽量

    68738
  • JVM
    JVM
    +關注

    關注

    0

    文章

    158

    瀏覽量

    12238
  • 線程
    +關注

    關注

    0

    文章

    505

    瀏覽量

    19705
  • zookeeper
    +關注

    關注

    0

    文章

    33

    瀏覽量

    3689
收藏 人收藏

    評論

    相關推薦

    在 Java 中利用 redis 實現分布式服務

    在 Java 中利用 redis 實現分布式服務
    發表于 07-05 13:14

    ZooKeeper分布式橋梁開發

    從傳統Java Web轉入分布式系統應用,再到接觸分布式協調框架ZooKeeper,通過痛苦的思維邏輯和理念轉變,歷經一個月時間,小伙伴們終于把Zo
    發表于 10-09 17:46 ?0次下載
    <b class='flag-5'>ZooKeeper</b><b class='flag-5'>分布式</b>橋梁開發

    Redis 分布式的正確實現方式

    分布式般有三種實現方式:1. 數據庫樂觀;2. 基于Redis的分布式
    的頭像 發表于 05-31 14:19 ?3609次閱讀

    開源分布式協調框架Zookeeper知識點詳解

    1 ZooKeeper簡介 ZooKeeper開源的分布式協調框架,它的定位是為分布式
    的頭像 發表于 05-03 09:32 ?1786次閱讀
    開源<b class='flag-5'>分布式</b>協調框架<b class='flag-5'>Zookeeper</b>五<b class='flag-5'>個</b>知識點詳解

    為什么需要分布式 基于Zookeeper安全嗎

    講清楚。導致很多讀者看了很多文章,依舊云里霧里。例如下面這些問題,你能清晰地回答上來嗎? 基于 Redis 如何實現分布式? Redi
    的頭像 發表于 08-10 18:06 ?5622次閱讀

    為什么需要分布式?基于Redis如何實現分布式

    分布式鎖相對應的是「單機」,我們在寫多線程程序時,避免同時操作共享變量產生數據問題,通常會使用
    的頭像 發表于 03-24 11:55 ?1153次閱讀

    深入理解redis分布式

    系統不同進程共同訪問共享資源的實現。如果不同的系統或同一個系統的不同主機之間共享了某個臨界資源,往往需要互斥來防止彼此干擾,以保證
    的頭像 發表于 10-08 14:13 ?978次閱讀
    深入理解redis<b class='flag-5'>分布式</b><b class='flag-5'>鎖</b>

    tldb提供分布式使用方法

    前言:分布式分布式系統中極為重要的工具。目前有多種分布式
    的頭像 發表于 11-02 14:44 ?906次閱讀
    tldb提供<b class='flag-5'>分布式</b><b class='flag-5'>鎖</b>使用方法

    redis分布式如何實現

    的情況,分布式的作用就是確保在同時間只有客戶端可以訪問共享資源,從而保證數據的致性和正
    的頭像 發表于 11-16 11:29 ?549次閱讀

    zookeeper分布式原理

    是提供高可用的、致性的機制,用于解決分布式系統中常見的致性問題,比如Leader選舉、分布式
    的頭像 發表于 12-03 16:33 ?659次閱讀

    Zookeeper的原理和作用

    Zookeeper分布式協調服務,它提供了組豐富的API和工具,用于構建分布式應用。它可
    的頭像 發表于 12-03 16:45 ?1552次閱讀

    zookeeper的核心配置文件是什么

    來定制化Zookeeper的行為和性能。 、介紹 Zookeeper高性能的分布式協調服
    的頭像 發表于 12-04 10:33 ?831次閱讀

    redis分布式方法

    Redis是種高性能的分布式緩存和鍵值存儲系統,它提供了種可靠的分布式解決方案。在分布式
    的頭像 發表于 12-04 11:22 ?1485次閱讀

    如何實現Redis分布式

    Redis是開源的內存數據存儲系統,可用于高速讀寫操作。在分布式系統中,為了保證數據的致性和避免競態條件,常常需要使用分布式
    的頭像 發表于 12-04 11:24 ?720次閱讀

    分布式的三種實現方式

    ,下面將分別介紹三種常見的實現方式。 、基于數據庫實現分布式分布式系統中,數據庫是最常
    的頭像 發表于 12-28 10:01 ?929次閱讀
    主站蜘蛛池模板: 色综合久久久久久久久久久 | ccav在线永久免费看| 色偷偷偷| 色性视频| 毛片网站网址| 欧美一区二区高清| 四虎音影| 五月激情综合| 一级毛片一级毛片一级级毛片 | 99色99| 黄色片网站大全| 你懂的 在线观看| 日本护士撒尿| 色视频在线免费| 四虎影视地址| 欧美成人精品一区二三区在线观看| 特黄三级| 视频免费黄色| 免费播放黄色| 久久999| 美女被啪到哭网站在线观看| 国产在线播放你懂的| 黄色三级网站免费| bt天堂中文在线| 97av在线| 美女好紧好大好爽12p| 欧美另类图片亚洲偷| 欧美黄色成人| 欧美ol丝袜高跟秘书在线观看| 免费观看视频在线观看| 91av成人| 日本免费不卡视频| 欧美不卡视频| 第四色亚洲| 亚洲国产精品综合久久网络 | 欧美高清xx| 一级做a爱过程免费视| 亚洲美国avcom| 天堂网中文字幕| 色噜噜狠狠色综合久| 精品欧美一区二区三区|