第1章 Zookeeper入門1.1 概述1.2 特點1.3 數據結構1.4 應用場景1.5 下載地址第2章 Zookeeper安裝2.1 本地模式安裝部署2.2 配置參數解讀第3章 Zookeeper內部原理3.1 選舉機制(面試重點)3.2 節點類型3.3 stat結構體3.4 監聽器原理(面試重點)3.5 寫數據流程第4章 Zookeeper實戰(開發重點)4.1 分布式安裝部署4.2 客戶端命令行操作4.3 API應用4.3.1 Eclipse環境搭建4.3.2 創建ZooKeeper客戶端4.3.3 創建子節點4.3.4 獲取子節點並監聽節點變化4.3.5 判斷Znode是否存在4.4 監聽服務器節點動態上下線案例第5章 企業面試真題5.1 請簡述ZooKeeper的選舉機制(半數機制)5.2 ZooKeeper的監聽原理是什么?5.3 ZooKeeper的部署方式有哪幾種?集群中的角色有哪些?集群最少需要幾台機器?5.4 ZooKeeper的常用命令
第1章 Zookeeper入門
1.1 概述
Zookeeper是一個開源的分布式的,為分布式應用提供協調服務的Apache項目。

1.2 特點

1.3 數據結構

1.4 應用場景
提供的服務包括:統一命名服務、統一配置管理、統一集群管理、服務器節點動態上下線、軟負載均衡等。
統一命名服務

統一配置管理

統一集群管理

服務器節點動態上下線

軟負載均衡

1.5 下載地址
1、官網首頁:
https://zookeeper.apache.org/

2、下載截圖,如下圖所示


第2章 Zookeeper安裝
2.1 本地模式安裝部署
1、安裝前准備
(1)安裝jdk
(2)拷貝Zookeeper安裝包到Linux系統下
(3)解壓到指定目錄
[atguigu@hadoop102 software]$ tar -zxvf zookeeper-3.4.10.tar.gz -C /opt/module/
2、配置修改
(1)將/opt/module/zookeeper-3.4.10/conf/這個路徑下的zoo_sample.cfg修改為zoo.cfg;
[atguigu@hadoop102 conf]$ pwd
/opt/module/zookeeper-3.4.10/conf
[atguigu@hadoop102 conf]$ mv zoo_sample.cfg zoo.cfg
(2)打開zoo.cfg文件,修改dataDir路徑:
[atguigu@hadoop102 zookeeper-3.4.10]$ vim zoo.cfg
修改如下內容:
dataDir=/opt/module/zookeeper-3.4.10/zkData
(3)在/opt/module/zookeeper-3.4.10/這個目錄上創建zkData文件夾
[atguigu@hadoop102 zookeeper-3.4.10]$ mkdir zkData
3、操作Zookeeper
(1)啟動Zookeeper:
[atguigu@hadoop102 zookeeper-3.4.10]$ bin/zkServer.sh start
(2)查看進程是否啟動:
[atguigu@hadoop102 zookeeper-3.4.10]$ jps
4020 Jps
4001 QuorumPeerMain
(3)查看狀態:
[atguigu@hadoop102 zookeeper-3.4.10]$ bin/zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /opt/module/zookeeper-3.4.10/bin/../conf/zoo.cfg
Mode: standalone
(4)啟動客戶端:
[atguigu@hadoop102 zookeeper-3.4.10]$ bin/zkCli.sh
(5)退出客戶端:
[zk: localhost:2181(CONNECTED) 0] quit
(6)停止Zookeeper服務:
[atguigu@hadoop102 zookeeper-3.4.10]$ bin/zkServer.sh stop
2.2 配置參數解讀
Zookeeper中的配置文件zoo.cfg中參數含義解讀如下:
- 1、tickTime=2000:通信心跳數,Zookeeper服務器與客戶端心跳時間,單位毫秒。
Zookeeper使用的基本時間,服務器之間或客戶端與服務器之間維持心跳的時間間隔,也就是每個tickTime時間就會發送一個心跳,時間單位為毫秒。
它用於心跳機制
,並且設置最小的session超時時間為兩倍心跳時間。(session的最小超時時間是2*tickTime) - 2、initLimit=10:LF初始通信時限
集群中的Follower跟隨者服務器
與Leader領導者服務器
之間初始連接時能容忍的最多心跳數(tickTime的數量),用它來限定集群中的Zookeeper服務器連接到Leader的時限。 - 3、syncLimit=5:LF同步通信時限
集群中Leader與Follower之間的最大響應時間單位,假如響應超過syncLimit * tickTime,Leader認為Follwer死掉,從服務器列表中刪除Follwer。 - 4、dataDir:數據文件目錄+數據持久化路徑
主要用於保存Zookeeper中的數據。 - 5、clientPort=2181:客戶端連接端口
監聽客戶端連接的端口。
第3章 Zookeeper內部原理
3.1 選舉機制(面試重點)
-
1)半數機制(Paxos 協議):集群中半數以上機器存活,集群可用。所以Zookeeper適合安裝奇數台服務器。
-
2)Zookeeper雖然在配置文件中並沒有指定Master和Slave。但是,Zookeeper工作時,是有一個節點為Leader,其他則為Follower,Leader是通過
內部的選舉機制
臨時產生的。 -
3)以一個簡單的例子來說明整個選舉的過程。
假設有五台服務器組成的Zookeeper集群,它們的id從1-5,同時它們都是最新啟動的,也就是沒有歷史數據,在存放數據量這一點上,都是一樣的。假設這些服務器依序啟動,來看看會發生什么,如下圖所示。 -
(1)服務器1啟動,此時只有它一台服務器啟動了,它發出去的報文沒有任何響應,所以它的選舉狀態一直是LOOKING狀態。
-
(2)服務器2啟動,它與最開始啟動的服務器1進行通信,互相交換自己的選舉結果,由於兩者都沒有歷史數據,所以id值較大的服務器2勝出,但是由於沒有達到超過半數以上的服務器都同意選舉它(這個例子中的半數以上是3),所以服務器1、2還是繼續保持LOOKING狀態。
-
(3)服務器3啟動,根據前面的理論分析,服務器3成為服務器1、2、3中的老大,而與上面不同的是,此時有三台服務器選舉了它,所以它成為了這次選舉的Leader。
-
(4)服務器4啟動,根據前面的分析,理論上服務器4應該是服務器1、2、3、4中最大的,但是由於前面已經有半數以上的服務器選舉了服務器3,所以它只能接收當小弟的命了。
(5)服務器5啟動,同4一樣當小弟。
3.2 節點類型

