1 什麼是ZooKeeper ZooKeeper 是一個分佈式的,開放源碼的分佈式應用程序協同服務。ZooKeeper 的設計目標是將那些複雜且容易出錯的分佈式一致性服務封裝起來,構成一個高效可靠的原語集,並以一系列簡單易用的接口提供給用戶使用。java
2 ZooKeeper 發展歷史node
ZooKeeper 最先起源於雅虎研究院的一個研究小組。在當時,研究人員發現,在雅虎內部不少大型系統基本都須要依賴一個相似的系統來進行分佈式協同,可是這些系統每每都存在分佈式單點問題。數據庫
因此,雅虎的開發人員就開發了一個通用的無單點問題的分佈式協調框架,這就是 ZooKeeper。ZooKeeper 以後在開源界被大量使用,下面列出了 3 個著名開源項目是如何使用 ZooKeeper:apache
Hadoop:使用 ZooKeeper 作 Namenode 的高可用。 HBase:保證集羣中只有一個 master,保存 hbase:meta 表的位置,保存集羣中的 RegionServer 列表。 Kafka:集羣成員管理,controller 節點選舉。 3 ZooKeeper 應用場景vim
不少分佈式協調服務均可以用 ZooKeeper 來作,其中典型應用場景以下:服務器
配置管理(configuration management):若是咱們作普通的 Java 應用,通常配置項就是一個本地的配置文件,若是是微服務系統,各個獨立服務都要使用集中化的配置管理,這個時候就須要 ZooKeeper。 DNS 服務 組成員管理(group membership):好比上面講到的 HBase 其實就是用來作集羣的組成員管理。 各類分佈式鎖 ZooKeeper 適用於存儲和協同相關的關鍵數據,不適合用於大數據量存儲。若是要存 KV 或者大量的業務數據,仍是要用數據庫或者其餘 NoSql 來作。markdown
爲何 ZooKeeper 不適合大數據量存儲呢?主要有如下兩個緣由:網絡
設計方面:ZooKeeper 須要把全部的數據(它的 data tree)加載到內存中。這就決定了ZooKeeper 存儲的數據量受內存的限制。這一點 ZooKeeper 和 Redis 比較像。通常的數據庫系統例如 MySQL(使用 InnoDB 存儲引擎的話)能夠存儲大於內存的數據,這是由於 InnoDB 是基於 B-Tree 的存儲引擎。B-tree 存儲引擎和 LSM 存儲引擎均可以存儲大於內存的數據量。 工程方面:ZooKeeper 的設計目標是爲協同服務提供數據存儲,數據的高可用性和性能是最重要的系統指標,處理大數量不是 ZooKeeper 的首要目標。所以,ZooKeeper 不會對大數量存儲作太多工程上的優化。 4 ZooKeeper 服務的使用session
要使用 ZooKeeper 服務,首先咱們的應用要引入 ZooKeeper 的客戶端庫,而後咱們客戶端庫和 ZooKeeper 集羣來進行網絡通訊來使用 ZooKeeper 的服務,本質上是 Client-Server 的架構,咱們的應用做爲一個客戶端來調用 ZooKeeper Server 端的服務。數據結構
5 ZooKeeper 數據模型
ZooKeeper 的數據模型是層次模型。層次模型常見於文件系統。層次模型和 key-value 模型是兩種主流的數據模型。ZooKeeper 使用文件系統模型主要基於如下兩點考慮:
文件系統的樹形結構便於表達數據之間的層次關係。 文件系統的樹形結構便於爲不一樣的應用分配獨立的命名空間(namespace)。 ZooKeeper 的層次模型稱做 data tree。Data tree 的每一個節點叫作 znode。不一樣於文件系統,每一個節點均可以保存數據。每一個節點都有一個版本(version),版本從 0 開始計數。
如上圖所示的 data tree 中有兩個子樹,一個用於應用 1(/app1)和另外一個用於應用 2(/app2)。
應用 1 的子樹實現了一個簡單的組成員協議:每一個客戶端進程 pi 建立一個 znode p_i 在 /app1 下,只要 /app1/p_i 存在就表明進程 pi 在正常運行。
5.1 data tree 接口
ZooKeeper 對外提供一個用來訪問 data tree的簡化文件系統 API:
使用 UNIX 風格的路徑名來定位 znode,例如 /A/X 表示 znode A 的子節點 X。 znode 的數據只支持全量寫入和讀取,沒有像通用文件系統那樣支持部分寫入和讀取。 data tree 的全部 API 都是 wait-free 的,正在執行中的 API 調用不會影響其餘 API 的完成。 data tree 的 API都是對文件系統的 wait-free 操做,不直接提供鎖這樣的分佈式協同機制。可是 data tree 的 API 很是強大,能夠用來實現多種分佈式協同機制。 5.2znode 分類
一個 znode 能夠是持久性的,也能夠是臨時性的,znode 節點也能夠是順序性的。每個順序性的 znode 關聯一個惟一的單調遞增整數,所以 ZooKeeper 主要有如下 4 種 znode:
持久性的 znode (PERSISTENT): ZooKeeper 宕機,或者 client 宕機,這個 znode 一旦建立就不會丟失。 臨時性的 znode (EPHEMERAL): ZooKeeper 宕機了,或者 client 在指定的 timeout 時間內沒有鏈接 server,都會被認爲丟失。 持久順序性的 znode (PERSISTENT_SEQUENTIAL): znode 除了具有持久性 znode 的特色以外,znode 的名字具有順序性。 臨時順序性的 znode (EPHEMERAL_SEQUENTIAL): znode 除了具有臨時性 znode 的特色以外,znode 的名字具有順序性。 6 安裝 ZooKeeper
到 archive.apache.org/dist/zookee… 下載 ZooKeeper,目前的最新版是 3.5.6。
把 apache-zookeeper-3.5.6-bin.tar.gz 解壓到一個本地目錄 (目錄名最好不要包含空格和中文)。我使用 /usr/local 目錄。
tar -zxvf apache-zookeeper-3.5.6-bin.tar.gz 把 conf 目錄下的 zoo_sample.cfg 重命名爲 zoo.cfg,而後修改配置。
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/data/zookeeper
clientPort=2181 配置如下環境變量 vim /etc/profile :
export ZOOKEEPER_HOME=/usr/local/apache-zookeeper-3.5.6-bin export PATH=ZOOKEEPER_HOME/bin:$ZOOKEEPER_HOME/conf 7 啓動 Zookeeper
再安裝配置完成後,就能夠啓動 Zookeeper,使用 zkServer.sh start 啓動 ZooKeeper 服務:
[root@wupx apache-zookeeper-3.5.6-bin]# zkServer.sh start /usr/bin/java ZooKeeper JMX enabled by default Using config: /usr/local/apache-zookeeper-3.5.6-bin/bin/../conf/zoo.cfg Starting zookeeper ... STARTED 檢查 ZooKeeper 日誌是否有出錯信息:
[root@wupx apache-zookeeper-3.5.6-bin]# cd logs/ [root@wupx logs]# grep -E -i "((exception)|(error))" * 由於返回沒有結果,說明沒有錯誤信息。
檢查 ZooKeeper 數據文件,這裏存放的 ZooKeeper 的事務日誌文件和快照日誌文件。
[root@wupx zookeeper]# cd /data/zookeeper/ [root@wupx zookeeper]# tree . ├── version-2 │ └── snapshot.0 └── zookeeper_server.pid 1 directory, 2 files 由於如今尚未運行任何 ZooKeeper 命令,因此尚未事務日誌文件。
最後會檢查 ZooKeeper 是否在 2181 端口上監聽。
netstat -an | ag 2181
執行後,咱們能夠看到 ZooKeeper 已經在 2181 這個端口上監聽了。
下面咱們演示下如何使用 zkCli:
8 zkCli 使用
在執行 zkCli.sh 命令後,會出現不少消息,這些消息證實咱們的 zkCli 和 ZooKeeper 的節點創建了有效鏈接。
2019-12-22 10:38:36,684 [myid:localhost:2181] - INFO [main-SendThread(localhost:2181):ClientCnxn$SendThread@959] - Socket connection established, initiating session, client: /127.0.0.1:54038, server: localhost/127.0.0.1:2181 使用 ls -R / 能夠遞歸查找 ZooKeeper 的 znode 節點,使用 create /znode_name 能夠建立 znode 節點,具體演示以下:
[zk: localhost:2181(CONNECTED) 0] ls -R / / /zookeeper /zookeeper/config /zookeeper/quota
[zk: localhost:2181(CONNECTED) 1] create /app1 Created /app1 [zk: localhost:2181(CONNECTED) 2] create /app2 Created /app2 [zk: localhost:2181(CONNECTED) 3] create /app1/p_1 1 Created /app1/p_1 [zk: localhost:2181(CONNECTED) 4] create /app1/p_2 2 Created /app1/p_2 [zk: localhost:2181(CONNECTED) 5] create /app1/p_3 3 Created /app1/p_3 [zk: localhost:2181(CONNECTED) 6] ls -R / / /app1 /app2 /zookeeper /app1/p_1 /app1/p_2 /app1/p_3 /zookeeper/config /zookeeper/quota 9 用 zkCli 實現鎖
分佈式鎖要求若是鎖的持有者宕了,鎖能夠被釋放。ZooKeeper 的 ephemeral 節點剛好具有這樣的特性。
接下來咱們來演示下,須要在兩個終端上分別啓動 zkCli,
在終端 1 上:
執行 zkCli.sh,再執行 create -e /lock 命令,來創建臨時 znode,加鎖的操做其實就是創建 znode 的過程,此時第一個客戶端加鎖成功。
接下來嘗試在第二個客戶端加鎖,在終端 2 上:
執行 zkCli.sh,再執行 create -e /lock 命令,會發現提示 Node already exists: /lock,提示 znode 已存在,znode 創建失敗,所以加鎖失敗,這時候咱們來監控這個 znode,使用 stat -w /lock 來等待鎖被釋放。
這個時候咱們退出第一個客戶端,在終端 1 上執行 quit 命令,會在客戶端 2 上收到一條 WATCHER 信息,具體以下:
WATCHER:: WatchedEvent state:SyncConnected type:NodeDeleted path:/lock 再收到這個事件後再次在客戶端 2 上執行加鎖,執行 create -e /lock,會顯示建立 znode 成功,即加鎖成功。
10 總結 這篇文章主要介紹了 ZooKeeper 的安裝配置,ZooKeeper 的基本概念和 zkCli 的使用,並用 zkCli 來實現一個鎖,爲後面更加深刻的學習打好基礎。
最後,分享一份進階寶典《Java核心知識點整理.pdf》,覆蓋了JVM、鎖、高併發、反射、Spring原理、微服務、Zookeeper、數據庫、數據結構等等,獲取請關注轉發本文後,私信: 寶典 便可