視頻教程傳送門 -> https://www.bilibili.com/video/BV19b411772h
1. Zookeeper
Zookeeper是一個分佈式協調服務的開源框架。
Zookeeper本質是一個分佈式的小文件存儲系統。
@Zookeeper特性
1)全局數據一致:每個服務器保存一份相同的數據副本,客戶端無論連接哪個服務器展示的數據是一致的
2)可靠性:消息(即增刪改查)被一臺服務器接受,那麼也將被所有服務器接受
3)順序性:全局有序 -- 在一臺服務器上,如果消息a在消息b前發佈,則在所有服務器上都是如此
偏序 -- 消息b在消息a後被同一個發送着發佈,a必將排在b前面
4)數據更新原子性:一次數據更新要麼成功(半數以上節點成功),要麼失敗
5)實時性:Zookeeper保證客戶端將在一個時間間隔範圍內獲得服務器的更新信息,或者服務器失效的信息
ZooKeeper具備CP特性
任何時刻的訪問請求能得到一致的數據結果
系統對網絡分割具備容錯性
不保證每次服務請求的可用性
@Zookeeper集羣角色
Leader
Zookeeper集羣工作的核心
事務請求(寫操作)的唯一調度和處理者,保證集羣事務處理的順序性
集羣內部各個服務器的調度者
說明:對於create、setData、delete等有寫操作的請求,需要統一轉發給Leader處理,Leader需要決定編號、執行操作,這個過程成爲一個事務。
Follower
處理客戶端非事務(讀操作)請求,轉發事務請求給Leader
參與集羣Leader選舉投票
Observer 針對訪問量比較大的Zookeeper集羣,可以增加觀察者角色(橫向擴展)
提供非事務服務、不參加投票
@Zookeeper集羣搭建
Zookeeper集羣通常由2n+1臺服務器組成(Leader選舉基於Paxos算法實現)
1)Leader+Follower模式
- 配置主機名稱和IP地址映射
- 修改Zookeeper配置文件
- 遠程複製分發安裝文件
- 設置myid
- 啓動Zookeeper集羣
2)啓用Observer模式
可在對應節點的配置文件添加 peerType=observer
並且在配置文件指定哪些節點被指定爲Observer,如
server.1:localhost:2181:3181:observer
以3臺服務器爲例,搭建步驟如下
step1
確認環境已安裝jdk
檢查集羣時間是否同步
檢查防火牆是否關閉(生產環境配置防火牆規則)
檢查是否配置主機IP映射
step2
tar -xzvf zookeeper-xxx.tar.gz
mv zookeeper-xxx zookeeper
step3
修改環境變量(3臺Zookeeper都要修改)
vi /etc/profile 添加如下行
export ZOOKEEPER_HOME=/home/hadoop/zookeeper
export PATH=$PATH:$ZOOKEEPER_HOME/bin
source /etc/profile
step4
修改Zookeeper配置文件(先在一臺修改)
cd zookeeper/conf
cp zoo_sample.cfg zoo.cfg
vi zoo.cfg 添加如下行
#可以修改數據路徑
dataDir=/root/apps/zookeeper/zkdata
#兩個端口分別是 心跳端口、選舉端口
server.1=node-1:2888:3888
server.2=node-2:2888:3888
server.3=node-3:2888:3888
創建文件myid
cd /root/apps/zookeeper/zkdata
echo 1 > myid
step5
分發安裝包到其它服務器
scp -r /root/apps root@node-1:/
scp -r /root/apps root@node-2:/
step6
修改其它服務器的myid文件
服務器node-1 修改myid內容爲2
服務器node-2 修改myid內容爲3
step7
在每臺服務器啓動zookeeper
cd bin
./zkServer.sh start
查看狀態(還可以看到是leader還是follower)
./zkServer.sh status
2. Zookeeper數據模型
@Znode特點
類似於文件系統的目錄樹,但也有的不同之處,其特點如下
1)Znode兼具文件和目錄兩種特點
- 既像文件一樣維護數據、元信息、ACL、時間戳等數據結構
- 又像目錄一樣可以作爲路徑標識的一部分,且可以具有子Znode
用戶可以對Znode具有增刪改查
2)Znode具有原子性操作
- 讀操作將獲取與節點相關的所有數據
- 寫操作將替換掉節點的所有數據
每一個節點都擁有自己的ACL(訪問控制列表),這個列表限定了特定用戶對目標節點可以執行的操作
3)Znode存儲數據大小有限制
Zookeeper雖然可以關聯一些數據,但並沒有被設計爲常規的數據庫或者大數據存儲
相反,它用來管理調度數據,如分佈式應用中的配置文件信息、狀態信息、彙集位置等(都是很小的數據,KB級)
Zookeeper的服務器和客戶端都被設計爲嚴格檢查並限制每個Znode的數據大小至多1M
4)Znode通過路徑引用
路徑必須是絕對路徑、且是唯一的
@Znode組成
每個Znode由3部分組成
stat 狀態信息,描述Znode版本、權限等信息
data 與該Znode關聯的數據
children 該Znode下的子節點
@Znode節點類型
臨時節點&永久節點
臨時節點:該節點的生命週期依賴於創建它們的會話
會話結束臨時節點將被自動刪除,也可以手動刪除
臨時節點不允許擁有子節點
永久節點:該節點的生命週期不依賴於會話,執行刪除操作纔會被刪除
Znode的序列特性
如果創建的時候指定的話,該Znode的名字後面會自動追加一個不斷增加的序列號
序列號記錄每個子節點創建的先後順序,對於此節點的父節點來說是唯一的
序列號的格式爲"%10d"(10位數字,沒有數值的數位用0補充)
@Znode屬性
通過命令get,可以獲得節點的屬性
dataVersion:數據版本號,每次對節點進行set操作,dataVersion的值都會增加1(即使設置的是相同數據)
可以有效避免數據更新時出現的先後順序問題
cversion:子節點的版本號,當Znode的子節點發生變化時,cversion的值就會加1
aclVersion:ACL的版本號
cZxid:Znode創建的事務id
mZxid:Znode被修改的事務id,每次對Znode的修改都會更新該值
對於zk來說,每次的變化都會產生一個唯一的事務id,zxid(Zookeeper Transaction Id)
通過zxid可以確定更新操作的先後順序。例如,如果zxid1小於zxid2,說明zxid1操作先於zxid2發生
zxid對於整個zk都是唯一的,即使操作的是不同的znode
ctime:節點創建時的時間戳
mtime:節點最新一次更新發生時的時間戳
ephemeralOwner:如果該節點位臨時節點,ephemeralOwner值表示與該節點綁定的session id;反之,ephemeralOwner爲0
在client和server通信之前,首先需要建立連接(session),連接建立後,如果發生連接超時、授權失敗、顯式關閉連接,連接處於CLOSED狀態,此時session結束
3. Zookeeper shell
@客戶端連接
運行 zkCli.sh -server ip 進入命令行工具
輸入help,輸出zk shell提示
說明:-server可選,不帶會在本機查找zk,帶上連接到遠端zk
@shell基本操作
1)創建節點
create [-s] [-e] path data acl
-s指定創建順序節點
-e指定創建臨時節點
acl用來進行權限控制
2)讀取節點
ls 命令 列出Zookeeper指定節點下的所有子節點(只能查看第一級子節點)
get 命令 獲取指定Znode的數據內容和屬性信息
ls2 命令 列出的信息比ls詳細,包括子節點和屬性信息,但不包括數據內容
ls path [watch]
get path [watch]
ls2 path [watch]
3)更新節點
set path data [version]
data 更新的新內容
version 數據的版本
更新後dataVersion會遞增
4)刪除節點
delete path [version]
若刪除的節點存在子節點則無法刪除
可以先刪除子節點
或者使用 rmr path 遞歸刪除節點
5)quata
setquota -n|-b val path 對節點增加限制
-n 表示子節點的最大個數
-b 表示數據值的最大長度
val 子節點的最大個數或數據值的最大長度
path 節點路徑
listquota path 列出指定節點的quota
delquota [-n|-b] path 刪除quota
注意:即使節點數超了,仍能創建,會在日誌zookeeper.out中打印WARN
6)history列出命令歷史
redo重新執行指定命令編號的歷史命令
4. Zookeeper Watcher
Zookeeper提供了分佈式數據發佈/訂閱功能
一對多訂閱=> 能讓多個訂閱者同時監聽某一個主題對象
當主題對象自身狀態變化時,會通知所有訂閱者
Zookeeper引入Watcher機制來實現分佈式通知功能
Zookeeper允許客戶端向服務器註冊一個Watcher監聽,當服務端的一些事件觸發了這個Watcher,就會向指定的客戶端發送一個事件來通知
觸發事件的種類有節點創建、節點刪除、節點改變、子節點改變等
@Watcher機制過程
客戶端向服務端註冊Watcher
服務端事件發生觸發Watcher
客戶端回調Watcher得到觸發事件情況
@Watcher機制特點
1)一次性觸發
事件發生觸發監聽,一個watcher event就會發送到設置監聽的客戶端
這種效果式一次性的,再發生同樣的事件不會觸發
2)事件封裝
Zookeeper使用WatchedEvent對象來封裝服務端事件並傳遞
WatchedEvent包含了每一個事件的三個基本屬性:
通知狀態(keeperState)
事件類型(eventType)
節點路徑(path)
3)event異步發送
Watcher的通知事件從服務端發送到客戶端是異步的
4)先註冊再觸發
Zookeeper中的Watcher機制必須客戶端先去服務端註冊監聽
同一個事件類型在不同的通知狀態中代表的含義不同,舉例如下表
其中連接狀態事件(type=None,path=null)不需要客戶端註冊,客戶端只要有需要直接處理就行了
@Shell客戶端設置Watcher
設置節點數據變動監聽
【例】get /aaa0000000001 watch
通過另一個客戶端更改節點數據
set /aaa0000000001 456789
此時設置監聽的節點收到的通知
再次通過另一個客戶端更改節點數據,設置監聽的節點不會收到通知
5. Zookeeper選舉機制
默認算法是FastLeaderElection,採用投票數大於半數則勝出的邏輯。
@相關概念
服務器ID
例如有三臺服務器,編號分別爲1、2、3
編號越大在選舉算法中的權重越大
選舉狀態
LOOKING 競選狀態
FOLLOWING 隨從狀態,同步leader狀態,參與投票
OBSERVING 觀察狀態,同步leader狀態,不參與投票
LEADING 領導者狀態
數據ID
服務器中存放的最新數據version
值越大說明數據越新,在選舉算法中數據越新權重越大
邏輯時鐘/投票次數
同一輪投票過程中的邏輯時鐘值是相同的
每投完一次票這個數據會增加
@全新集羣選舉
假設目前有5臺服務器,每臺服務器均沒有數據
編號分別是1、2、3、4、5,按編號依次啓動
規則:每個機器都給自己投票、投票數過半選舉結束
step1 服務器1啓動,給自己投票
然後發投票信息,由於其它機器還沒有啓動所以它收不到反饋信息
服務器1處於Looking狀態
step2 服務器2啓動,給自己投票
同時與服務器1交換信息
由於服務器2的編號大所以勝出
但此時投票數沒有大於半數,服務器1、2處於Looking狀態
step3 服務器3啓動,給自己投票
同時與服務器1、2交換信息
由於服務器2的編號大所以勝出
此時投票數正好大於半數,投票結束,服務器3成爲Leader,服務器1、2成爲Follower
step4 服務器4啓動,給自己投票
同時與服務器1、2、3交換信息
投票已結束,服務器4成爲Follower
step5 服務器5啓動,同服務器4
全新集羣選舉主要影響因素 -> 服務器編號
@非全新集羣選舉
對於運行正常的zookeeper集羣,中途有服務器down,需要重新選舉時,選舉過程就需要加入數據ID、服務器ID和邏輯時鐘
數據ID:數據新的version就大,數據每次更新都會更新version
服務器ID:配置的myid中的值,每個機器一個
邏輯時鐘:這個值從0開始遞增,每次選舉對應一個值。如果在同一次選舉中,這個值是一致的
選舉Leader標準如下
1)邏輯時鐘小的選舉結果被忽略,重新投票
2)同一邏輯時鐘後,數據id大的勝出
3)數據id相同的情況下,服務器id大的勝出
6. 典型應用
@數據發佈與訂閱(配置中心)
配置中心=> 發佈者將數據發佈到ZK節點上,供訂閱者動態獲取數據,實現配置信息的集中式管理和動態更新
應用在啓動的時候會主動來獲取一次配置,同時,在節點上註冊一個Watcher
配置有更新會實時通知到訂閱的客戶端,從而達到獲取最新配置信息的目的
例如,分佈式搜索服務中,索引的元信息和服務器集羣節點狀態存放在ZK的一些指定節點,供各個客戶端訂閱使用
注意:適合數據量很小的場景,這樣數據更新會比較快
@命名服務(Naming Service)
在分佈式系統中,通過使用命名服務,客戶端應用能夠根據指定名字來獲取資源或服務的地址,提供者等信息
被命名的實體通常可以是集羣中的機器、提供的服務地址、遠程對象等 => 可以統稱爲名字(Name)
通過調用ZK提供的創建節點的API,可以創建一個全局唯一的path => 可以作爲一個名稱
例如,阿里巴巴開源的分佈式服務框架Dubbo中使用Zookeeper來作爲其命名服務,維護全局的服務地址列表
@分佈式鎖
鎖服務可以分爲兩類,一個是保持獨佔,另一個是控制時序
1)保持獨佔
所有試圖獲取這個鎖的客戶端,最終只有一個可以成功獲得這把鎖
通常做法是把zk上的一個Znode看作一把鎖,通過create Znode的方式來實現
所有客戶端都去創建/distribute_lock節點(是臨時節點且非序列化),最終成功創建的那個客戶端擁有這把鎖
2)控制時序
所有視圖來獲取這個鎖的客戶端,最終都會被安排執行,只是有個全局時序
做法基本和前述相同,只是/distribute_lock已經預先存在
客戶端在它下面創建臨時有序節點
父節點/distribute_lock維持一份sequence,保證子節點創建的時序性
推薦閱讀:
我們能用zookeeper做什麼 https://blog.csdn.net/zhangzq86/article/details/80981234