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

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

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

3天內不再提示

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

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

概述

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

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

Zookeeper分布式鎖實現原理

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

圖片

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

大家有沒有想過為什么要設置對前一個節(jié)點的監(jiān)聽?

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

原生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創(chuàng)建的子節(jié)點
    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();
                }

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

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

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

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

在實際的開發(fā)鐘,我們會直接使用成熟的框架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),能有效的解決分布式鎖問題,但是性能并不高。

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

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

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

    關注

    20

    文章

    2987

    瀏覽量

    107245
  • 代碼
    +關注

    關注

    30

    文章

    4891

    瀏覽量

    70350
  • JVM
    JVM
    +關注

    關注

    0

    文章

    160

    瀏覽量

    12541
  • 線程
    +關注

    關注

    0

    文章

    507

    瀏覽量

    20109
  • zookeeper
    +關注

    關注

    0

    文章

    34

    瀏覽量

    3899
收藏 人收藏

    評論

    相關推薦
    熱點推薦

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

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

    ZooKeeper分布式橋梁開發(fā)

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

    Redis 分布式的正確實現方式

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

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

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

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

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

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

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

    深入理解redis分布式

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

    tldb提供分布式使用方法

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

    redis分布式如何實現

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

    zookeeper分布式原理

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

    Zookeeper的原理和作用

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

    zookeeper的核心配置文件是什么

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

    redis分布式方法

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

    如何實現Redis分布式

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

    分布式的三種實現方式

    ,下面將分別介紹三種常見的實現方式。 、基于數據庫實現分布式分布式系統中,數據庫是最常
    的頭像 發(fā)表于 12-28 10:01 ?1214次閱讀
    主站蜘蛛池模板: 国产码一区二区三区 | 五月天激激婷婷大综合丁香 | 四虎影院免费观看视频 | 色偷偷免费视频 | 成人爽a毛片在线视频网站 成人窝窝午夜看片 | 免费黄视频在线观看 | 自拍偷自拍亚洲精品被多人伦好爽 | 扒开末成年粉嫩的流白浆视频 | 狠狠色丁香久久综合五月 | 特级中国aaa毛片 | 免费午夜影片在线观看影院 | 亚洲成a人片7777 | 成年男人永久免费看片 | 午夜激情福利 | 老色鬼久久综合第一 | 噜色| 5566精品资源在线播放 | 性夜影院爽黄a爽免费视频 性瘾高h姚蕊全文免费阅读 | caoporn97人人做人人爱最新 | 天堂资源地址在线 | 在线看av的网址 | 日产精品卡二卡三卡四卡乱码视频 | 操国产美女 | 日本 韩国 三级 国产 欧美 | 91激情| 久久99久久99精品免观看 | www.夜夜爽| 日本精品视频四虎在线观看 | 最新色站 | 天天看黄| 亚洲午夜久久久久久噜噜噜 | www.五月婷婷.com| 午夜久久久久久亚洲国产精品 | 国产小毛片 | 性欧美性free| 五月天婷婷综合 | 国产成人三级视频在线观看播放 | 国产精品性 | 男女无遮挡在线完整视频 | 天天插综合 | 日韩一区二区三区免费 |