基於zookeeper的分散式鎖實現
阿新 • • 發佈:2019-02-19
之前已經實現過基於redis的分散式鎖
這次用zookeeper來實現.
原理:ZooKeeper有四種形式的目錄節點,四種CreateMode
- PERSISTENT:持久化目錄節點,儲存的資料不會丟失。
- PERSISTENT_SEQUENTIAL:順序自動編號的持久化目錄節點,儲存的資料不會丟失,並且根據當前已近存在的節點數自動加 1,然後返回給客戶端已經成功建立的目錄節點名。
- EPHEMERAL:臨時目錄節點,一旦建立這個節點的客戶端與伺服器埠也就是session 超時,這種節點會被自動刪除。
- EPHEMERAL_SEQUENTIAL:臨時自動編號節點,一旦建立這個節點的客戶端與伺服器埠也就是session 超時,這種節點會被自動刪除,並且根據當前已近存在的節點數自動加 1,然後返回給客戶端已經成功建立的目錄節點名。
用EPHEMERAL_SEQUENTIAL即可實現分散式鎖.當獲取鎖的時候,節點會自動增加,如果你的節點是最小的,那麼你就獲取鎖.不然就等待.(只需等待比你大的節點是否還存在,如果不存在,重複獲取)
##程式碼
package demo.zookeeper.lock; import demo.zookeeper.AbstractZooKeeper; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import java.io.IOException; import java.util.Comparator; import java.util.List; /** * 基於Zookeeper的分散式鎖 * Created by yuyufeng on 2017/8/22. */ public class LockUtil { //鎖失效時間10秒 private static final int TIME_OUT = 10000; private static final String HOST = "localhost:2181"; private static ZooKeeper zookeeper; private static String ROOT_PATH = "/lockdata"; static { try { zookeeper = new ZooKeeper(HOST, TIME_OUT, new AbstractZooKeeper()); try { //建立鎖節點存放根目錄 if (zookeeper.exists(ROOT_PATH, false) == null) { zookeeper.create(ROOT_PATH, "true".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } } catch (Exception e) {} } catch (IOException e) {} } /** * 獲取一個鎖 * @param key 同步監聽物件 * @return */ public static boolean getLock(String key) { Long beginTime = System.currentTimeMillis(); try { if (zookeeper.exists(ROOT_PATH + "/" + key, false) == null) { zookeeper.create(ROOT_PATH + "/" + key, "true".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } } catch (KeeperException e) {} catch (InterruptedException e) {} String result = null; try { result = zookeeper.create(ROOT_PATH + "/" + key + "/", "true".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); } catch (KeeperException e) {} catch (InterruptedException e) {} result = result.substring(result.lastIndexOf("/") + 1, result.length()); return lockHandler(key, beginTime, result); } private static boolean lockHandler(String key, Long beginTime, String result){ List<String> list = null; try { list = zookeeper.getChildren(ROOT_PATH + "/" + key, false); } catch (KeeperException e) {} catch (InterruptedException e) {} if (list == null || list.size() == 0) { System.out.println(Thread.currentThread().getName()+"獲取鎖異常,失敗!"); return false; } System.out.println("=========================================="); list.sort(new Comparator<String>() { @Override public int compare(String o1, String o2) { return Integer.valueOf(o1) - Integer.valueOf(o2); } }); System.out.println(Thread.currentThread().getName()+"獲取鎖結果: " + result + " :" + list.get(0)); if (result.equals(list.get(0))) { System.out.println(Thread.currentThread().getName()+ROOT_PATH + "/" + key + " 獲取鎖成功"); return true; } //為獲取鎖失敗,監聽當前節點之前的節點,如之前的節點已經不存在,再次獲取. String beforeNode = list.get(0); for (int i = 0; i < list.size(); i++) { if (result.equals(list.get(i))) { beforeNode = list.get(--i); break; } } //監聽上一個節點5秒,如果上一個節點不存在,則再去獲取鎖 while ((System.currentTimeMillis() - beginTime) < 5000) { System.out.println(Thread.currentThread().getName()+" 正在監聽節點 " + ROOT_PATH + "/" + key + "/" + beforeNode); try { if (zookeeper.exists(ROOT_PATH + "/" + key + "/" + beforeNode, false) == null) { return lockHandler(key, beginTime, result); } } catch (KeeperException e) {} catch (InterruptedException e) {} try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName()+"之前的節點" + beforeNode + "未釋放,放棄獲取鎖"); try { zookeeper.delete(ROOT_PATH + "/" + key + "/" + result, -1); } catch (InterruptedException e) {} catch (KeeperException e) {} return false; } /** * 刪除一個監聽物件下所有的節點 * @param key * @return */ public static boolean unLockNode(String key){ List<String> list = null; try { list = zookeeper.getChildren(ROOT_PATH + "/" + key, false); } catch (KeeperException e) { } catch (InterruptedException e) { } if (list != null) { for (String s : list) { try { zookeeper.delete(ROOT_PATH + "/" + key + "/" + s, -1); } catch (InterruptedException e) { } catch (KeeperException e) { } } } return true; } /** * 解鎖當前獲取鎖的節點 * * @param key * @return * @throws InterruptedException * @throws KeeperException */ public static boolean unLock(String key) { System.out.println("開始解鎖-------------"); List<String> list = null; try { list = zookeeper.getChildren(ROOT_PATH + "/" + key, false); } catch (KeeperException e) {} catch (InterruptedException e) {} if (list == null || list.size() == 0) { System.out.println(Thread.currentThread().getName()+"鎖不存在,或已經被解鎖,成功!"); return false; } System.out.println("=========================================="); list.sort(new Comparator<String>() { @Override public int compare(String o1, String o2) { return Integer.valueOf(o1) - Integer.valueOf(o2); } }); try { zookeeper.delete(ROOT_PATH + "/" + key + "/" + list.get(0), -1); } catch (InterruptedException e) {} catch (KeeperException e) {} try { if (zookeeper.exists(ROOT_PATH + "/" + key + "/" + list.get(0), false) != null) { System.out.println(Thread.currentThread().getName()+" 解鎖失敗!"); return false; } } catch (KeeperException e) {} catch (InterruptedException e) {} System.out.println(Thread.currentThread().getName()+" 解鎖成功!"); return true; } public static void main(String[] args) { // System.out.println(LockUtil.getLock("a1")); // System.out.println(LockUtil.unLock("a1")); new Thread() { @Override public void run() { System.out.println(LockUtil.getLock("a1")); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(LockUtil.unLock("a1")); }}.start(); new Thread() { @Override public void run() { System.out.println(LockUtil.getLock("a1")); System.out.println(LockUtil.unLock("a1")); }}.start(); new Thread() { @Override public void run() { System.out.println(LockUtil.getLock("a1")); }}.start(); LockUtil.unLockNode("a1"); } }
##執行
========================================== ========================================== ========================================== Thread-2獲取鎖結果: 0000000301 :0000000300 Thread-0獲取鎖結果: 0000000300 :0000000300 Thread-1獲取鎖結果: 0000000302 :0000000300 Thread-0/lockdata/a1 獲取鎖成功 Thread-2 正在監聽節點 /lockdata/a1/0000000300 true Thread-1 正在監聽節點 /lockdata/a1/0000000301 Thread-2 正在監聽節點 /lockdata/a1/0000000300 Thread-1 正在監聽節點 /lockdata/a1/0000000301 Thread-2 正在監聽節點 /lockdata/a1/0000000300 Thread-1 正在監聽節點 /lockdata/a1/0000000301 Thread-2 正在監聽節點 /lockdata/a1/0000000300 Thread-1 正在監聽節點 /lockdata/a1/0000000301 開始解鎖------------- ========================================== Thread-0 解鎖成功! true Thread-2 正在監聽節點 /lockdata/a1/0000000300 ========================================== Thread-2獲取鎖結果: 0000000301 :0000000301 Thread-2/lockdata/a1 獲取鎖成功 true Thread-1 正在監聽節點 /lockdata/a1/0000000301 Thread-1 正在監聽節點 /lockdata/a1/0000000301 Thread-1 正在監聽節點 /lockdata/a1/0000000301 Thread-1 正在監聽節點 /lockdata/a1/0000000301 Thread-1 正在監聽節點 /lockdata/a1/0000000301 Thread-1 正在監聽節點 /lockdata/a1/0000000301 Thread-1之前的節點0000000301未釋放,放棄獲取鎖 false
使用zookeeper相對使用redis作為分散式鎖的區別
優勢 | 缺點 |
---|---|
zook可作讀寫鎖.(實現方式:使用讀寫兩種方式,獲取讀鎖時當獲取的節點前面有比自己小的寫鎖節點存在,有則獲取失敗.獲取寫鎖則必須當前節點為最小的節點) | 在實現過程中可以看出,效率沒有redis的高,固應對高併發能量沒有使用redis做分散式鎖強 |