說到分布式開發,不得不說的就是zookeeper了;zookeeper官網說到Apache ZooKeeper致力於開發和維護可實現高度可靠的分布式協調的開源服務器。那么zk作為一個協調者的存在,是分布式比不可少的一部分。廢話不多說,直接上干貨
Zookeeper(https://zookeeper.apache.org/)的安裝包可以直接在官網上獲取,https://zookeeper.apache.org/doc/current/zookeeperStarted.html這里有Zookeeper的一些常用的簡單命令,我們可以嘗試着去試試它。
下面來說分布式鎖,它用到的場景;比如:我們常說的驚群效應、Zookeeper集群爭先讀取緩存等。這里可能有人提到用redis實現的分布式鎖,其實對比redis和Zookeeper的官網敘述,我們就能清晰的發現:Zookeeper比Redis更適合去做分布式鎖。Zookeeper的擔保第一條就是它的順序性和一致性,其次是它的原子性。大家也可以詳細的去了解一下。再分清楚這些之后,我們可以試着思考下Zookeeper它是怎么去實現的分布式鎖?根據什么去實現的?
可以想到如何獲取到Zookeeper的每個節點以及子節點變化,Zookeeper的Watch機制充分的實現了這一點。通過Watch機制可以清晰的監聽到Zookeeper的每個節點的變化。自然而然的這里也要用到Zookeeper的第三方客戶端。Zookeeper的第三方客戶端有兩個;一個是zkclient、一個是curator。在curator中它自己已經實現了分布式鎖,感興趣的可以去看看它的實現源碼。
在第三方客戶端連接到Zookeeper之后,就可以開始實現分布式鎖了。鎖的排他性、堵塞性、可重入性。排他性zk默認就實現了,zk的節點必須是唯一的。分布式鎖采用Zookeeper的臨時順序節點來實現,首先獲取鎖,創建一個Zookeeper的臨時順序節點;然后需要一個柵欄(CountDownLatch),確保所有人都拿到自己的編號,即在同一起跑線上。然后開始搶鎖,給一個發令槍(CountDown)。然后搶到鎖的就去執行自己的業務,watch再次監聽節點數據變化,然后請求線程去進行阻塞等待,接下來再刪除節點,釋放鎖。
1 public boolean tryLock() { 2 3 if(currentPath.get() == null || !client.exists(currentPath.get())) { 4 String node = client.createEphemeralSequential(LockPath+"/", "locked"); 5 currentPath.set(node); 6 } 7 8 9 List<String> children =client.getChildren(LockPath); 10 // 排序list 11 Collections.sort(children); 12 13 // 判斷當前節點是否是最小的 14 if(currentPath.get().equals(LockPath+"/"+children.get(0))) { 15 return true; 16 }else { 17 18 int curIndex = children.indexOf(currentPath.get().substring(LockPath.length() + 1)); 19 String bnode = LockPath+"/"+children.get(curIndex -1); 20 beforePath.set(bnode); 21 } 22 return false; 23 } 24 25 26 public void lock() { 27 if(! tryLock()) { 28 // 阻塞等待鎖的釋放 29 waitForLock(); 30 // 重新搶鎖 31 lock(); 32 } 33 } 34 private void waitForLock() { 35 36 CountDownLatch cdl = new CountDownLatch(1); 37 // 用zkwatcher事件來通知 38 IZkDataListener listener = new IZkDataListener() { 39 40 public void handleDataDeleted(String dataPath) throws Exception { 41 System.out.println("================zk節點被刪除================"); 42 cdl.countDown(); 43 } 44 45 public void handleDataChange(String dataPath, Object data) throws Exception { 46 } 47 }; 48 // watcher /zk 數據變化 49 client.subscribeDataChanges(beforePath.get(), listener); 50 51 // 請求線程去進行阻塞等待 52 if(client.exists(beforePath.get())) { 53 try { 54 cdl.await(); 55 } catch (InterruptedException e) { 56 e.printStackTrace(); 57 } 58 } 59 // 取消注冊事件 60 client.unsubscribeDataChanges(beforePath.get(), listener); 61 }
至此一個分布式鎖就實現了。
接下來說Zookeeper的偽集群。搭建的時候需要注意的情況:
1、DataDir的位置。
2、Zookeeper的端口號
3、
4、myid文件,myid文件是創建在DataDir目錄下的,myid文件就是給一個id數,id數需與server.1 ... 后邊的編號一致。
然后分別啟動三台服務器,注意看服務器輸出的日志消息。不出意外的話Zookeeper的集群就可以搭建成功了!