3.3 stat結構體

- 1)cZxid - 創建節點的事務zxid
每次修改ZooKeeper狀態都會收到一個zxid形式的時間戳,也就是ZooKeeper事務ID。
事務ID是ZooKeeper中所有修改總的次序。每個修改都有唯一的zxid,如果zxid1小於zxid2,那么zxid1在zxid2之前發生。 - 2)ctime - znode被創建的毫秒數(從1970年開始)
- 3)mZxid - znode最后更新的事務zxid
- 4)mtime - znode最后修改的毫秒數(從1970年開始)
- 5)pZxid - znode最后更新的子節點zxid
- 6)cversion - znode子節點變化號,znode子節點修改次數
- 7)dataVersion - znode數據變化號
- 8)aclVersion - znode訪問控制列表的變化號
- 9)ephemeralOwner - 如果是臨時節點,這個是znode擁有者的session id。如果不是臨時節點則是0。
10)dataLength - znode的數據長度
11)numChildren - znode子節點數量
3.4 監聽器原理(面試重點)

3.5 寫數據流程

第4章 Zookeeper實戰(開發重點)
4.1 分布式安裝部署
1、集群規划
在hadoop102、hadoop103和hadoop104三個節點上部署Zookeeper。
2、解壓安裝
(1)解壓Zookeeper安裝包到/opt/module/目錄下
[atguigu@hadoop102 software]$ tar -zxvf zookeeper-3.4.10.tar.gz -C /opt/module/
(2)同步/opt/module/zookeeper-3.4.10/目錄內容到hadoop103、hadoop104
[atguigu@hadoop102 module]$ xsync zookeeper-3.4.10/
3、配置服務器編號
(1)在/opt/module/zookeeper-3.4.10/這個目錄下創建zkData
[atguigu@hadoop102 zookeeper-3.4.10]$ mkdir -p zkData
(2)在/opt/module/zookeeper-3.4.10/zkData目錄下創建一個myid的文件
[atguigu@hadoop102 zkData]$ touch myid
添加myid文件,注意一定要在linux里面創建,在notepad++里面很可能亂碼。
(3)編輯myid文件
[atguigu@hadoop102 zkData]$ vim myid
在文件中添加與server對應的編號:
2
(4)拷貝配置好的zookeeper到其他機器上
[atguigu@hadoop102 zkData]$ xsync myid
並分別在hadoop102、hadoop103上修改myid文件中內容為3、4
4、配置zoo.cfg文件
(1)重命名/opt/module/zookeeper-3.4.10/conf這個目錄下的zoo_sample.cfg為zoo.cfg
[atguigu@hadoop102 conf]$ mv zoo_sample.cfg zoo.cfg
(2)打開zoo.cfg文件
[atguigu@hadoop102 conf]$ vim zoo.cfg
修改數據存儲路徑配置
dataDir=/opt/module/zookeeper-3.4.10/zkData
增加如下配置
#######################cluster##########################
server.2=hadoop102:2888:3888
server.3=hadoop103:2888:3888
server.4=hadoop104:2888:3888
(3)同步zoo.cfg配置文件
[atguigu@hadoop102 conf]$ xsync zoo.cfg
(4)配置參數解讀
server.A=B:C:D。
A是一個數字,表示這個是第幾號服務器;
集群模式下配置一個文件myid,這個文件在dataDir目錄下,這個文件里面有一個數據就是A的值,Zookeeper啟動時讀取此文件,拿到里面的數據與zoo.cfg里面的配置信息比較從而判斷到底是哪個server。
B是這個服務器的ip地址;
C是這個服務器與集群中的Leader服務器交換信息
的端口;
D是萬一集群中的Leader服務器掛了,需要一個端口來重新進行選舉,選出一個新的Leader,而這個端口就是用來執行選舉時
服務器相互通信的端口。
4、集群操作
(1)分別啟動Zookeeper
[atguigu@hadoop102 zookeeper-3.4.10]$ bin/zkServer.sh start
[atguigu@hadoop103 zookeeper-3.4.10]$ bin/zkServer.sh start
[atguigu@hadoop104 zookeeper-3.4.10]$ bin/zkServer.sh start
(2)查看狀態
[atguigu@hadoop102 zookeeper-3.4.10]# bin/zkServer.sh status
JMX enabled by default
Using config: /opt/module/zookeeper-3.4.10/bin/../conf/zoo.cfg
Mode: follower
[atguigu@hadoop103 zookeeper-3.4.10]# bin/zkServer.sh status
JMX enabled by default
Using config: /opt/module/zookeeper-3.4.10/bin/../conf/zoo.cfg
Mode: leader
[atguigu@hadoop104 zookeeper-3.4.5]# bin/zkServer.sh status
JMX enabled by default
Using config: /opt/module/zookeeper-3.4.10/bin/../conf/zoo.cfg
Mode: follower
4.2 客戶端命令行操作
1、啟動客戶端
[atguigu@hadoop103 zookeeper-3.4.10]$ bin/zkCli.sh
2、顯示所有操作命令
[zk: localhost:2181(CONNECTED) 1] help
3、查看當前znode中所包含的內容
[zk: localhost:2181(CONNECTED) 0] ls /
[zookeeper]
4、查看當前節點詳細數據
[zk: localhost:2181(CONNECTED) 1] ls2 /
[zookeeper]
cZxid = 0x0
ctime = Thu Jan 01 08:00:00 CST 1970
mZxid = 0x0
mtime = Thu Jan 01 08:00:00 CST 1970
pZxid = 0x0
cversion = -1
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 1
5、分別創建2個普通節點
[zk: localhost:2181(CONNECTED) 3] create /sanguo "zhuge"
Created /sanguo
[zk: localhost:2181(CONNECTED) 4] create /sanguo/shuguo "liubei"
Created /sanguo/shuguo
6、獲得節點的值
[zk: localhost:2181(CONNECTED) 5] get /sanguo
zhuge
cZxid = 0x100000003
ctime = Wed Aug 29 00:03:23 CST 2018
mZxid = 0x100000003
mtime = Wed Aug 29 00:03:23 CST 2018
pZxid = 0x100000004
cversion = 1
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 7
numChildren = 1
[zk: localhost:2181(CONNECTED) 6]
[zk: localhost:2181(CONNECTED) 6] get /sanguo/shuguo
liubei
cZxid = 0x100000004
ctime = Wed Aug 29 00:04:35 CST 2018
mZxid = 0x100000004
mtime = Wed Aug 29 00:04:35 CST 2018
pZxid = 0x100000004
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 6
numChildren = 0
7、創建短暫節點
[zk: localhost:2181(CONNECTED) 7] create -e /sanguo/wuguo "zhouyu"
Created /sanguo/wuguo
(1)在當前客戶端是能查看到的
[zk: localhost:2181(CONNECTED) 3] ls /sanguo
[wuguo, shuguo]
(2)退出當前客戶端然后再重啟客戶端
[zk: localhost:2181(CONNECTED) 12] quit
[atguigu@hadoop104 zookeeper-3.4.10]$ bin/zkCli.sh
(3)再次查看根目錄下短暫節點已經刪除
[zk: localhost:2181(CONNECTED) 0] ls /sanguo
[shuguo]
8、創建帶序號的節點
(1)先創建一個普通的根節點/sanguo/weiguo
[zk: localhost:2181(CONNECTED) 1] create /sanguo/weiguo "caocao"
Created /sanguo/weiguo
(2)創建帶序號的節點
[zk: localhost:2181(CONNECTED) 2] create -s /sanguo/weiguo/xiaoqiao "jinlian"
Created /sanguo/weiguo/xiaoqiao0000000000
[zk: localhost:2181(CONNECTED) 3] create -s /sanguo/weiguo/daqiao "jinlian2"
Created /sanguo/weiguo/daqiao0000000001
[zk: localhost:2181(CONNECTED) 4] create -s /sanguo/weiguo/diaocan "jinlian3"
Created /sanguo/weiguo/diaocan0000000002
如果原來沒有序號節點,序號從0開始依次遞增。如果原節點下已有2個節點,則再排序時從2開始,以此類推。
9、修改節點數據值
[zk: localhost:2181(CONNECTED) 6] set /sanguo/weiguo "simayi"
10、節點的值變化監聽
(1)在hadoop104主機上注冊監聽/sanguo節點數據變化
[zk: localhost:2181(CONNECTED) 8] get /sanguo watch
(2)在hadoop103主機上修改/sanguo節點的數據
[zk: localhost:2181(CONNECTED) 1] set /sanguo "xisi"
(3)觀察hadoop104主機收到數據變化的監聽
WATCHER::
WatchedEvent state:SyncConnected type:NodeDataChanged path:/sanguo
注意:該注冊監聽一次,那么就只生效一次。下次需要重新注冊。
11、節點的子節點變化監聽(路徑變化)
(1)在hadoop104主機上注冊監聽/sanguo節點的子節點變化
[zk: localhost:2181(CONNECTED) 1] ls /sanguo watch
[aa0000000001, server101]
(2)在hadoop103主機/sanguo節點上創建子節點
[zk: localhost:2181(CONNECTED) 2] create /sanguo/jin "simayi"
Created /sanguo/jin
(3)觀察hadoop104主機收到子節點變化的監聽
WATCHER::
WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/sanguo
注意:該注冊監聽一次,那么就只生效一次。下次需要重新注冊。
12、刪除節點
[zk: localhost:2181(CONNECTED) 4] delete /sanguo/jin
13、遞歸刪除節點
[zk: localhost:2181(CONNECTED) 15] rmr /sanguo/shuguo
14、查看節點狀態
[zk: localhost:2181(CONNECTED) 17] stat /sanguo
cZxid = 0x100000003
ctime = Wed Aug 29 00:03:23 CST 2018
mZxid = 0x100000011
mtime = Wed Aug 29 00:21:23 CST 2018
pZxid = 0x100000014
cversion = 9
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 4
numChildren = 1
4.3 API應用
4.3.1 Eclipse環境搭建
1、創建一個Maven工程
2、添加pom文件
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.8.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.zookeeper/zookeeper -->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.10</version>
</dependency>
</dependencies>
3、拷貝log4j.properties文件到項目根目錄
需要在項目的src/main/resources目錄下,新建一個文件,命名為“log4j.properties”,在文件中填入。
log4j.rootLogger=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File=target/spring.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n
4.3.2 創建ZooKeeper客戶端
/**
* 創建ZooKeeper客戶端
* @throws IOException
*/
@Before
public void init() throws IOException {
zkClient = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
public void process(WatchedEvent event) {
// 再次啟動監聽
try {
zkClient.getChildren("/", true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
4.3.3 創建子節點
/**
* 創建子節點
* @throws Exception
*/
@Test
public void create() throws Exception {
// 參數1:要創建的節點的路徑; 參數2:節點數據 ; 參數3:節點權限 ;參數4:節點的類型
String nodeCreated = zkClient.create("/atguigu", "jinlian".getBytes(), Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);
System.out.println(nodeCreated);
}
4.3.4 獲取子節點並監聽節點變化
/**
* 獲取子節點並監聽節點變化
* @throws Exception
*/
@Test
public void getChildren() throws Exception {
List<String> children = zkClient.getChildren("/", true);
for (String child : children) {
System.out.println(child);
}
// 延時阻塞
Thread.sleep(Long.MAX_VALUE);
}
4.3.5 判斷Znode是否存在
/**
* 判斷znode是否存在
* @throws Exception
*/
@Test
public void exist() throws Exception {
Stat stat = zkClient.exists("/eclipse", false);
System.out.println(stat == null ? "not exist" : "exist");
}
4.4 監聽服務器節點動態上下線案例
1、需求
某分布式系統中,主節點可以有多台,可以動態上下線,任意一台客戶端都能實時感知到主節點服務器的上下線。
2、需求分析,如下圖所示

3、具體實現
(0)先在集群上創建/servers節點
[zk: localhost:2181(CONNECTED) 10] create /servers "servers"
Created /servers
(1)服務器端向Zookeeper注冊代碼
package com.atguigu.zookeeper;
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.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
public class DistributeServer {
public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
// 1、連接zookeeper集群
DistributeServer server = new DistributeServer();
server.getConnect();
// 2、注冊服務器節點
server.registerServer(args[0]);
// 3、業務邏輯功能
server.business(args[0]);
}
private void business(String hostname) throws InterruptedException {
System.out.println(hostname + " is working ...");
Thread.sleep(Long.MAX_VALUE);
}
private String parentNode = "/servers";
private void registerServer(String hostname) throws KeeperException, InterruptedException {
String path = zkClient.create(parentNode + "/server", hostname.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
System.out.println(hostname + " is online " + path);
}
private String connectString = "hadoop102:2181,hadoop103:2181,hadoop104:2181";
private int sessionTimeout = 2000;
private ZooKeeper zkClient = null;
private void getConnect() throws IOException {
zkClient = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
public void process(WatchedEvent event) {
}
});
}
}
(2)客戶端代碼
package com.atguigu.zookeeper;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
public class DistributeClient {
public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
// 1、連接zookeeper集群
DistributeClient client = new DistributeClient();
client.getConnect();
// 2、獲取子節點並監聽節點變化
client.getChildren();
// 3、業務邏輯功能
client.business();
}
private void business() throws InterruptedException {
System.out.println("client is working ...");
Thread.sleep(Long.MAX_VALUE);
}
private String parentNode = "/servers";
private void getChildren() throws KeeperException, InterruptedException {
// 1、獲取服務器子節點信息,並且對父節點進行監聽
List<String> children = zkClient.getChildren(parentNode , true);
// 2、存儲服務器信息列表
ArrayList<String> hosts = new ArrayList<String>();
// 3、遍歷所有節點,獲取節點中的主機名稱信息
for (String child : children) {
byte[] data = zkClient.getData(parentNode + "/" + child, false, null);
hosts.add(new String(data));
}
// 4、打印服務器列表信息
System.out.println(hosts);
}
private String connectString = "hadoop102:2181,hadoop103:2181,hadoop104:2181";
private int sessionTimeout = 2000;
private ZooKeeper zkClient = null;
private void getConnect() throws IOException {
zkClient = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
public void process(WatchedEvent event) {
// 再次啟動監聽
try {
getChildren();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
第5章 企業面試真題
5.1 請簡述ZooKeeper的選舉機制(半數機制)
詳見3.1。
5.2 ZooKeeper的監聽原理是什么?
詳見3.4。
5.3 ZooKeeper的部署方式有哪幾種?集群中的角色有哪些?集群最少需要幾台機器?
(1)部署方式單機模式、集群模式。
(2)角色:Leader和Follower。
(3)集群最少需要機器數:3。
5.4 ZooKeeper的常用命令
ls
create
get
delete
set
……