原創播客,如需轉載請註明出處。原文地址:http://www.cnblogs.com/crawl/p/8352919.html html
----------------------------------------------------------------------------------------------------------------------------------------------------------java
筆記中提供了大量的代碼示例,須要說明的是,大部分代碼示例都是本人所敲代碼並進行測試,不足之處,請你們指正~git
本博客中全部言論僅表明博主本人觀點,如有疑惑或者須要本系列分享中的資料工具,敬請聯繫 qingqing_crawl@163.comgithub
GitHub:https://github.com/QingqingQiapache
-----------------------------------------------------------------------------------------------------------------------------------------------------------服務器
前言:ZooKeeper 是提供少許數據存儲和管理的分佈式協調服務。適合存儲狀態管理信息,能夠進行數據的讀寫,同步,提供對數據節點的監聽功能。利用 ZooKeeper 能夠實現不少功能,好比:Hadoop2.0,使用 Zookeeper 的事件處理確保整個集羣只有一個活躍的 NameNode,存儲配置信息等;能夠利用 ZooKeeper 感知集羣中哪臺主機宕機或者下線等等。今天介紹另外一個經常使用的功能,利用 Zookeeper 實現分佈式共享鎖。session
利用 Zookeeper 實現分佈式共享鎖,能夠作到一次只有指定個數的客戶端訪問服務器的某些資源。分佈式
利用 Zookeeper 實現分佈式共享鎖的步驟大體能夠分爲如下幾步:ide
1. 客戶端上線即向 Zookeeper 註冊,建立一把鎖工具
2. 判斷是否只有一個客戶端工做,若只有一個客戶端工做,此客戶端能夠處理業務
3. 獲取父節點下注冊的全部鎖,經過判斷本身是不是號碼最小的那一把鎖,如果則能夠處理業務,不然等待
值的注意的是,在某一客戶端獲取到鎖處理完業務後,必須釋放鎖
1. 新建一個 DistributedLock 類
private ZooKeeper zkClient = null; //鏈接字符串 private static final String connectString = "zookeeper01:2181,zookeeper02:2181,zookeeper03:2181"; //超時時間 private static final int sessionTimeout = 2000; //父節點 private static final String parentNode = "/locks"; //記錄本身建立子節點的路徑 private volatile String thisPath; public static void main(String[] args) throws Exception { //1.獲取 ZooKeeper 的客戶端鏈接 DistributedLock distLock = new DistributedLock(); distLock.getZKClient(); //2.註冊一把鎖 distLock.regiestLock(); //3.監聽父節點,判斷是否只有本身在線 distLock.watchParent(); }
2. main 方法中定義了三個方法
1)getZKClient():用來獲取 Zookeeper 客戶端的鏈接
其中 process 方法是當監聽節點發生變化時調用,其中獲取定義的父節點的全部子節點,而後判斷當前節點是不是最小節點,如果則進行業務邏輯處理階段,並從新註冊一把新的鎖
//獲取 zk 客戶端 public void getZKClient() throws Exception { zkClient = new ZooKeeper(connectString, sessionTimeout, new Watcher() { @Override public void process(WatchedEvent event) { //判斷事件類型,只處理子節點變化事件 if(event.getType() == EventType.NodeChildrenChanged && event.getPath().equals(parentNode)) { try { List<String> childrens = zkClient.getChildren(parentNode, true); //判斷本身是不是最小的 String thisNode = thisPath.substring((parentNode + "/").length()); Collections.sort(childrens); if(childrens.indexOf(thisNode) == 0){ //處理業務邏輯 dosomething(); //從新註冊一把新的鎖 thisPath = zkClient.create(parentNode + "/lock", null, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); } } catch (Exception e) { e.printStackTrace(); } } } }); }
2)main 中的第二個方法是 rediestLock()
調用 Zookeeper 客戶端的 create() 方法,創建一個新的節點
//註冊一把鎖 public void regiestLock() throws Exception { thisPath = zkClient.create(parentNode + "/lock", null, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); }
3)第三個是 watchParent() 方法
在此方法中判斷是否只有一個節點在線,若只有本身一個節點,則調用業務處理的方法
//監聽父節點,判斷是否只有本身在線 public void watchParent() throws Exception { List<String> childrens = zkClient.getChildren(parentNode, true); if (childrens != null && childrens.size() == 1) { //只有本身在線,處理業務邏輯(處理完業務邏輯,必須刪釋放鎖) dosomething(); } else { //不是隻有本身在線,說明別人已經獲取到鎖,等待 Thread.sleep(Long.MAX_VALUE); } }
4)最後一個是自定義的業務邏輯方法
須要注意的是,當處理完業務邏輯後,必須釋放鎖
//業務邏輯方法,注意:須要在最後釋放鎖 public void dosomething() throws Exception { System.out.println("或獲得鎖:" + thisPath); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } finally { System.out.println("釋放鎖:" + thisPath); zkClient.delete(thisPath, -1); } }
3. 最後貼一下所有代碼
package com.software.bigdata.zkdistlock; import java.util.Collections; import java.util.List; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.Watcher.Event.EventType; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; /** * @Description: 分佈式共享鎖 * * @author Crawl * @date 2018年1月25日 下午5:02:42 */ public class DistributedLock { private ZooKeeper zkClient = null; //鏈接字符串 private static final String connectString = "zookeeper01:2181,zookeeper02:2181,zookeeper03:2181"; //超時時間 private static final int sessionTimeout = 2000; //父節點 private static final String parentNode = "/locks"; //記錄本身建立子節點的路徑 private volatile String thisPath; public static void main(String[] args) throws Exception { //1.獲取 ZooKeeper 的客戶端鏈接 DistributedLock distLock = new DistributedLock(); distLock.getZKClient(); //2.註冊一把鎖 distLock.regiestLock(); //3.監聽父節點,判斷是否只有本身在線 distLock.watchParent(); } //業務邏輯方法,注意:須要在最後釋放鎖 public void dosomething() throws Exception { System.out.println("或獲得鎖:" + thisPath); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } finally { System.out.println("釋放鎖:" + thisPath); zkClient.delete(thisPath, -1); } } //監聽父節點,判斷是否只有本身在線 public void watchParent() throws Exception { List<String> childrens = zkClient.getChildren(parentNode, true); if (childrens != null && childrens.size() == 1) { //只有本身在線,處理業務邏輯(處理完業務邏輯,必須刪釋放鎖) dosomething(); } else { //不是隻有本身在線,說明別人已經獲取到鎖,等待 Thread.sleep(Long.MAX_VALUE); } } //註冊一把鎖 public void regiestLock() throws Exception { thisPath = zkClient.create(parentNode + "/lock", null, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); } //獲取 zk 客戶端 public void getZKClient() throws Exception { zkClient = new ZooKeeper(connectString, sessionTimeout, new Watcher() { @Override public void process(WatchedEvent event) { //判斷事件類型,只處理子節點變化事件 if(event.getType() == EventType.NodeChildrenChanged && event.getPath().equals(parentNode)) { try { List<String> childrens = zkClient.getChildren(parentNode, true); //判斷本身是不是最小的 String thisNode = thisPath.substring((parentNode + "/").length()); Collections.sort(childrens); if(childrens.indexOf(thisNode) == 0){ //處理業務邏輯 dosomething(); //從新註冊一把新的鎖 thisPath = zkClient.create(parentNode + "/lock", null, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); } } catch (Exception e) { e.printStackTrace(); } } } }); } }