【1】Zookeeper基礎入門
① Zookeeper是什麼
Zookeeper是一個開源的分佈式的、爲分佈式應用提供協調服務的Apache項目。
Zookeeper從設計模式角度來理解,其實是一個基於觀察者模式設計的分佈式服務管理框架。它負責存儲和管理大家都關心的數據,然後接受觀察者的註冊。一旦這些數據的狀態發送變化,Zookeeper就將負責通知已經在Zookeeper上註冊的那些觀察者做出相應的反應。從Zookeeper存儲數據和通知機制來講,可以說Zookeeper=文件系統+通知機制。
zookeeper實現服務器節點動態上下線過程描述如下:
- 服務端啓動時去註冊信息(創建的都是臨時節點);
- 客戶端獲取到當前在線服務器列表,並且註冊監聽;
- 服務器節點下線;
- 服務器節點上下線事件通知(Zookeeper負責);
- 客戶端收到監聽通知則做出相應反應,比如重新獲取在線服務器列表註冊監聽。
② Zookeeper集羣特點
集羣如下圖所示:
- Zookeeper集羣是一個leader和多個follow組成的集羣。
- 集羣中只要半數以上節點存活,Zookeeper集羣就能正常服務。
- 全局數據一致,每個server保存一份相同的數據副本,client無論連接到哪個server,數據都是一致的。
- 更新請求順序進行,來自同一個Client的更新請求按其發送順序依次執行。
- 數據更新原子性,一次數據更新要麼成功,要麼失敗。
- 實時性,在一定時間範圍內,Client能讀到最新數據。
③ Zookeeper的數據結構
Zookeeper的數據模型的結構與UNIX文件系統很類似,整體上可以看做是一棵樹,每個節點稱做一個ZNode。每一個ZNode默認能夠存儲1MB的數據,每個ZNode可以通過其路徑唯一標識。
如下圖所示:
④ 應用場景
Zookeeper有諸多應用場景,如統一命名服務、統一配置管理、統一集羣管理、服務器節點動態上下線、軟負載均衡等。
- 統一命名服務
在分佈式環境下,經常需要對應用/服務進行統一命名,便於區別。例如IP不容易記住而域名容易記住。
- 統一配置管理
分佈式環境下,配置文件同步非常常見。一般要求一個集羣中,所有節點的配置信息是一致的,比如kafka集羣。對配置文件修改後,希望能夠快速同步到各個節點上。
配置管理可交由Zookeeper實現:可將配置信息寫入Zookeeper上的一個ZNode;每個客戶端服務器監聽這個ZNode;一旦ZNode中的數據被修改,Zookeeper將通知各個客戶端。
- 統一集羣管理
分佈式環境中,實時掌握每個節點的狀態是很必要的,以便可以根據節點實時狀態做出一些調整。
Zookeeper可以實現實時監控節點狀態的變化:可以將節點信息寫入Zookeeper的一個ZNode;監聽這個ZNode可獲取它的實時狀態變化。
- 軟負載均衡
在Zookeeper中記錄每臺服務器的訪問數,讓訪問數最少的服務器去處理最新的客戶端請求。
【2】單擊版Zookeeper下載與安裝
官網地址:http://zookeeper.apache.org/
下載地址:https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/
① 本機模式安裝Zookeeper
這裏下載的是3.5.5版本,將其放到linux某個路徑下(這裏有個坑,請下載apache-zookeeper-3.5.5-bin.tar.gz,該包可以正常使用。apache-zookeeper-3.5.5.tar.gz是源碼包):
注意,安裝Zookeeper一定先安裝好jdk,linux安裝jdk可以參考如下博文:
Linux下源碼安裝jdk1.8 , CentOS7 下rpm安裝jdk1.8 。
安裝步驟如下:
- 解壓
tar -zxvf apache-zookeeper-3.5.5-bin.tar.gz
//或者指定解壓目錄
tar -zxvf apache-zookeeper-3.5.5-bin.tar.gz -C /home/softinstall
- 修改配置文件(把conf中的zoo_sample.cfg重命名爲zoo.cfg)
cd apache-zookeeper-3.5.5/conf
mv zoo_sample.cfg zoo.cfg
//配置數據路徑
mkdir -p /home/softinstall/apache-zookeeper-3.5.5/zkData
vim zoo.cfg
② 操作Zookeeper
- 啓動Zookeeper服務端
[root@localhost apache-zookeeper-3.5.5-bin]# bin/zkServer.sh start
ZooKeeper JMX enabled by default
Using config: /opt/softinstall/apache-zookeeper-3.5.5-bin/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
[root@localhost apache-zookeeper-3.5.5-bin]# jps
27368 QuorumPeerMain
27388 Jps
- 關閉zkserver
bin/zkServer.sh stop
- 查看服務狀態
[root@localhost apache-zookeeper-3.5.5-bin]# bin/zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /opt/softinstall/apache-zookeeper-3.5.5-bin/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: standalone //表示單機模式
- 重啓Zookeeper服務
//進入目錄下
bin/zkServer.sh restart
- 啓動客戶端
bin/zkCli.sh
- 查看根目錄下內容
[zk: localhost:2181(CONNECTED) 2] ls /
[zookeeper]
- 退出客戶端
[zk: localhost:2181(CONNECTED) 6] quit
【3】Zookeeper配置文件參數解讀
配置文件主要參數如下:
# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial
# synchronization phase can take
initLimit=10
# The number of ticks that can pass between
# sending a request and getting an acknowledgement
syncLimit=5
# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just
# example sakes.
dataDir=/home/softinstall/apache-zookeeper-3.5.5/zkData
# the port at which the clients will connect
clientPort=2181
# the maximum number of client connections.
# increase this if you need to handle more clients
#maxClientCnxns=60
#
# Be sure to read the maintenance section of the
# administrator guide before turning on autopurge.
#
# http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
#
# The number of snapshots to retain in dataDir
#autopurge.snapRetainCount=3
# Purge task interval in hours
# Set to "0" to disable auto purge feature
#autopurge.purgeInterval=1
① tickTime=2000
該參數含義爲通信心跳數 ,Zookeeper服務器端和客戶端心跳時間,單位毫秒。2000表示2S一次心跳檢測,也就是每個tickTime時間就會發送一個心跳。它用於心跳機制,並且設置最小的session超時時間爲兩倍心跳時間(session的最小超時時間=2*tickTime)。
② initLimit=10
集羣中的follower與leader之間初始連接時能容忍的最多心跳數(tickTime的數量),用它來限定集羣中Zookeeper服務器連接到leader的時限。
10個心跳幀,10*2=20s是最開始通信時leader和follower最大延時時間。如果超過該時間,則認爲二者斷開通信。
③ syncLimit=5
集羣正常啓動後集羣中leader與follower之間的最大響應時間單位。假如響應時間超過syncLimit*tickTime,leader認爲follower死掉,從服務器列表中刪除follower。
④ clientPort=2181
客戶端與服務端通信的端口號,默認爲2181。
⑤ dataDir
zookeeper的數據目錄。另外還有日誌文件目錄默認在../apache-zookeeper-3.5.5-bin/logs
下。
【3】利用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 {
DistributeClient client = new DistributeClient();
// 1 獲取zookeeper集羣連接
client.getConnect();
// 2 註冊監聽
client.getChlidren();
// 3 業務邏輯處理
client.business();
}
private void business() throws InterruptedException {
Thread.sleep(Long.MAX_VALUE);
}
private void getChlidren() throws KeeperException, InterruptedException {
List<String> children = zkClient.getChildren("/servers", true);
// 存儲服務器節點主機名稱集合
ArrayList<String> hosts = new ArrayList<String>();
for (String child : children) {
byte[] data = zkClient.getData("/servers/"+child, false, null);
hosts.add(new String(data));
}
// 將所有在線主機名稱打印到控制檯
System.out.println(hosts);
}
private String connectString="127.0.0.1:3181,127.0.0.1:3182,127.0.0.1:3183";
private int sessionTimeout = 2000;
private ZooKeeper zkClient;
private void getConnect() throws IOException {
zkClient = new ZooKeeper(connectString , sessionTimeout , new Watcher() {
public void process(WatchedEvent event) {
try {
getChlidren();
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
服務端代碼
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.ZooKeeper;
import org.apache.zookeeper.ZooDefs.Ids;
public class DistributeServer {
public static void main(String[] args) throws Exception {
DistributeServer server = new DistributeServer();
// 1 連接zookeeper集羣
server.getConnect();
// 2 註冊節點
server.regist(args[0]);
// 3 業務邏輯處理
server.business();
}
private void business() throws InterruptedException {
Thread.sleep(Long.MAX_VALUE);
}
private void regist(String hostname) throws KeeperException, InterruptedException {
//注意,創建的是臨時、有序號節點。這樣服務器down了節點也就不存在了
String path = zkClient.create("/servers/server", hostname.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
System.out.println(hostname +"is online ");
}
private String connectString="127.0.0.1:3181,127.0.0.1:3182,127.0.0.1:3183";
private int sessionTimeout = 2000;
private ZooKeeper zkClient;
private void getConnect() throws IOException {
zkClient = new ZooKeeper(connectString , sessionTimeout , new Watcher() {
public void process(WatchedEvent event) {
// TODO Auto-generated method stub
}
});
}
}
zookeeper更多使用參考博文:ZooKeeper學習之內部原理