Java架構學習(四十二)Zookeeper基礎&ZK概述&ZK資料結構&windows搭建ZK&Java操作ZK&ZK建立臨時節點&ZK的Watcher事件通知&架構面試
阿新 • • 發佈:2018-12-21
一、Zookeeper概述
1、什麼是Zookeeper? 答:Zookeeper是分散式開源框架,是分散式協調工具。 2、應用場景: 答:dubbo 是rpc遠端呼叫框架+Zookeeper作為註冊中心,(命名服務) 釋出訂閱 --- wathcher 對zk節點發生改變的時候,都會有事件通知。 3、負載均衡。 4、分散式通知(wathcher) 5、master選舉策略-- 主備 投票機制 使用ping機制心跳檢測 6、Zookeeper實現分散式鎖。redis實現分散式鎖、SpringCLoud分散式鎖 7、使用Zookeeper實現分散式配置中心。 架構上問:Zookeeper實現原理
二、Zookeeper資料結構
1、Zookeeper資料結構 類似 xml 是樹狀儲存結構
2、Zookeeper節點四種節點型別
1、持久節點:不會被刪除,會儲存在硬盤裡。
2、持久順序節點:就是在節點裡面會有編號
3、臨時節點 :跟宣告週期是繫結的,連線斷開了,節點資訊就會直接刪除掉。
4、臨時順序節點:在節點裡面會有個編號。
節點wathcher:就是節點的事件通知。
就是事件監聽,獲取到節點資訊的改變、增刪查改。
dubbo使用的是Zookeeper註冊中心。
使用zk實現釋出訂閱原理 類似訊息佇列機制。
三、windows搭建Zookeeper
命令列命令使用: ls / -- 檢視當前所有的節點 create /leeue test 場景一個新節點 內容是 test set /leeue i 修改節點資訊test 變成 i delete /leeue 刪除
四、使用Java語言操作Zookeeper
pom.xml
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.6</version>
</dependency>
程式碼:
package com.leeue.zk;
import java.io.IOException;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.server.ZKDatabase;
/**
*
* @classDesc: 功能描述:(Java語言去操作 zookeeper)
* @author:<a href=" [email protected]">李月</a>
* @Version:v1.0
* @createTime:2018年11月15日 上午11:12:22
*/
public class Test001 {
private static String ADDRESS = "127.0.0.1";
private static int SESSION_OUT_TIME = 2000;
public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
ZooKeeper zooKeeper = new ZooKeeper(ADDRESS, SESSION_OUT_TIME, new Watcher() {
// 這裡面就是事件通知
public void process(WatchedEvent event) {
/*try {
// 偽造阻塞現象 因為這裡是 一個主執行緒 一個子執行緒在執行
Thread.sleep(20000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}*/
//1 獲取事件狀態
KeeperState state = event.getState();
// 2.判斷當前連線狀態
if(state == KeeperState.SyncConnected) {
// 3.獲取事件型別
EventType eventType = event.getType();
if(eventType == EventType.None) { //None 表示事件是連線的狀態
System.out.println("###ZK開始啟動連線了###");
}
}
}
});
String result = zooKeeper.create("/leeue4", "one".getBytes(), Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);
System.out.println("###新增節點資訊:"+result);
zooKeeper.close();// 關閉連線
}
}
上面是一個在建立連線的是在子執行緒中執行,而建立節點是在主執行緒中執行。
這麼寫有時候會導致,連線還沒建立,就開始建立節點了,就會報錯誤。
導致節點建立不成功。所以下面程式碼 加入了訊號量來實現 。
為什麼ZK建立連線,是建立一個新的執行緒,而不在主執行緒中建立連線?
答:因為如果在主執行緒中建立連線,就有可能一直造成主執行緒的阻塞現象發生。
可能連線要需要很長時間。而導致後面後面程式碼沒法執行。
而使用子執行緒去連線,就不會影響主執行緒中後面程式碼的執行。不會有阻塞現象
傳送。
還有就是為了提高響應速度,才使用多執行緒的來建立連線。
使用CountDownLatch 阻塞使用者程式,使用者必須等待連線成功,傳送成功訊號後
才能建立節點
package com.leeue.zk;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.server.ZKDatabase;
/**
*
* @classDesc: 功能描述:(Java語言去操作 zookeeper)
* @author:<a href="[email protected]">李月</a>
* @Version:v1.0
* @createTime:2018年11月15日 上午11:12:22
*/
public class Test001 {
private static String ADDRESS = "127.0.0.1";
private static int SESSION_OUT_TIME = 2000;
/**
* countDownLatch: 某一個執行緒開始執行前,需要等待前面n個執行緒執行完閉後才能執行。初始化
* 是 new CountDownLatch(n) 每當一個執行緒執行完畢後,就需要 將計速器-1 countDownLatch.countDown()
* 當計數器的值變成0時,countDownLatch.await()上的執行緒就會被喚醒。
* 一個典型應用場景就是啟動一個服務時,主執行緒需要等待多個元件載入完畢,之後再繼續執行。
*/
private static CountDownLatch countDownLatch = new CountDownLatch(1); // 訊號量
public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
ZooKeeper zooKeeper = new ZooKeeper(ADDRESS, SESSION_OUT_TIME, new Watcher() {
// 這裡面就是事件通知
public void process(WatchedEvent event) {
try {
// 偽造阻塞現象 因為這裡是 一個主執行緒 一個子執行緒在執行
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//1 獲取事件狀態
KeeperState state = event.getState();
// 2.判斷當前連線狀態
if(state == KeeperState.SyncConnected) {
// 3.獲取事件型別
EventType eventType = event.getType();
if(eventType == EventType.None) { //None 表示事件是連線的狀態
System.out.println("###ZK開始啟動連線了###");
}
countDownLatch.countDown();
}
}
});
countDownLatch.await();//等待資訊量中的計數器變成0 後才開始執行下面的程式碼
String result = zooKeeper.create("/leeue4", "one".getBytes(), Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);
System.out.println("###新增節點資訊:"+result);
zooKeeper.close();// 關閉連線
}
}
五、建立ZK臨時節點
在ZK中建立的節點都是不能重複的。
ZK也可以設定節點許可權,Ids.
建立臨時節點程式碼。
package com.leeue.zk;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.server.ZKDatabase;
/**
*
* @classDesc: 功能描述:(Java語言去操作 zookeeper)
* @author:<a href="[email protected]">李月</a>
* @Version:v1.0
* @createTime:2018年11月15日 上午11:12:22
*/
public class Test001 {
private static String ADDRESS = "127.0.0.1";
private static int SESSION_OUT_TIME = 2000;
/**
* countDownLatch: 某一個執行緒開始執行前,需要等待前面n個執行緒執行完閉後才能執行。初始化
* 是 new CountDownLatch(n) 每當一個執行緒執行完畢後,就需要 將計速器-1 countDownLatch.countDown()
* 當計數器的值變成0時,countDownLatch.await()上的執行緒就會被喚醒。
* 一個典型應用場景就是啟動一個服務時,主執行緒需要等待多個元件載入完畢,之後再繼續執行。
*/
private static CountDownLatch countDownLatch = new CountDownLatch(1); // 訊號量
public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
ZooKeeper zooKeeper = new ZooKeeper(ADDRESS, SESSION_OUT_TIME, new Watcher() {
// 這裡面就是事件通知
public void process(WatchedEvent event) {
try {
// 偽造阻塞現象 因為這裡是 一個主執行緒 一個子執行緒在執行
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//1 獲取事件狀態
KeeperState state = event.getState();
// 2.判斷當前連線狀態
if(state == KeeperState.SyncConnected) {
// 3.獲取事件型別
EventType eventType = event.getType();
if(eventType == EventType.None) { //None 表示事件是連線的狀態
System.out.println("###ZK開始啟動連線了###");
}
countDownLatch.countDown();
}
}
});
countDownLatch.await();//等待資訊量中的計數器變成0 後才開始執行下面的程式碼
// 建立臨時節點、許可權是所有的伺服器都可以訪問。
String result = zooKeeper.create("/leeue_temp", "one".getBytes(), Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL);
System.out.println("###新增節點資訊:"+result);
zooKeeper.close();// 關閉連線
}
}
會建立節點,但是執行了 zooKeeper.close()時候,節點會被刪除掉。
ZK實現分散式鎖,就是使用臨時節點來實現的。
連線釋放後,節點也就刪除了。
六、ZK中的Watcher事件通知
1、zk watcher事件通知:當節點發生改變、新增、修改、刪除都會有事件通知。
package com.leeue.zk;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.server.ZKDatabase;
/**
* @classDesc: 功能描述:(封裝ZK連線)
* @author:<a href="[email protected]">李月</a>
* @Version:v1.0
* @createTime:2018年11月15日 下午1:48:00
*/
public class Test002 implements Watcher{
//zk連線地址
private static String ADDRESS = "127.0.0.1";
//zk會話超時時間
private static int SESSION_OUT_TIME = 2000;
//訊號量
private static CountDownLatch countDownLatch = new CountDownLatch(1); // 訊號量
//定義成全域性的
ZooKeeper zooKeeper;
//建立zookeeper連線
public void createConnection(String connectString,int sessionTimeout){
try {
zooKeeper = new ZooKeeper(connectString, sessionTimeout, this);
} catch (IOException e) {
e.printStackTrace();
}
}
//建立持久化節點
public boolean createNode(String path,String data) {
// 如果沒有異常就是說明該幾點就建立成功。
try {
//建立節點 開啟事件通知
exists(path,true);
String result = zooKeeper.create(path, data.getBytes(), Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);
System.out.println("###新增節點成功path:"+path+"data:"+data+"新增節點資訊:"+result);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
// 走事件通知的方法
public void process(WatchedEvent event) {
System.out.println("");
System.out.println("#####事件通知開始#####");
//1.獲取事件狀態
KeeperState keeperState = event.getState();
//2.獲取節點路徑
String path = event.getPath();
//3.獲取事件型別
EventType type = event.getType();
System.out.println("###進入process方法 狀態:"+keeperState+"路徑:"+path+"事件型別:"+type);
// 4.判斷當前連線狀態
if(keeperState == KeeperState.SyncConnected) {
// 判斷事件型別 None 表示建立連線成功了
if(type == EventType.None) { //None 表示事件是連線的狀態
System.out.println("###ZK開始啟動連線了###");
countDownLatch.countDown();
}else if(type == EventType.NodeCreated){
System.out.println("###獲取到事件通知,當前node節點被建立+"+path+"###");
}else if(type == EventType.NodeDataChanged){
System.out.println("###獲取到事件通知,當前node節點被修改+"+path+"###");
}else if(type == EventType.NodeDeleted){
System.out.println("###獲取到事件通知,當前node節點被刪除+"+path+"###");
}
}
System.out.println("");
System.out.println("#####事件通知結束#####");
}
// 傳送事件通知 只有實現了這個方法才能傳送事件通知
public Stat exists(String path, boolean isWatch) {
try {
return zooKeeper.exists(path, isWatch);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
// 關閉連線
public void close() {
try {
zooKeeper.close();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String[] args) {
Test002 test002 = new Test002();
test002.createConnection(ADDRESS, SESSION_OUT_TIME);
test002.createNode("/leeue", "1");
test002.close();
}
}
注意必須要開啟事件通知才會有通知。
上面update
作業題:zk事件通知有延遲的情況下,怎麼處理。
zk實現分散式鎖 臨時節點
zk底層實現原理
zk負載均衡
zk選舉
zk分散式配置中心