1. 程式人生 > >Java架構學習(四十二)Zookeeper基礎&ZK概述&ZK資料結構&windows搭建ZK&Java操作ZK&ZK建立臨時節點&ZK的Watcher事件通知&架構面試

Java架構學習(四十二)Zookeeper基礎&ZK概述&ZK資料結構&windows搭建ZK&Java操作ZK&ZK建立臨時節點&ZK的Watcher事件通知&架構面試

一、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分散式配置中心