基于Zookeeper使用ZkClient實現分布式鎖

有段時間沒寫博客了,在整理之前寫過的一套自定義框架,并且整理好上傳值github上了,也有一些新功能還在開發,歡迎大家使用:一個好用的Http接口請求工具組件

可能今天這篇文章跟之前的比有些跳躍性,一下子就談到了Zookeeper了,不過也沒關系啦,先談談最常用,然后在慢慢看Zooeeper的其他知識。

簡單介紹

ZooKeeper致力于提供一個高性能、高可用,且具備嚴格的順序訪問控制能力的分布式協調服務,是雅虎公司創建,是GoogleChubby一個開源的實現,也是HadoopHbase的重要組件。它的數據以樹形結構(類似于文件系統)儲存在內存當中由于數儲存在內存當中(并且每個節點都必須要有數據),所以拿取數據效率特別快。

分布式鎖:功能與之前并發編程中的Lock功能一致,主要是為了解決共享資源被競爭所導致的并發問題。由于并發編程當中鎖是在當前的JVM當中,而對于分布式的服務來說單純的JVM的鎖已經不起作用了,不過實現功能還是一致。

監聽機制

既然是鎖,那么就存在線程等待以及線程被喚醒功能,所以就需要有一個監聽機制,當ZooKeeper上的鎖被釋放之后需監聽到,并且通知服務去獲取鎖資源,正好在ZooKeeper當中存在一種監聽機制,為事件監聽器(Watcher)

事件監聽器:客戶端可以在節點上注冊監聽器,當特定的事件發生后,ZooKeeper會通知到感興趣的客戶端;被監聽的事件有:NodeCreated(節點創建)、NodeDeleted(節點刪除)NodeDataChanged(節點數據被改變)NodeChildrenChange(子節點被修改)

Node類型

基于ZooKeeper分布式鎖必然基于節點,在ZooKeeper創建節點共有四點類型:

1、持久化節點(PERSISENT):?同一個節點路徑只能創建一個節點,并且連接創建節點后,斷開連接后,節點仍然存在并且關閉服務會保存至磁盤上。

2、持久化順序節點(PERSISENT_SEQUENTIAL):同一個節點路徑可以創建多個節點,并且ZooKeeper會自動分配一個按順序的節點號,斷開連接后,節點仍然會保存至磁盤。

3、臨時節點(EPHEMERAL):在一個連接中,同一路徑下只能創建一個節點,當創建節點的連接關閉后,該節點會被刪除,如果非正常關閉連接,則過一段時間后節點會被刪除。

4、臨時順序節點(EPHEMERAL_SEQUENTIAL):同一路徑下可以創建多個節點,但是節點名稱ZooKeeper會自動分配一個按順序的節點號,當連接關閉后,這些節點會被刪除。

實現方式

先引入一下zkClient的坐標

<dependency><groupId>com.101tec</groupId><artifactId>zkclient</artifactId><version>0.10</version>
</dependency>

既然是鎖,那么必定是同一個節點,而且要先去嘗試獲取到鎖,如果沒有獲取到,那么就進入等待,并且監聽同一個節點是否被刪除(或者修改),如果刪除,則喚醒等待的線程,并且再次去獲取ZooKeeper上的鎖節點;那么整體的流程如下:

//鎖節點
public static final String PATH = "/lock";
//嘗試獲取鎖	
public abstract boolean tryLock();
//等待鎖釋放
public abstract void waitLock();
//釋放鎖
public abstract void unLock();
//獲取鎖
void getLock(){if(tryLock()) {//獲取到鎖后,進行業務操作System.out.println(Thread.currentThread().getName() + " get Lock");}else {//沒有獲取則進入等待,并且監聽鎖節點是否被釋放waitLock();//再次獲取鎖getLock();}
}

?我這里是采用ZkClient進行操作ZooKeeper的,先創建一個ZkClient連接:

	CountDownLatch latch = null;private static final String CONNECTION = "127.0.0.1:2181";ZkClient zkClient = new ZkClient(CONNECTION,3000);

先來看看嘗試獲取鎖可以怎樣實現:

@Override
public boolean tryLock() {try {//創建臨時節點,也可以創建持久化節點,到時候釋放節點的時候刪除就好了zkClient.createEphemeral(PATH, "1".getBytes());return true;} catch (Exception e) {//如果創建失敗,則獲取節點鎖失敗,則進入等待return false;}
}

進入等待,并且監聽鎖節點是否刪除或者修改:

@Override
public void waitLock() {//創建監聽事件IZkDataListener listen = new IZkDataListener() {public void handleDataChange(String dataPath, Object data) throws Exception {//當前方法為監聽節點修改,如果節點進行修改,那么就會執行當前方法}public void handleDataDeleted(String dataPath) throws Exception {//我這里是釋放鎖為刪除節點,刪除會執行當前方法latch.countDown();}};//注冊監聽器zkClient.subscribeDataChanges(PATH, listen);//如果ZooKeeper上存在鎖節點,那么進入等待if(zkClient.exists(PATH)) {//采用CountDownLatch等待latch = new CountDownLatch(1);try {//進入等待latch.await();} catch (InterruptedException e) {e.printStackTrace();}}//刪除監聽器zkClient.unsubscribeDataChanges(PATH, listen);
}

然后就是釋放節點了:

@Override
public void unLock() {if(zkClient != null) {System.out.println(Thread.currentThread().getName() + " unlock.. ");//刪除節點zkClient.delete(PATH);//關閉當前連接zkClient.close();}
}

這么一套流程下來,分布式鎖的功能就完成了,當這種實現的功能類似JVM鎖中的非公平鎖,即沒有先后順序所言,如果想要達到公平鎖,那么就必須得使用順序節點進行操作了。

那么分布式公平鎖監聽的節點就不是同一個節點了,而是監聽當前節點的上一個節點:

private String lockSeq = null;private String before = null;@Override
public boolean tryLock() {if(!zkClient.exists(PATH)) {try {zkClient.createPersistent(PATH, true);} catch (Throwable t) {//已經創建完畢,并發問題,拋異常處理}}if(lockSeq == null) {lockSeq = zkClient.createEphemeralSequential(PATH + "/", "1".getBytes());}List<String> children = zkClient.getChildren(PATH);if(lockSeq.equals(PATH + "/" + children.get(0))) {return true;}else {for(String str : children) {if(lockSeq.contains(str)) {break;}before = PATH + "/" + str;}System.out.println(Thread.currentThread().getName() + " before node:" + before);return false;}
}@Override
public void waitLock() {IZkDataListener listen = new IZkDataListener() {public void handleDataChange(String dataPath, Object data) throws Exception {}public void handleDataDeleted(String dataPath) throws Exception {latch.countDown();}};zkClient.subscribeDataChanges(before, listen);if(zkClient.exists(before)) {latch = new CountDownLatch(1);try {latch.await();} catch (InterruptedException e) {e.printStackTrace();}}zkClient.unsubscribeDataChanges(before, listen);}@Override
public void unLock() {if(zkClient != null) {System.out.println(Thread.currentThread().getName() + " unlock.. ");System.out.println("this node:" + lockSeq + " last node:" + before);zkClient.delete(lockSeq);}
}

實現的代碼如上(一個簡單的實現方式,存在單應用并發問題,可以使用記錄線程的方式解決并發問題),大家可以各位去試一下,個人覺得非公平鎖的效率相比公平鎖來說效率要高一點點,不過對應大量的分布式服務去競爭鎖資源的話,個人建議還是使用公平鎖,避免阻塞時間過長,導致服務業務長期停滯問題。

還會存在一個問題就是臨時順序節點在關閉服務的時候ZooKeeper上會等待幾秒鐘才會刪除臨時節點,所以建議在程序中加上Hook鉤子方法進行刪除。

    static {Runtime.getRuntime().addShutdownHook(new Thread(ZkClientLock::run));}private static void run() {System.out.println("關閉服務...");//刪除所有臨時順序節點,避免影響其他服務zkClient.delete(lockSeq);zkClient.close();}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/537113.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/537113.shtml
英文地址,請注明出處:http://en.pswp.cn/news/537113.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

算法題學到的一些小語言細節

1.要學會用i&#xff1b;可以簡化很多代碼&#xff1a;i&#xff1b;copyFromMe(i)&#xff1b;可以寫成&#xff1a;copyFromeMe(i) 2.StringBuffer也跟列表一樣有append函數&#xff1b; 3.if語句是分支不能進行循環&#xff0c;要寫成while才能替代循環里面的判斷 4. 這里的…

android 按鈕帶圖標 陰影_android中帶圖標的按鈕(ImageButton)怎么用

展開全部除了Android系統自帶的Button按鈕以外&#xff0c;還提供了帶圖標的按鈕ImageButton要制作帶圖標的按鈕&#xff0c;首先要在布局62616964757a686964616fe58685e5aeb931333337613163文件中定義ImageButton&#xff0c;然后通過setImageDrawable方法來設置要顯示的圖標。…

Zookeeper基礎常用操作以及ACL權限

這次將Zookeeper的一些基礎用法以及權限這塊的都補充一下在這篇博客中。 上篇博客介紹了基于ZooKeeper實現的分布式鎖&#xff0c;也介紹了一些ZooKeeper的節點類型以及監聽機制&#xff0c;今天這里就不作過多的介紹了&#xff0c;大家也可以自行的去官方文檔上看看更具體的介…

[中醫經絡學習一]足陽明胃經

人體有六臟&#xff08;心、肝、脾、肺、腎五臟&#xff0c;再加心包&#xff09;六腑&#xff08;胃、小腸、大腸、膀胱、膽、三焦&#xff09;&#xff0c;每個臟腑都聯接著一條經絡&#xff0c;一共12條經絡&#xff0c;稱“十二正經”&#xff0c;經絡的走向在四肢兩側基本…

ThreadLocal原理解析以及是否需要調用remove方法

平常的開發過程中&#xff0c;如果有個類不是線程安全的&#xff0c;比如SimpleDateFormat&#xff0c;要使這個類在并發的過程中是線程安全的&#xff0c;那么可以將變量設置位局部變量&#xff0c;不過存在的問題就是頻繁的創建對象&#xff0c;對性能和資源會有一定降低和消…

Maven基礎及概念

前言 今天就來聊聊Maven的基礎和一些比較概念性的東西&#xff0c;還有一些常用的Maven命令啥的&#xff0c;主要是某人腦子記不住&#xff0c;記在博客中讓她自己看吧&#xff0c;省的費心給她找。 后續的文章會聊到Maven的一些比較高級用法&#xff0c;像自定義插件&#x…

織夢縮略圖自動補齊絕對路徑_織夢生成文章內容縮略圖時自動加上域名絕對路徑...

今天又接了個織夢CMS的有償服務,客戶想要后臺添加文章內容的時候,縮略圖自動變成帶上絕對路徑的格式.比如我們默認的縮略圖是這樣的 /uploads/allimg/150814/123P2NB-0-lp.png 他想要的效果是這樣的 http://www.youwujun.com.cn/uploads/allimg/150814/123P2NB-0-lp.png大家懂我…

BUAA 436 孟竹的復習計劃(二維樹狀數組)

題目鏈接&#xff1a;http://acm.buaa.edu.cn/problem/436/ 題意&#xff1a;一個數列兩種操作&#xff1a;&#xff08;1&#xff09;將某個位置的數字改成另一個數字&#xff1b;&#xff08;2&#xff09;交換兩個位置的數字。每次操作之后輸出逆序數的個數。 思路&#xff…

Maven之pom.xml常用標簽解析及鏡像配置

前言 Maven僅僅是個打包工具而已&#xff0c;個人覺得沒有太大必要花費在打包工具上&#xff0c;這里就列舉一下個人覺得會常用標簽的使用就好了&#xff0c;原理啥的基本就不太會去深度了解了&#xff0c;如果以后遇到需了解Maven工作原理的工作的話&#xff0c;到時候一定分…

idea 導入svn代碼_idea導入svn項目

起初和導入git項目一樣&#xff0c;file - new - project from version control - &#xff0c;這后面選 subversion。在打開的 checkout from subversion對話框中&#xff0c;輸入svn地址&#xff0c;比如 svn://11.22.33.44/demo。添加一個后&#xff0c;展開新加項&#xff…

由mysql8降級到mysql5

最近在研究liferay的使用。liferay可以連接mysql數據庫。電腦中裝的mysql的最新版本是mysql8。于是開始按照liferay的要求進行連接。但是多番嘗試后&#xff0c;均報錯&#xff1a;java.sql.SQLException: java.lang.ClassCastException: java.math.BigInteger cannot be cast …

tf計算矩陣維度_tensorflow中關于 多維tensor的運算(tf.multiply, tf.matmul, tf.tensordot)...

multiply 等同與* &#xff0c;用于計算矩陣之間的element-wise 乘法&#xff0c;要求矩陣的形狀必須一致(或者是其中一個維度為1)&#xff0c;否則會報錯&#xff1a;import tensorflow as tfa tf.constant([1, 2, 3, 4, 5, 6, 7, 8, 9, 10,11,12], shape[2, 3, 2])b tf.con…

Maven高級之插件開發

前言 終于來到了Maven的插件開發&#xff0c;其實Maven的插件并沒有想象的那么難&#xff0c;剛開始講Maven基礎的時候就演示了一下JDK是如何打包的&#xff0c;Maven打包只是在JDK打包上封裝了一層而已&#xff0c;Maven也支持自定義插件開發 創建 我們先使用quickstart原型…

HTTP1.1新增了五種請求方法:OPTIONS、PUT、PATCH、DELETE、TRACE 、 CONNECT

200 &#xff08;成功&#xff09; 服務器已成功處理了請求。 通常&#xff0c;這表示服務器提供了請求的網頁。 201 &#xff08;已創建&#xff09; 請求成功并且服務器創建了新的資源。 202 &#xff08;已接受&#xff09; 服務器已接受請求&#xff0c;但尚未處…

katalon進行app測試_Katalon API 測試 Demo

為何選擇Katalon符合我們當下的情況&#xff0c;測試需要借助現有工具提高測試效率以及提高測試質量&#xff1b;為何不自己寫代碼&#xff1f;不是只有自己寫的框架才是最好的&#xff0c;合適的才是最好的&#xff1b;katalon 支持ui、mobile、api 同時也支持腳本模式&#x…

Maven高級之archetype(原型/骨架)開發

前言 archetype這個的主要功能就是將寫好的項目模塊打包成一個原型&#xff0c;然后提供給其他人使用&#xff0c;這樣別人就可以快速使用這個項目模板了。 這個東西雖然很多人都基本用不上&#xff0c;但原型這個東西用的好還是很方便的&#xff0c;能夠在開發新項目上省去大…

深度學習在搜索業務中的探索與實踐

本文根據美團高級技術專家翟藝濤在2018 QCon全球軟件開發大會上的演講內容整理而成&#xff0c;內容有修改。引言 2018年12月31日&#xff0c;美團酒店單日入住間夜突破200萬&#xff0c;再次創下行業的新紀錄&#xff0c;而酒店搜索在其中起到了非常重要的作用。本文會首先介紹…

cesium面積計算_cesium-長度測量和面積測量

(更新)多謝網友的提醒&#xff0c;面積測量的小問題已經修改&#xff0c;經測試可正常使用網上找的大神的實現方法有點問題&#xff0c;實現有一些bug&#xff0c;作為cesium新手一個&#xff0c;棄之不忍&#xff0c;只好硬著頭皮修改了&#xff0c;不過還好問題不大&#xff…

SpringBoot自動配置原理流程

前言 新公司太忙了&#xff0c;都沒啥空更新博客&#xff0c;就隨便記錄一下以前的學習筆記吧。SpringBoot是基于Spring上的衍生框架&#xff0c;只要看懂了Spring的話&#xff0c;學這個就比較簡單了&#xff1b;SpringBoot也是在當前微服務時代下流行的框架&#xff0c;并且…

算法:對象方式數組去重

var arr [3, 1, 1, 4 , 2 , 4 , 2 , 4 , 2, 1, 1, 3, 3, 3];var ary[];var obj{};for(var i0;i<arr.length;i){var curarr[i];if(!obj[cur]){obj[cur]cur;ary.push(cur);}}console.log(ary); 復制代碼