系統中存在批量審批、批量授權等高併發操作。因處理機器、線程不同,導致緩存刷新時出現數據互相覆蓋丟失:例如同一用戶多個權限申請並發發送至 ACL 不同服務器,機器 A 處理授權 A,機器 B 同時處理授權 B,事務隔離導致雙方看不見對方提交的數據,刷新緩存時從 DB 讀取各自可見數據,最終造成權限丟失。核心需求:跨機器、跨線程的協調同步,即分布式鎖。
誤區警示:ReentrantLock 僅能解決單進程內線程同步,其 lock/unlock 必須在同一線程,無法用於分布式場景。
ZooKeeper 是分布式協調服務框架,基於觀察者模式,提供類文件系統的目錄節點樹(ZNode)存儲數據並監控狀態變化。核心特性:
| 方法 | 功能 |
|---|---|
| create(path, data, acl, mode) | 創建節點,mode 指定四類節點類型 |
| exists(path, watch/watcher) | 判斷節點存在並註冊監聽 |
| getChildren(path, watch/watcher) | 獲取子節點列表 |
| setData(path, data, version) | 更新節點數據,版本號樂觀鎖 |
| getData(path, watch/watcher, stat) | 讀取節點數據及元數據 |
| delete(path, version) | 刪除節點,-1 匹配所有版本 |
ZooKeeper zk = new ZooKeeper("localhost:2181", 3000, event ->
System.out.println("觸發事件: " + event.getType()));
zk.create("/testRootPath", "testRootData".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
zk.create("/testRootPath/testChildPathOne", "testChildDataOne".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println(new String(zk.getData("/testRootPath", false, null))); // testRootData
System.out.println(zk.getChildren("/testRootPath", true)); // [testChildPathOne]
zk.setData("/testRootPath/testChildPathOne", "modifyChildDataOne".getBytes(), -1);
zk.create("/testRootPath/testChildPathTwo", "testChildDataTwo".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
zk.delete("/testRootPath/testChildPathTwo", -1);
zk.delete("/testRootPath/testChildPathOne", -1);
zk.delete("/testRootPath", -1);
zk.close();
利用 ZNode 唯一路徑特性生成全局唯一名稱,類似 JNDI 但更輕量,調用 create 即可實現。
將配置存儲於 ZNode,所有應用機器 getData + watch 監聽,配置變更推送通知,各機器拉取最新配置熱加載,實現配置中心核心功能。
getChildren + watch,節點刪減/新增觸發變更通知。void findLeader() {
byte[] leader = zk.getData(root + "/leader", true, null);
if (leader != null) { following(); }
else {
String newLeader = zk.create(root + "/leader", localhostBytes, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
if (newLeader != null) leading(); else mutex.wait();
}
}
加鎖流程:
_locknode_/lock- 臨時順序節點。getChildren 獲取所有鎖節點,取最小編號。exists(比自己小一號節點, watch=true) 監聽,收到刪除通知回到第 2 步。解鎖:刪除自己創建的臨時節點即可。
void getLock() {
List<String> list = zk.getChildren(root, false);
String[] nodes = list.toArray(new String[0]);
Arrays.sort(nodes);
if (myZnode.equals(root + "/" + nodes[0])) doAction();
else waitForLock(nodes[0]);
}
void waitForLock(String lower) {
Stat stat = zk.exists(root + "/" + lower, true);
if (stat != null) mutex.wait(); else getLock();
}
/start 觸發所有成員 watch 執行任務。/queue_i,消費者 getChildren 取最小編號消費並刪除,保證先進先出。ZooKeeper 作為 Hadoop 生態核心組件,通過層次化目錄樹 + Watch 機制,優雅解決了分布式系統中的配置管理、服務發現、主從選舉、分布式鎖、隊列協調等經典問題。實際落地注意:
參考資料:ZooKeeper 典型應用場景深度解析