一、Zookeeper概述
1.1?集中式和分布式
單機架構(gòu)
一個系統(tǒng)業(yè)務(wù)量很小的時候所有的代碼都放在一個項目中就好了,然后這個項目部署在一臺服務(wù)器上,整個項目所有的服務(wù)都由這臺服務(wù)器提供。
缺點:
- 服務(wù)性能存在瓶頸,用戶增長的時候性能下降等。
- 不可伸縮性
- 代碼量龐大,系統(tǒng)臃腫,牽一發(fā)動全身
- 單點故障問題
集群架構(gòu)
單機處理到達瓶頸的時候,你就把單機復制幾份,這樣就構(gòu)成了一個集群。
集群存在的問題:
當你的業(yè)務(wù)發(fā)展到一定程度的時候,你會發(fā)現(xiàn)一個問題無論怎么增加節(jié)點,貌似整個集群性能的提升效果并不明顯了。這時候,你就需要使用分布式架構(gòu)了。
分布式架構(gòu)
分布式架構(gòu)就是將一個完整的系統(tǒng),按照業(yè)務(wù)功能,拆分成一個個獨立的子系統(tǒng),在分布式結(jié)構(gòu)中,每個子系統(tǒng)就被稱為“服務(wù)”。這些子系統(tǒng)能夠獨立運行在web容器中,它們之間通過RPC方式通信(遠程調(diào)用)。
分布式的優(yōu)勢:
- 系統(tǒng)之間的耦合度大大降低,可以獨立開發(fā)、獨立部署、獨立測試,系統(tǒng)與系統(tǒng)之間的邊界非常明確,排錯也變得相當容易,開發(fā)效率大大提升。
- 系統(tǒng)之間的耦合度降低,從而系統(tǒng)更易于擴展。我們可以針對性地擴展某些服務(wù)。
- 服務(wù)的復用性更高。比如,當我們將用戶系統(tǒng)作為單獨的服務(wù)后,該公司所有的產(chǎn)品都可以使用該系統(tǒng)作為用戶系統(tǒng),無需重復開發(fā)。
?三者區(qū)別
總結(jié):
將一套系統(tǒng)拆分成不同子系統(tǒng)部署在不同服務(wù)器上(這叫分布式),然后部署多個相同的子系統(tǒng)在不同的服務(wù)器上(這叫集群)。
集群:多個人在一起作同樣的事 。
分布式?:多個人在一起作不同的事 。
?1.2?CAP定理
?分布式系統(tǒng)正變得越來越重要,大型網(wǎng)站幾乎都是分布式的。分布式系統(tǒng)的最大難點,就是各個節(jié)點的狀態(tài)如何同步。CAP 定理是這方面的基本定理,也是理解分布式系統(tǒng)的起點。說白了,就是要使各個模塊的狀態(tài)、數(shù)據(jù)共享,保持數(shù)據(jù)一致。
分布式系統(tǒng)的三個指標
- Consistency(一致性)
- Availability (可用性)
- Partition tolerance (分區(qū)容錯性)
分區(qū)容錯性
大多數(shù)分布式系統(tǒng)都分布在多個子網(wǎng)絡(luò)。每個子網(wǎng)絡(luò)就叫做一個區(qū)。分區(qū)容錯的意思是,區(qū)間通信可能失敗。比如,一臺服務(wù)器放在中國,另一臺服務(wù)器放在美國,這就是兩個區(qū),它們之間可能無法通信。
結(jié)論:
分區(qū)容錯無法避免,因此可以認為 CAP 的 P 總是成立。CAP 定理告訴我們,剩下的 C 和 A 無法同時做到。
一致性
寫操作之后的讀操作,必須返回該值。舉例來說,某條記錄是 v0,用戶向 G1 發(fā)起一個寫操作,將其改為 v1。
接下來,用戶的讀操作就會得到 v1。這就叫一致性。
問題是,用戶有可能向 G2 發(fā)起讀操作,由于 G2 的值沒有發(fā)生變化,因此返回的是 v0。G1 和 G2 讀操作的結(jié)果不一致,這就不滿足一致性了。?
?為了讓 G2 也能變?yōu)?v1,就要在 G1 寫操作的時候,讓 G1 向 G2 發(fā)送一條消息,要求 G2 也改成 v1。
?可用性
只要收到用戶的請求,服務(wù)器就必須給出回應(yīng).
解釋:
用戶可以選擇向 G1 或 G2 發(fā)起讀操作。不管是哪臺服務(wù)器,只要收到請求,就必須告訴用戶,到底是 v0 還是 v1,否則就不滿足可用性。
一致性和可用性的矛盾
解釋:
如果保證 G2 的一致性,那么 G1 必須在寫操作時,鎖定 G2 的讀操作和寫操作。只有數(shù)據(jù)同步后,才能重新開放讀寫。鎖定期間,G2 不能讀寫,沒有可用性不。
一致性和可用性如何選擇?
- 一致性(CP)
特別是涉及到重要的數(shù)據(jù),就比如錢,商品數(shù)量,商品價格。
- 可用性? (AP)
網(wǎng)頁的更新不是特別強調(diào)一致性,短時期內(nèi),一些用戶拿到老版本,另一些用戶拿到新版本,問題不會特別大。
1.3?什么是Zookeeper
?分布式架構(gòu)
多個節(jié)點協(xié)同問題:
- 每天的定時任務(wù)由誰哪個節(jié)點來執(zhí)行?
- RPC調(diào)用時的服務(wù)發(fā)現(xiàn)?
- 如何保證并發(fā)請求的冪等
這些問題可以統(tǒng)一歸納為多節(jié)點協(xié)調(diào)問題,如果靠節(jié)點自身進行協(xié)調(diào)這是非常不可靠的,性能上也不可取。必須由一個獨立的服務(wù)(zooKeeper)做協(xié)調(diào)工作,它必須可靠,而且保證性能。
一個應(yīng)用程序,涉及多個進程協(xié)作時,`業(yè)務(wù)邏輯代碼中混雜有大量復雜的進程協(xié)作邏輯。
上述多進程協(xié)作邏輯,有 2 個特點:
- 處理復雜
- 處理邏輯可重用
?因此,考慮將多進程協(xié)作的共性問題拎出,作為基礎(chǔ)設(shè)施,讓 RD 更加專注業(yè)務(wù)邏輯開發(fā),即:
?Zookeeper從何而來?
?ZooKeeper最早起源于雅虎研究院的一個研究小組。在當時,研究人員發(fā)現(xiàn),在雅虎內(nèi)部很多大型系統(tǒng)基本都需要依賴一個類似的系統(tǒng)來進行分布式協(xié)調(diào),但是這些系統(tǒng)往往都存在分布式單點問題。
解決:
雅虎的開發(fā)人員就試圖開發(fā)一個通用的無單點問題的分布式協(xié)調(diào)框架,以便讓開發(fā)人員將精力集中在處理業(yè)務(wù)邏輯上。
Zookeeper介紹
ZooKeeper是一個開放源代碼的分布式協(xié)調(diào)服務(wù)。ZooKeeper的設(shè)計目標是將那些復雜且容易出錯的分布式一致性服務(wù)封裝起來,構(gòu)成一個高效可靠的原語集,并以一系列簡單易用的接口提供給用戶使用。
說明:
Zookeeper顧名思義就是動物園管理員。 因為Hadoop生態(tài)各個項目都是動物得圖標。 所以很符合管理員得形象。
?1.3?應(yīng)用場景
數(shù)據(jù)發(fā)布/訂閱
數(shù)據(jù)發(fā)布/訂閱的一個常見的場景是配置中心,發(fā)布者把數(shù)據(jù)發(fā)布到 ZooKeeper 的一個或一系列的節(jié)點上,供訂閱者進行數(shù)據(jù)訂閱,達到動態(tài)獲取數(shù)據(jù)的目的。例如我們可以將A\B\C三個應(yīng)用的數(shù)據(jù)庫連接信息寫到公共的服務(wù)中,當數(shù)據(jù)庫異常時可以快速切換到其他正常的數(shù)據(jù)庫。
ZooKeeper 采用的是推拉結(jié)合的方式。
- 推: 服務(wù)端會推給注冊了監(jiān)控節(jié)點的客戶端 Wathcer 事件通知
- 拉: 客戶端獲得通知后,然后主動到服務(wù)端拉取最新的數(shù)據(jù)
具體流程:
- 把配置信息寫到一個 Znode 上,例如?
/DBConfiguration
- 客戶端啟動初始化階段讀取服務(wù)端節(jié)點的數(shù)據(jù),并且注冊一個數(shù)據(jù)變更的 Watcher
- 配置變更只需要對 Znode 數(shù)據(jù)進行 set 操作,數(shù)據(jù)變更的通知會發(fā)送到客戶端,客戶端重新獲取新數(shù)據(jù),完成配置動態(tài)修改
負載均衡
負載均衡是一種手段,用來把對某種資源的訪問分攤給不同的設(shè)備,從而減輕單點的壓力。
實現(xiàn)的思路:
- 首先建立 Servers 節(jié)點,并建立監(jiān)聽器監(jiān)視 Servers 子節(jié)點的狀態(tài)(用于在服務(wù)器增添時及時同步當前集群中服務(wù)器列表)
- 在每個服務(wù)器啟動時,在 Servers 節(jié)點下建立臨時子節(jié)點?Worker Server,并在對應(yīng)的字節(jié)點下存入服務(wù)器的相關(guān)信息,包括服務(wù)的地址,IP,端口等等
- 可以自定義一個負載均衡算法,在每個請求過來時從 ZooKeeper 服務(wù)器中獲取當前集群服務(wù)器列表,根據(jù)算法選出其中一個服務(wù)器來處理請求
命名服務(wù)
命名服務(wù)就是提供名稱的服務(wù)。ZooKeeper 的命名服務(wù)有兩個應(yīng)用方面。
功能:
- 提供類 JNDI 功能,可以把系統(tǒng)中各種服務(wù)的名稱、地址以及目錄信息存放在 ZooKeeper,需要的時候去 ZooKeeper 中讀取
- 制作分布式的序列號生成器.
分布式協(xié)調(diào)/通知
?總結(jié):
zookeeper是一個文件系統(tǒng)加監(jiān)聽機制,很牛逼就完了!
?1.4?為什么要選擇Zookeeper?
隨著分布式架構(gòu)的出現(xiàn),越來越多的分布式應(yīng)用會面臨數(shù)據(jù)一致性問題。很遺憾的是,在解決分布式數(shù)據(jù)一致性上,除了ZooKeeper之外,目前還沒有一個成熟穩(wěn)定且被大規(guī)模應(yīng)用的解決方案。
主要:
ZooKeeper無論從易用性還是穩(wěn)定性上來說,都已經(jīng)達到了一個工業(yè)級產(chǎn)品的標準。ZooKeeper是免費的,你無須為它支付任何費用。這點對于一個小型公司,尤其是初創(chuàng)團隊來說,無疑是非常重要的。
?廣泛應(yīng)用
最后,ZooKeeper已經(jīng)得到了廣泛的應(yīng)用。諸如Hadoop、HBase、Storm、kafka等越來越多的大型分布式項目都將Zookeeper作為核心組件。
1.5?基本概念
集群角色
通常在分布式系統(tǒng)中,構(gòu)成一個集群的每一臺機器都有自己的角色,最典型的集群模式就是Master/Slave模式(主備模式)。在這種模式中,我們把能夠處理所有寫操作的機器稱為Master機器,把所有通過異步復制方式獲取最新數(shù)據(jù),并提供讀服務(wù)的機器稱為Slave機器。
概念顛覆:
而在ZooKeeper中,這些概念被顛覆了。它沒有沿用傳統(tǒng)的MasterlSlave概念,而是引入了Leader、Follower和 Observer三種角色。
?數(shù)據(jù)節(jié)點(znode)
在談到分布式的時候,我們通常說的“節(jié)點”是指組成集群的每一臺機器。
在ZooKeeper中節(jié)點分為兩類
- 第一類同樣是指構(gòu)成集群的機器,我們稱之為機器節(jié)點
- 第二類則是指數(shù)據(jù)模型中的數(shù)據(jù)單元,我們稱之為數(shù)據(jù)節(jié)點——ZNode。
ZooKeeper將所有數(shù)據(jù)存儲在內(nèi)存中,數(shù)據(jù)模型是一棵樹。
Watcher監(jiān)聽機制
Watcher(事件監(jiān)聽器),是ZooKeeper 中的一個很重要的特性。
注意:
ZooKeeper 允許用戶在指定節(jié)點上注冊一些Watcher,并且在一些特定事件觸發(fā)的時候,ZooKeeper 服務(wù)端會將事件通知到感興趣的客戶端上去,該機制是ZooKeeper實現(xiàn)分布式協(xié)調(diào)服務(wù)的重要特性。
ACL權(quán)限控制
ZooKeeper 采用ACL (Access Control Lists)策略來進行權(quán)限控制,類似于UNIX文件系統(tǒng)的權(quán)限控制。ZooKeeper定義了如下5種權(quán)限。
- CREATE:創(chuàng)建子節(jié)點的權(quán)限
- READ:獲取節(jié)點數(shù)據(jù)和子節(jié)點列表的權(quán)限
- WRITE:更新節(jié)點數(shù)據(jù)的權(quán)限
- DELETE:刪除子節(jié)點的權(quán)限
- ADMIN:設(shè)置節(jié)點ACL的權(quán)限
注意:
create和delete這兩種權(quán)限都是針對子節(jié)點的權(quán)限控制。
二、Zookeeper部署運行
2.1?偽集群安裝
偽集群模式適合在開發(fā)和測試的環(huán)境下使用。?
下載Zookeeper,并上傳到Linux
注意:需要配置JDK環(huán)境支持。
解壓zookeeper
tar -zxvf apache-zookeeper-3.7.0-bin.tar.gz -C /usr/local
#修改名稱
mv apache-zookeeper-3.7.0-bin zookeeper
修改配置文件
進入zookeeper的安裝目錄的conf目錄
cd conf
#修改樣式文件名稱
mv zoo_sample.cfg zoo.cfg
修改zoo.cfg,配置zookeeper數(shù)據(jù)存放位置和日志存放位置。?
創(chuàng)建數(shù)據(jù)持久化目錄
mkdir /usr/local/zookeeper/zkdata
mkdir /usr/local/zookeeper/zklogs
?啟動zookeeper服務(wù)
[root@localhost zookeeper]# ./bin/zkServer.sh start
查看Zookeeper運行狀態(tài)
[root@localhost zookeeper]# ./bin/zkServer.sh status
關(guān)閉zookeeper?
[root@localhost bin]# ./zkServer.sh stop
?2.2集群安裝
?1.環(huán)境準備
注意在集群部署之前先刪除zkdata和zklogs目錄下的文件,這兩個目錄下的文件是在偽集群搭建的時候產(chǎn)生的。
服務(wù)器(純凈)? 192.168.66.100
服務(wù)器(zk-1)? 192.168.66.101
服務(wù)器(zk-2)?192.168.66.102![]()
?2.將純凈中的jdk傳到zk-1和zk-2中
# 將純凈虛擬機的jdk傳遞到zk-1虛擬機,文件的位置和純凈的相同
scp -r jdk1.8/ 192.168.66.101:$PWD
?3.分別為zk-1和zk-2兩臺虛擬機配置JDK環(huán)境。
#進入配置文件
vim /etc/profile
#在配置文件添加如下配置
export JAVA_HOME=/usr/local/jdk1.8
export PATH=$PATH:$JAVA_HOME/bin
#使配置文件生效
source /etc/profile
#查看jdk是否配置成功
Java -version
4.修改純凈虛擬機的zookeeper的配置文件zoo.cfg
#進入配置文件
vim zoo.cfg
#在配置文件中添加如下配置
dataDir=/usr/local/zookeeper/zkdata
dataLogDir=/usr/local/zookeeper/zklogs
clientPort=2181
server.1=192.168.66.100:2888:3888
server.2=192.168.66.101:2888:3888
server.3=192.168.66.102:2888:3888
5.將純凈中的zookeeper分發(fā)到zk-1和zk-2兩臺虛擬機。?
scp -r zookeeper/ 192.168.66.101:$PWD
scp -r zookeeper/ 192.168.66.102:$PWD
6.為三個虛擬機中的zookeeper分別設(shè)置ID
#在純凈虛擬機中的zookeeper下的zkdata目錄下使用:
echo 1 > myid
#在zk-1虛擬機中的zookeeper下的zkdata目錄下使用:
echo 2 > myid
#在zk-2虛擬機中的zookeeper下的zkdata目錄下使用:
echo 3 > myid
7.開啟三個虛擬機的zookeeper服務(wù)
#先將三個虛擬機的防火墻關(guān)了
service firewalld stop
#開啟zookeeper服務(wù)(因為沒有配置環(huán)境變量,因此在開啟服務(wù)的時候要在bin下開啟)
[root@localhost bin]# ./zkServer.sh start
#查看服務(wù)狀態(tài)
[root@localhost bin]# ./zkServer.sh status
?
?
注意:
啟動后,用jps應(yīng)該能看到一個進程:QuorumPeerMain。光有進程不代表zk已經(jīng)正常服務(wù),需要用命令檢查狀態(tài):bin/zkServer.sh status 能看到角色模式:為leader或follower,即正常了。
2.3?服務(wù)管理
腳本 | 說明 |
---|---|
zkCleanup | 清理Zookeeper歷史數(shù)據(jù),包括事務(wù)日志文件和快照數(shù)據(jù)文件 |
zkCli | Zookeeper的簡易客戶端 |
zkEnv | 設(shè)置Zookeeper的環(huán)境變量 |
zkServer | Zookeeper服務(wù)器的啟動、停止和重啟腳本 |
配置環(huán)境變量(三臺虛擬機都需要配置)
#打開配置文件
vim /etc/profile
#在配置文件中添加如下配置
export ZOOKEEPER_HOME=/usr/local/zookeeper
export PATH=$PATH:$ZOOKEEPER_HOME/bin
#配置生效
source /etc/profile
這樣在任何一個文件下使用zkServer.sh start 都能啟動zookeeper服務(wù)。
創(chuàng)建一鍵啟動/一鍵停止腳本
如果集群中存在很多的zookeeper服務(wù)的話,一個個開啟或者是關(guān)閉的話無疑是非常麻煩的事情,因此我們可以使用一個腳本來管理全部的服務(wù)的開啟和關(guān)閉。
#在zookeeper的bin下創(chuàng)建可執(zhí)行文件
vim zkStart-all.sh
#為文件添加權(quán)限
chmod +x zkStart-all.sh
#在配置文件添加如下配置
if [ $# -ne 1 ];then
echo "無效參數(shù),用法為: $1 {start|stop|restart|status}"
exit
fi
#遍歷所有節(jié)點
for host in 192.168.66.101 192.168.66.102 192.168.66.103
do
echo "========== $host 正在 $1 ========= "
#發(fā)送命令給目標機器
ssh $host "source /etc/profile; /usr/local/zookeeper/bin/zkServer.sh $1"
done
#啟動腳本
./zkStart-all.sh start
#查看集群中各個虛擬機的狀態(tài)
./zkStart-all.sh status
#關(guān)閉全部zookeeper服務(wù)
./zkStart-all.sh stop
?三、Zookeeper系統(tǒng)模型
?3.1 數(shù)據(jù)模型
在Zookeeper中,可以說 Zookeeper中的所有存儲的數(shù)據(jù)是由znode組成的,節(jié)點也稱為 znode,并以 key/value 形式存儲數(shù)據(jù)。
?樹
介紹:
整體結(jié)構(gòu)類似于 linux 文件系統(tǒng)的模式以樹形結(jié)構(gòu)存儲。其中根路徑以?/?開頭。
保存數(shù)據(jù)
注意:
以 key/value 形式存儲數(shù)據(jù)。key就是znode的節(jié)點路徑,比如 /java , /server。value就是spring和192.168.66.100
3.2 節(jié)點類型及特性
ZooKeeper 節(jié)點是有生命周期的,這取決于節(jié)點的類型。節(jié)點類型可以分為持久節(jié)點、臨時節(jié)點,以及時序節(jié)點,具體在節(jié)點創(chuàng)建過程中,一般是組合使用,可以生成以下 4 種節(jié)點類型:
持久節(jié)點
持久節(jié)點是zookeeper中最常見的一種節(jié)點類型。所謂持久節(jié)點,是指改數(shù)據(jù)節(jié)點被創(chuàng)建后,就會一直存在與zookeeper服務(wù)器上,直到有刪除操作來主動清除這個節(jié)點。
/java ?spring
/zk? zk-1
持久順序節(jié)點
這類節(jié)點的基本特性和上面的節(jié)點類型是一致的。額外的特性是,在ZK中,每個父節(jié)點會為他的第一級子節(jié)點維護一份時序,會記錄每個子節(jié)點創(chuàng)建的先后順序。
臨時節(jié)點
- 從名稱上可以看出該節(jié)點的一個最重要的特性就是臨時性。
- 所謂臨時性是指,如果將節(jié)點創(chuàng)建為臨時節(jié)點,那么該節(jié)點數(shù)據(jù)不會一直存儲在 ZooKeeper 服務(wù)器上。
區(qū)別:
和持久節(jié)點不同的是,臨時節(jié)點的生命周期和客戶端會話綁定。也就是說,如果客戶端會話失效,那么這個節(jié)點就會自動被清除掉。注意,這里提到的是會話失效,而非連接斷開。另外,在臨時節(jié)點下面不能創(chuàng)建子節(jié)點。
臨時順序節(jié)點
臨時順序節(jié)點的基本特性和臨時節(jié)點是一致的,同樣是在臨時節(jié)點的基礎(chǔ)上,添加了順序的特性。
3.3?客戶端命令行
#開啟zookeeper集群
zkStart-all.sh start
#進入純凈虛擬機的zookeeper文件系統(tǒng)
zkCli.sh
#在根路徑下創(chuàng)建持久節(jié)點java,節(jié)點存放的數(shù)據(jù)是spring
[zk: localhost:2181(CONNECTED) 0] create /java spring
#查看根目錄下有哪些節(jié)點
[zk: localhost:2181(CONNECTED) 1] ls /
[java, zookeeper]
#獲取Java節(jié)點的值
[zk: localhost:2181(CONNECTED) 2] get /java
spring
#修改Java節(jié)點的值為springmvc
[zk: localhost:2181(CONNECTED) 3] set /java springmvc
#刪除Java節(jié)點
[zk: localhost:2181(CONNECTED) 5] delete /java
創(chuàng)建臨時節(jié)點:create -e? /java? ?spring
創(chuàng)建持久型順序節(jié)點:create -s? /java? spring
3.4?節(jié)點信息
節(jié)點的狀態(tài)結(jié)構(gòu)
每個節(jié)點都有屬于自己的狀態(tài)信息,這就很像每個人的身份信息一樣。
#查看Java節(jié)點的信息
[zk: localhost:2181(CONNECTED) 8] stat /java
cZxid = 0x500000005
ctime = Wed Jun 28 01:58:53 CST 2023
mZxid = 0x500000005
mtime = Wed Jun 28 01:58:53 CST 2023
pZxid = 0x500000005
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 6
numChildren = 0
?3.5?Watcher監(jiān)聽機制
ZooKeeper 提供了分布式數(shù)據(jù)的發(fā)布/訂閱功能。一個典型的發(fā)布/訂閱模型系統(tǒng)定義了一種一對多的訂閱關(guān)系,能夠讓多個訂閱者同時監(jiān)聽某一個主題對象,當這個主題對象自身狀態(tài)變化時,會通知所有訂閱者,使它們能夠做出相應(yīng)的處理。?
注意:
在ZooKeeper中,引入了Watcher機制來實現(xiàn)這種分布式的通知功能。ZooKeeper 允許客戶端向服務(wù)端注冊一個 Watcher 監(jiān)聽,當服務(wù)端的一些指定事件觸發(fā)了這個Watcher,那么就會向指定客戶端發(fā)送一個事件通知來實現(xiàn)分布式的通知功能。
監(jiān)聽節(jié)點變化
#純凈客戶端監(jiān)聽Java節(jié)點的變化
ls -w /java
#在zk-1客戶端創(chuàng)建/java/spring節(jié)點
[zk: localhost:2181(CONNECTED) 1] create /java/spring spirngmvc
Created /java/spring
#純凈客戶端監(jiān)聽到了Java節(jié)點的變化
[zk: localhost:2181(CONNECTED) 11]
WATCHER::
WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/java
注意:
命令如果使用 -w ,那么監(jiān)聽的是節(jié)點的變化,而不是值的變化。
監(jiān)聽節(jié)點的值的變化
#純凈客戶端監(jiān)聽節(jié)點/java/spring的值
get -w /java/spring
#zk-1客戶端修改/java/spring節(jié)點的值
[zk: localhost:2181(CONNECTED) 2] set /java/spring mybatis
#純凈客戶端監(jiān)聽到節(jié)點值的變化
[zk: localhost:2181(CONNECTED) 2]
WATCHER::
WatchedEvent state:SyncConnected type:NodeDataChanged path:/java/spring
注意:
watch監(jiān)聽機制只能夠使用一次,如果下次想要使用,必須重新監(jiān)聽,就比如ls path watch命令,只能監(jiān)聽節(jié)點路徑的改變一次,如果還想監(jiān)聽,那么需要再執(zhí)行一次ls path watch命令。
3.6?權(quán)限控制ACL
?在ZooKeeper的實際使用中,我們的做法往往是搭建一個共用的ZooKeeper集群,統(tǒng)一為若干個應(yīng)用提供服務(wù)。在這種情況下,不同的應(yīng)用之間往往是不會存在共享數(shù)據(jù)的使用場景的,因此需要解決不同應(yīng)用之間的權(quán)限問題。
注意:?
- ZooKeeper的權(quán)限控制是基于每個znode節(jié)點的,需要對每個節(jié)點設(shè)置權(quán)限
- 每個znode支持設(shè)置多種權(quán)限控制方案和多個權(quán)限
- 子節(jié)點不會繼承父節(jié)點的權(quán)限,客戶端無權(quán)訪問某節(jié)點,但可能可以訪問它的子節(jié)點
權(quán)限模式schema
ZooKeeper內(nèi)置了一些權(quán)限控制方案,可以用以下方案為每個節(jié)點設(shè)置權(quán)限:
方案 | 描述 |
---|---|
world | 只有一個用戶:anyone,代表所有人(默認) |
ip | 使用IP地址認證 |
auth | 使用已添加認證的用戶認證 |
digest | 使用“用戶名:密碼”方式認證 |
授權(quán)對象ID
授權(quán)對象ID是指,權(quán)限賦予的用戶或者一個實體,例如:IP 地址或者機器。授權(quán)模式 schema 與 授權(quán)對象 ID 之間關(guān)系:
權(quán)限模式 | 授權(quán)對象 |
---|---|
IP | 通常是一個IP地址或是IP段,例如“192.168.66.101” |
Digest | 自定義,通常是“username:BASE64(SHA-1(username:password))” |
World | 只有一個ID:"anyone" |
Super | 與Digest模式一致 |
權(quán)限permission
權(quán)限 | ACL簡寫 | 描述 |
---|---|---|
CREATE | c | 可以創(chuàng)建子節(jié)點 |
DELETE | d | 可以刪除子節(jié)點(僅下一級節(jié)點) |
READ | r | 可以讀取節(jié)點數(shù)據(jù)及顯示子節(jié)點列表 |
WRITE | w | 可以設(shè)置節(jié)點數(shù)據(jù) |
ADMIN | a | 可以設(shè)置節(jié)點訪問控制列表權(quán)限 |
權(quán)限相關(guān)命令
命令 | 使用方式 | 描述 |
---|---|---|
getAcl | getAcl | 讀取ACL權(quán)限 |
setAcl | setAcl | 設(shè)置ACL權(quán)限 |
addauth | addauth | 添加認證用戶 |
?示例:World方案
[zk: localhost:2181(CONNECTED) 0] create /node1 1
Created /node1
[zk: localhost:2181(CONNECTED) 1] getAcl /node1
'world,'anyone #默認為world方案
: cdrwa #任何人都擁有所有權(quán)限
示例:IP方案
#創(chuàng)建節(jié)點
[zk: localhost:2181(CONNECTED) 0] create /node2 1
Created /node2
#設(shè)置權(quán)限(只有ip地址為192.168.66.100的客戶端才能操作該節(jié)點)
[zk: localhost:2181(CONNECTED) 1] setAcl /node2 ip:192.168.66.100:cdrwa
#使用IP非 192.168.66.100 的機器
[zk: localhost:2181(CONNECTED) 0] get /node2
Authentication is not valid : /node2 #沒有權(quán)限
示例:Auth方案
#創(chuàng)建節(jié)點
[zk: localhost:2181(CONNECTED) 0] create /node3 1
Created /node3
#添加認證用戶zj,密碼123456
[zk: localhost:2181(CONNECTED) 1] addauth digest zj:123456
#為創(chuàng)建的權(quán)限用戶zj設(shè)置權(quán)限
[zk: localhost:2181(CONNECTED) 2] setAcl /node3 auth:zj:cdrwa
#獲取權(quán)限
[zk: localhost:2181(CONNECTED) 3] getAcl /node3
'digest,'zj:UvJWhBril5yzpEiA2eV7bwwhfLs=
: cdrwa
示例:Digest方案
#密碼加密
echo -n zj:123456 | openssl dgst -binary -sha1 | openssl base64
UvJWhBril5yzpEiA2eV7bwwhfLs=
#創(chuàng)建節(jié)點
[zk: localhost:2181(CONNECTED) 0] create /node4 1
Created /node4
#使用是算好的密文密碼添加權(quán)限:
[zk: localhost:2181(CONNECTED) 1] setAcl /node4 digest:zj:UvJWhBril5yzpEiA2eV7bwwhfLs=:cdrwa
#獲取節(jié)點數(shù)據(jù)沒有權(quán)限
[zk: localhost:2181(CONNECTED) 3] get /node4
Authentication is not valid : /node4
#添加認證用戶
[zk: localhost:2181(CONNECTED) 4] addauth digest zj:123456
#成功讀取數(shù)據(jù)
[zk: localhost:2181(CONNECTED) 5] get /node4 1
四、原生api操作Zookeeper
?利用Zookeeper官方的原生java api進行連接,然后演示一些創(chuàng)建、刪除、修改、查詢節(jié)點的操作。
1.創(chuàng)建maven項目,引入依賴。
<dependencies>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.8</version>
</dependency>
</dependencies>
2.創(chuàng)建連接和節(jié)點
注意:在使用Java連接zookeeper服務(wù)的時候先要在虛擬機開啟zookeeper的服務(wù)。(zkSserver.sh start)
public class ZKMain {
public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
//1.創(chuàng)建與服務(wù)端的對話
/**
* 第一個參數(shù):zookeeper的服務(wù)地址
* 第二個參數(shù):會話超時時間
* 第三個參數(shù):監(jiān)聽機制
*/
ZooKeeper zooKeeper = new ZooKeeper("192.168.66.100:2181," +
"192.168.66.101:2181," +
"192.168.66.102:2181", 4000, null);
//2.查看連接狀態(tài)
System.out.println(zooKeeper.getState());
//3.創(chuàng)建節(jié)點
/*
* 第一個參數(shù):節(jié)點的名字
* 第二個參數(shù):節(jié)點的數(shù)據(jù)
* 第三個參數(shù):節(jié)點的權(quán)限策略
* ZooDefs.Ids.OPEN_ACL_UNSAFE:完全開放任何人都能操作
* ZooDefs.Ids.CREATOR_ALL_ACL:創(chuàng)建者才能操作
* ZooDefs.Ids.READ_ACL_UNSAFE:只讀權(quán)限
* 第四個參數(shù):節(jié)點類型(持久、臨時、……)
* CreateMode.PERSISTENT:持久型節(jié)點
* CreateMode.EPHEMERAL:臨時節(jié)點
* CreateMode.PERSISTENT_SEQUENTIAL:持久順序節(jié)點
* CreateMode.EPHEMERAL_SEQUENTIAL:臨時順序節(jié)點
*/
zooKeeper.create("/zkTest","1".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
}
znode 類型有四種?
- PERSISTENT - 持久化目錄節(jié)點,客戶端與zookeeper斷開連接后,該節(jié)點依舊存在
- PERSISTENT_SEQUENTIAL - 持久化,并帶有序列號
- EPHEMERAL - 臨時目錄節(jié)點,客戶端與zookeeper斷開連接后,該節(jié)點被刪除
- EPHEMERAL_SEQUENTIAL - 臨時,并帶有序列號
?3.修改節(jié)點數(shù)據(jù)
//5.修改節(jié)點
/**
* 參數(shù)一:節(jié)點名稱
* 參數(shù)二:修改的數(shù)據(jù)
* 參數(shù)三:版本,全部修改設(shè)為-1
*/
zooKeeper.setData("/zkTest","2".getBytes(),-1);
4.獲取節(jié)點數(shù)據(jù)和節(jié)點信息
//6.獲取節(jié)點數(shù)據(jù)
/**
* 參數(shù)一:節(jié)點名稱
* 參數(shù)二:監(jiān)聽機制
* 參數(shù)三:狀態(tài)信息
*/
byte[] data = zooKeeper.getData("/zkTest", null, null);
System.out.println(new String(data));
//7.獲取節(jié)點的子節(jié)點信息
/**
* 參數(shù)一:節(jié)點名稱
* 參數(shù)二:監(jiān)聽機制
*/
List<String> children = zooKeeper.getChildren("/zkTest", null);
for (String child : children) {
System.out.println(child);
}
5.監(jiān)聽節(jié)點
public class ZKWatcher {
public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
//1.創(chuàng)建與服務(wù)端的對話
/*
* 第一個參數(shù):zookeeper的服務(wù)地址
* 第二個參數(shù):會話超時時間
* 第三個參數(shù):監(jiān)聽機制,接口類型,使用匿名內(nèi)部類實現(xiàn)該接口
*/
ZooKeeper zooKeeper = new ZooKeeper("192.168.66.100:2181," +
"192.168.66.101:2181," +
"192.168.66.102:2181", 4000, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
System.out.println("正在監(jiān)聽……");
}
});
//2.注冊監(jiān)聽機制監(jiān)聽節(jié)點
zooKeeper.getChildren("/zkTest", new Watcher() {
/*對 /zkTest 的子節(jié)點進行操作的時候會回調(diào)該監(jiān)聽方法*/
@Override
public void process(WatchedEvent watchedEvent) {
//查看監(jiān)聽路徑
System.out.println("監(jiān)聽路徑:"+watchedEvent.getPath());
//查看監(jiān)聽的事件
System.out.println("監(jiān)聽的事件:"+watchedEvent.getType());
}
});
//線程休眠,保持持續(xù)監(jiān)聽
Thread.sleep(Long.MAX_VALUE);
}
}
刪除 /zkTest 的子節(jié)點? /zkTest1 節(jié)點的數(shù)據(jù)時觸發(fā)監(jiān)聽:
?6.監(jiān)聽節(jié)點數(shù)據(jù)
package com.zj;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import java.io.IOException;
public class ZKWatcher {
public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
//1.創(chuàng)建與服務(wù)端的對話
/*
* 第一個參數(shù):zookeeper的服務(wù)地址
* 第二個參數(shù):會話超時時間
* 第三個參數(shù):監(jiān)聽機制,接口類型,使用匿名內(nèi)部類實現(xiàn)該接口
*/
ZooKeeper zooKeeper = new ZooKeeper("192.168.66.100:2181," +
"192.168.66.101:2181," +
"192.168.66.102:2181", 4000, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
System.out.println("正在監(jiān)聽……");
}
});
//3.監(jiān)聽節(jié)點數(shù)據(jù)
zooKeeper.getData("/zkTest", new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
//查看監(jiān)聽路徑
System.out.println("監(jiān)聽路徑:"+watchedEvent.getPath());
//查看監(jiān)聽的事件
System.out.println("監(jiān)聽的事件:"+watchedEvent.getType());
}
},null);
//線程休眠,保持持續(xù)監(jiān)聽
Thread.sleep(Long.MAX_VALUE);
}
}
修改 /zkTest 節(jié)點的數(shù)據(jù):
注意:?
?通過zooKeeper.getchildren("/",new watch()){}來注冊監(jiān)聽,監(jiān)聽的是整個根節(jié)點,但是這個監(jiān)聽只能監(jiān)聽一次。線程休眠是為了讓監(jiān)聽等待事件發(fā)生,不然會隨著程序直接運行完。
五、zkClient操作Zookeeper
使用zookeeper遇到問題:
- 重復注冊watcher
- session失效重連
- 異常處理(刪除節(jié)點不能有子節(jié)點,新增節(jié)點必須有父節(jié)點等)
?zkclient是Github上一個開源的Zookeeper客戶端,在Zookeeper原生 API接口之上進行了包裝,是一個更加易用的Zookeeper客戶端。同時Zkclient在內(nèi)部實現(xiàn)了諸如Session超時重連,Watcher反復注冊等功能,從而提高開發(fā)效率。
添加依賴
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.10</version>
</dependency>
創(chuàng)建會話
//1.創(chuàng)建會話
ZkClient zkClient = new ZkClient("192.168.66.100:2181,192.168.66.101:2181,192.168.66.102:2181");
創(chuàng)建節(jié)點
//3.創(chuàng)建節(jié)點
zkClient.create("/zkTest/zkTest3","3", CreateMode.PERSISTENT);
獲取/zkTest子節(jié)點
//2.獲取/zkTest子節(jié)點
List<String> children = zkClient.getChildren("/zkTest");
children.forEach(System.out::println);
修改節(jié)點數(shù)據(jù)
zkClient.writeData("/zkTest/zkTest3","4");
獲取節(jié)點數(shù)據(jù)
//5.獲取節(jié)點數(shù)據(jù)
String data = zkClient.readData("/zkTest/zkTest3");
刪除節(jié)點
//6.刪除節(jié)點
zkClient.delete("/zkTest/zkTest3");
注冊節(jié)點監(jiān)聽事件
//7.注冊節(jié)點監(jiān)聽事件
zkClient.subscribeChildChanges("/zkTest",new IZkChildListener(){
@Override
public void handleChildChange(String s, List<String> list) throws Exception {
System.out.println("節(jié)點改變了");
list.forEach(System.out::println);
}
});
注冊數(shù)據(jù)監(jiān)聽事件
//8.注冊數(shù)據(jù)監(jiān)聽事件
zkClient.subscribeDataChanges("/zkTest", new IZkDataListener() {
@Override
public void handleDataChange(String s, Object o) throws Exception {
System.out.println("數(shù)據(jù)改變了");
}
@Override
public void handleDataDeleted(String s) throws Exception {
System.out.println("數(shù)據(jù)刪除了");
}
});
六、ApacheCurator操作Zookeeper
Curator是 Netflix公司開源的一套ZooKeeper客戶端框架。和ZkClient一樣,Curator解決了很多ZooKeeper客戶端非常底層的細節(jié)開發(fā)工作,包括連接重連、反復注冊Watcher和 NodeExistsException異常等,目前已經(jīng)成為了Apache的頂級項目,是全世界范圍內(nèi)使用最廣泛的ZooKeeper客戶端之一。
Curator包
- curator-framework:對zookeeper的底層api的一些封裝。
- curator-client:提供一些客戶端的操作,例如重試策略等。
- curator-recipes:封裝了一些高級特性,如:Cache事件監(jiān)聽、選舉、分布式鎖、分布式計數(shù)器、分布式Barrier等。
添加Maven依賴
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.2.0</version>
</dependency>
創(chuàng)建會話
//1.創(chuàng)建會話
String connStr = "192.168.66.100:2181,192.168.66.101:2181,192.168.66.102:2181";
CuratorFramework cur= CuratorFrameworkFactory.builder()
.connectString(connStr)
.connectionTimeoutMs(5000)
.retryPolicy(new ExponentialBackoffRetry(1000,3)) //斷開重試機制
.build();
//2.連接
cur.start();連接
創(chuàng)建節(jié)點
//3.創(chuàng)建節(jié)點
cur.create().withMode(CreateMode.PERSISTENT).forPath("/zkTest/zkTest4","4".getBytes());
獲取數(shù)據(jù)
//4.獲取數(shù)據(jù)
byte[] bytes = cur.getData().forPath("/zkTest/zkTest4");
System.out.println(new String(bytes));
刪除一個節(jié)點
//5.刪除節(jié)點
cur.delete().forPath("/zkTest/zkTest4");
注意:
此方法只能刪除葉子節(jié)點,否則會拋出異常。
刪除一個節(jié)點,并且遞歸刪除其所有的子節(jié)點
cur.delete().deletingChildrenIfNeeded().forPath("/zkTest");
更新一個節(jié)點的數(shù)據(jù)內(nèi)容
client.setData().forPath("path","data".getBytes());
監(jiān)聽節(jié)點
//8.監(jiān)聽機制
NodeCache nodeCache = new NodeCache(cur, "/zkTest/zkTest3");
nodeCache.getListenable().addListener(()-> System.out.println("被修改"));
nodeCache.start();
七、Zookeeper高級
7.1?四字命令
之前使用stat命令來驗證ZooKeeper服務(wù)器是否啟動成功,這里的stat命令就是ZooKeeper 中最為典型的命令之一。ZooKeeper中有很多類似的命令,它們的長度通常都是4個英文字母,因此我們稱之為“四字命令”。
添加配置(前提)
vim zoo.cfg
#開啟四字命令
4lw.commands.whitelist=*
#添加nc命令
yum install nc -y
conf
輸出Zookeeper相關(guān)服務(wù)的詳細配置信息,如客戶端端口,數(shù)據(jù)存儲路徑、最大連接數(shù)、日志路徑、數(shù)據(jù)同步端口、主節(jié)點推舉端口、session超時時間等等。
echo conf| nc localhost 2181
注意:
注意,conf命令輸出的配置信息僅僅是輸出一些最基本的配置參數(shù)。另外,conf命令會根據(jù)當前的運行模式來決定輸出的信息。如果是單機模式(standalone), 就不會輸出諸如initLimit.syncLimit、electionAlg 和electionPort等集群相關(guān)的配置信息。
cons
cons 命令用于輸出當前這臺服務(wù)器上所有客戶端連接的詳細信息,包括每個客戶端的客戶端IP、會話ID和最后一次與服務(wù)器交互的操作類型等。
echo cons | nc localhost 2181
ruok
ruok命令用于輸出當前ZooKeeper服務(wù)器是否正在運行。該命令的名字非常有趣,其諧音正好是“Are you ok”。執(zhí)行該命令后,如果當前ZooKeeper服務(wù)器正在運行,那么返回“imok”, 否則沒有任何響應(yīng)輸出。
echo ruok | nc localhost 2181
stat
stat命令用于獲取ZooKeeper服務(wù)器的運行時狀態(tài)信息,包括基本的ZooKeeper版本、打包信息、運行時角色、集群數(shù)據(jù)節(jié)點個數(shù)等信息,另外還會將當前服務(wù)器的客戶端連接信息打印出來。
echo stat | nc localhost 2181
注意:
除了一些基本的狀態(tài)信息外,stat命令還會輸出一些服務(wù)器的統(tǒng)計信息,包括延遲情況、收到請求數(shù)和返回的響應(yīng)數(shù)等。注意,所有這些統(tǒng)計數(shù)據(jù)都可以通過srst命令進行重置。
mntr
列出集群的關(guān)鍵性能數(shù)據(jù),包括zk的版本、最大/平均/最小延遲數(shù)、數(shù)據(jù)包接收/發(fā)送量、連接數(shù)、zk角色(Leader/Follower)、node數(shù)量、watch數(shù)量、臨時節(jié)點數(shù)。
echo mntr | nc localhost 2181
7.2?選舉機制
核心選舉原則
- Zookeeper集群中只有超過半數(shù)以上的服務(wù)器啟動,集群才能正常工作;
- 在集群正常工作之前,myid小的服務(wù)器給myid大的服務(wù)器投票,直到集群正常工作,選出Leader;
- 半數(shù)機制;
選舉機制流程
- 服務(wù)器1啟動,給自己投票,然后發(fā)投票信息,由于其它機器還沒有啟動所以它收不到反饋信息,服務(wù)器1的狀態(tài)一直屬于Looking(選舉狀態(tài))。
- 服務(wù)器2啟動,給自己投票,同時與之前啟動的服務(wù)器1交換結(jié)果,由于服務(wù)器2的編號大所以服務(wù)器2勝出,但此時投票數(shù)沒有大于半數(shù),所以兩個服務(wù)器的狀態(tài)依然是LOOKING。
- 服務(wù)器3啟動,給自己投票,同時與之前啟動的服務(wù)器1,2交換信息,由于服務(wù)器3的編號最大所以服務(wù)器3勝出,此時投票數(shù)正好大于半數(shù),所以服務(wù)器3成為領(lǐng)導者,服務(wù)器1,2成為小弟。
- 服務(wù)器4啟動,給自己投票,同時與之前啟動的服務(wù)器1,2,3交換信息,盡管服務(wù)器4的編號大,但之前服務(wù)器3已經(jīng)勝出,所以服務(wù)器4只能成為小弟。
- 服務(wù)器5啟動,后面的邏輯同服務(wù)器4成為小弟。
選擇機制中的概念
Serverid:服務(wù)器ID
比如有三臺服務(wù)器,編號分別是1,2,3。
?編號越大在選擇算法中的權(quán)重越大。
Zxid:數(shù)據(jù)ID
服務(wù)器中存放的最大數(shù)據(jù)ID.
?值越大說明數(shù)據(jù)越新,在選舉算法中數(shù)據(jù)越新權(quán)重越大。文章來源:http://www.zghlxwxcb.cn/news/detail-521968.html
Epoch:邏輯時鐘
或者叫投票的次數(shù),同一輪投票過程中的邏輯時鐘值是相同的。每投完一次票這個數(shù)據(jù)就會增加,然后與接收到的其它服務(wù)器返回的投票信息中的數(shù)值相比,根據(jù)不同的值做出不同的判斷。文章來源地址http://www.zghlxwxcb.cn/news/detail-521968.html
到了這里,關(guān)于分布式調(diào)用與高并發(fā)處理 Zookeeper分布式協(xié)調(diào)服務(wù)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!