您好,登錄后才能下訂單哦!
ZooKeeper 是一個開源的分布式協(xié)調(diào)服務(wù),由雅虎創(chuàng)建,是 Google Chubby 的開源實現(xiàn)。分布式應(yīng)用程序可以基于 ZooKeeper 實現(xiàn)諸如數(shù)據(jù)發(fā)布/訂閱、負(fù)載均衡、命名服務(wù)、分布式協(xié)調(diào)/通知、集群管理、Master 選舉、配置維護,名字服務(wù)、分布式同步、分布式鎖和分布式隊列等功能。
數(shù)據(jù)模型:ZooKeeper 允許分布式進(jìn)程通過共享的層次結(jié)構(gòu)命名空間進(jìn)行相互協(xié)調(diào),這與標(biāo)準(zhǔn)文件系統(tǒng)類似。名稱空間由 ZooKeeper 中的數(shù)據(jù)寄存器組成,稱為 Znode,這些類似于文件和目錄。與典型文件系統(tǒng)不同,ZooKeeper 數(shù)據(jù)保存在內(nèi)存中,這意味著 ZooKeeper 可以實現(xiàn)高吞吐量和低延遲。
順序訪問:對于來自客戶端的每個更新請求,ZooKeeper 都會分配一個全局唯一的遞增編號。這個編號反應(yīng)了所有事務(wù)操作的先后順序,應(yīng)用程序可以使用 ZooKeeper 這個特性來實現(xiàn)更高層次的同步原語。這個編號也叫做時間戳—zxid(ZooKeeper Transaction Id)。
可構(gòu)建集群:為了保證高可用,最好是以集群形態(tài)來部署 ZooKeeper,這樣只要集群中大部分機器是可用的(能夠容忍一定的機器故障),那么 ZooKeeper 本身仍然是可用的??蛻舳嗽谑褂?ZooKeeper 時,需要知道集群機器列表,通過與集群中的某一臺機器建立 TCP 連接來使用服務(wù)??蛻舳耸褂眠@個 TCP 鏈接來發(fā)送請求、獲取結(jié)果、獲取監(jiān)聽事件以及發(fā)送心跳包。如果這個連接異常斷開了,客戶端可以連接到另外的機器上。
工作原理:
Leader選舉:
1 //客戶端連接zookeeper服務(wù)器
2 ZooKeeper zkClient = new ZooKeeper(CONNECT_STR, 50000, new Watcher() {
3 @Override
4 public void process(WatchedEvent watchedEvent) {
5 //監(jiān)控服務(wù)節(jié)點變化
6 System.out.println("sssss");
7 }
8 });
9
10 //獲取根節(jié)點下的所有節(jié)點
11 List<String> nodeList= zkClient.getChildren("/",null);
12
13 System.out.println(nodeList.toString());
14
15 //Stat isExists= zkClient.exists(LOCK_ROOT_PATH,null);
16 //在test父節(jié)點下創(chuàng)建子節(jié)點
17 String lockPath = zkClient.create("/test/why","why".getBytes(),
18 ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL);
代碼中需要注意的是如果父節(jié)點不存在,會報異常,同時父節(jié)點不能是臨時節(jié)點。
Znode:
Session:
Watcher:是 ZooKeeper 中的一個很重要的特性。ZooKeeper 允許用戶在指定節(jié)點上注冊一些 Watcher,并且在一些特定事件觸發(fā)的時候,ZooKeeper 服務(wù)端會將事件通知到感興趣的客戶端上去,該機制是 ZooKeeper 實現(xiàn)分布式協(xié)調(diào)服務(wù)的重要特性。
Version: Zookeeper 的每個 ZNode 上都會存儲數(shù)據(jù),對應(yīng)于每個 ZNode,Zookeeper 都會為其維護一個叫作 Stat 的數(shù)據(jù)結(jié)構(gòu)。Stat 中記錄了這個 ZNode 的三個數(shù)據(jù)版本,分別是:version(當(dāng)前節(jié)點版本)、cversion(當(dāng)前節(jié)點的子節(jié)點版本)、aversion(當(dāng)前節(jié)點的ACL版本)
ACL:ZooKeeper 采用 ACL(AccessControlLists)策略來進(jìn)行權(quán)限控制,類似于 UNIX 文件系統(tǒng)的權(quán)限控制。ZooKeeper 定義了 5 種權(quán)限:CREATE/READ/WRITE/DELETE/ADMIN
package com.why;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
/*
* 分布式鎖
* */
public class DistributeLock {
private static final String LOCK_ROOT_PATH = "/test";
//private static final String LOCK_NODE_NAME = "Lock";
private static ZooKeeper _zkClient;
static {
try {
_zkClient = new ZooKeeper("192.168.6.132:2181", 500000, null);
} catch (IOException e) {
e.printStackTrace();
}
}
public static String getLock() {
try {
//System.out.println(_zkClient.getChildren("/",false));
String lockPath = _zkClient.create( "/test/why", null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
//System.out.println(lockPath);
//System.out.println(_zkClient.getChildren(LOCK_ROOT_PATH,false));
if (tryLock(lockPath))
return lockPath;
else
return null;
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
private static boolean tryLock(String lockPath) throws KeeperException, InterruptedException {
List<String> lockPaths = _zkClient.getChildren(LOCK_ROOT_PATH, false);
Collections.sort(lockPaths);
int index=lockPaths.indexOf(lockPath.substring(LOCK_ROOT_PATH.length()+1));
if(index==0){
//獲得鎖
return true;
}
else{
String preLockPath="/"+lockPaths.get(index-1);
Watcher watcher=new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
synchronized (this){
//喚醒線程
notifyAll();
}
}
};
Stat stat=_zkClient.exists(preLockPath,watcher);
if(stat==null){
return tryLock(lockPath);
}else{
synchronized (watcher){
watcher.wait();
}
return tryLock(lockPath);
}
}
}
public static void closeZkClient() throws InterruptedException {
_zkClient.close();
}
public static void releaseLock(String lockPath) throws KeeperException, InterruptedException {
_zkClient.delete(lockPath,-1);
}
}
測試:
package com.why;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooKeeper;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MultiThreadDemo {
private static int counter = 0;
public static void plus() throws InterruptedException {
Thread.sleep(500);
counter++;
//System.out.println(counter);
}
public static int Count(){
return counter;
}
public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
ExecutorService executor= Executors.newCachedThreadPool();
final int num=10;
for(int i=0;i<num;i++){
executor.submit(new Runnable() {
@Override
public void run() {
try {
String path = DistributeLock.getLock();
System.out.println(path);
plus();
DistributeLock.releaseLock(path);
System.out.println(Count());
} catch (InterruptedException | KeeperException e) {
e.printStackTrace();
}
}
});
}
executor.shutdown();
}
}
針對于上面所涉及到的知識點我總結(jié)出了有1到5年開發(fā)經(jīng)驗的程序員在面試中涉及到的絕大部分架構(gòu)面試題及答案做成了文檔和架構(gòu)視頻資料免費分享給大家(包括Dubbo、Redis、Netty、zookeeper、Spring cloud、分布式、高并發(fā)等架構(gòu)技術(shù)資料),希望能幫助到您面試前的復(fù)習(xí)且找到一個好的工作,也節(jié)省大家在網(wǎng)上搜索資料的時間來學(xué)習(xí),也可以關(guān)注我一下以后會有更多干貨分享。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。