- Posted by 微博@Yangsc_o
- 原創文章,版權聲明:自由轉載-非商用-非衍生-保持署名 | Creative Commons BY-NC-ND 3.0
zookeeper簡介
什麼是zookeeper
- zooKeeper由雅虎研究院開發, 是Google Chubby的開源實現, 後來託管到Apache, 於2010年11月正式成爲Apache的頂級項目。
- 大數據生態系統裏的很多組件的命名都是某種動物或者昆蟲, 比如hadoop就是🐘, hive就是🐝。 zookeeper即動物園管理者, 顧名思義就是管理大數據生態系統各組件
的管理員, 如下圖所示:
zookeeper應用場景
zooKeeper是一個經典的分佈式數據一致性解決方案, 致力於爲分佈式應用提供一個高性能、 高可用, 且具有嚴格順序訪問控制能力的分佈式協調存儲服務。
- 維護配置信息
- 分佈式鎖服務
- 集羣管理
- 生成分佈式唯一ID
zookeeper特點
- 高性能
zooKeeper將全量數據存儲在內存中, 並直接服務於客戶端的所有非事務請求, 尤其適用於以讀爲主的應用場景
- 高可用
zooKeeper一般以集羣的方式對外提供服務, 一般3 ~ 5臺機器就可以組成一個可用的Zookeeper集羣了, 每臺機器都會在內存中維護當前的服務器狀態, 並且每臺機器之間都相
互保持着通信。 只要集羣中超過一半的機器都能夠正常工作, 那麼整個集羣就能夠正常對外服務
- 嚴格順序訪問
對於來自客戶端的每個更新請求, ZooKeeper都會分配一個全局唯一的遞增編號,這個編號反映了所有事務操作的先後順序
zookeeper的數據模型
- zookeeper的數據節點可以視爲樹狀結構(或者目錄) , 樹中的各節點被稱爲znode(即zookeeper node) , 一個znode可以有多個子節點。 zookeeper節點在結構上表現爲樹狀; 使用路徑path來定位某個znode;
- znode, 兼具文件和目錄兩種特點。 既像文件一樣維護着數據、 元信息、 ACL(權限控制)、 時間戳等數據結構, 又像目錄一樣可以作爲路徑標識的一部分;
節點數據結構
- 一個znode大體上分爲3各部分:
- 節點的數據: 即znode data(節點path, 節點data)的關係就像是java map中(key,value)的關係節點的子節點children
- 節點的狀態stat: 用來描述當前節點的創建、 修改記錄, 包括cZxid、 ctime等
- 節點狀態stat的屬性
[zk: localhost:2181(CONNECTED) 7] get /ns-1/tenant
cZxid = 0x6a0000000a
ctime = Wed Mar 27 09:56:44 CST 2019
mZxid = 0x6a0000000a
mtime = Wed Mar 27 09:56:44 CST 2019
pZxid = 0x6a0000000e
cversion = 2
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 2
- 屬性說明:
cZxid: 數據節點創建時的事務 ID
ctime: 數據節點創建時的時間
mZxid: 數據節點最後一次更新時的事務 ID
mtime: 數據節點最後一次更新時的時間
pZxid: 數據節點的子節點最後一次被修改時的事務 ID
cversion: 子節點的更改次數
dataVersion: 節點數據的更改次數
aclVersion: 節點的 ACL 的更改次數
ephemeralOwner: 如果節點是臨時節點, 則表示創建該節點的會話的
SessionID; 如果節點是持久節點, 則該屬性值爲 0
dataLength: 數據內容的長度
numChildren: 數據節點當前的子節點個數
節點類型
zookeeper中的節點有兩種, 分別爲臨時節點和永久節點。節點的類型在創建時即被確定, 並且不能改變。
- 臨時節點: 該節點的生命週期依賴於創建它們的會話。 一旦會話(Session)結束, 臨時節點將被自動刪除, 當然可以也可以手動刪除。 雖然每個臨時的Znode都會綁定到一個客戶端會話, 但他們對所有的客戶端還是可見的。 另外, ZooKeeper的臨時節點不允許擁有子節點。
- 持久化節點: 該節點的生命週期不依賴於會話, 並且只有在客戶端顯示執行刪除操作的時候, 他們才能被刪除;
zookeeper常用Shell命令
- 新增節點
create [-s] [-e] path data #其中-s 爲有序節點, -e 臨時節點
- 更新節點
更新節點的命令是 set , 可以直接進行修改eg:set /hadoop “345”
基於版本號進行更改時, 樂觀鎖機制, 當你傳入的數據版本號(dataVersion) 和當前節點的數據版本號不符合時,zookeeper 會拒絕本次修改:eg: set /hadoop “3456” 1
version No is not valid : /hadoop
- 刪除節點
delete path [version]
和更新節點數據一樣, 也可以傳入版本號, 當你傳入的數據版本號 (dataVersion)和當前節點的數據版本號不符合時,zookeeper 不會執行刪除操作。
要想刪除某個節點及其所有後代節點, 可以使用遞歸刪除, 命令爲 rmr path
- 查看節點
get path
- 查看節點狀態
可以使用 stat 命令查看節點狀態, 它的返回值和 get 命令類似, 但不會返回節點數據
- 查看節點列表
查看節點列表有 ls path 和 ls2 path 兩個命令, 後者是前者的增強, 不僅可以查看指定路徑下的所有節點, 還可以查看當前節點的信息
- 監聽器 get path [watch]
使用 get path [watch] 註冊的監聽器能夠在節點內容發生改變的時候, 向客戶端發出通知。 需要注意的是 zookeeper 的觸發器是一次性的 (One-time trigger), 即觸發一次後就會立即失效。
- 監聽器stat path [watch]
使用 stat path [watch] 註冊的監聽器能夠在節點狀態發生改變的時候, 向客
戶端發出通知
- 監聽器ls\ls2 path [watch]
使用 ls path [watch] 或 ls2 path [watch] 註冊的監聽器能夠監聽該節點下所有子節點的增加和刪除操作
zookeeper的acl權限控制
概述
- zookeeper 類似文件系統, client 可以創建節點、 更新節點、 刪除節點,可以通過acl控制節點的權限的控制;
- 使用scheme: id: permission 來標識, 主要涵蓋 3 個方面:
權限模式(scheme) : 授權的策略
授權對象(id) : 授權的對象
權限(permission) : 授予的權限 - 其特性如下
- zooKeeper的權限控制是基於每個znode節點的, 需要對每個節點設置權限
- 每個znode支持設置多種權限控制方案和多個權限
- 子節點不會繼承父節點的權限, 客戶端無權訪問某節點, 但可能可以訪問它的子節點
權限模式
- world 只有一個用戶: anyone, 代表登錄zokeeper所有人(默認)
- ip 對客戶端使用IP地址認證
- auth 使用已添加認證的用戶認證
- digest 使用“用戶名:密碼”方式認證
授權的對象
- 給誰授予權限
- 授權對象ID是指, 權限賦予的實體, 例如: IP 地址或用戶。
授予的權限
- create、delete、read、writer、 admin,即 增、 刪、 改、 查、 管理權限,這5種權限簡寫爲cdrwa;
注意:這5種權限中, delete是指對子節點的刪除權限, 其它4種權限指對自身節點的操作權限 - 授權的相關命令
- getAcl getAcl 讀取ACL權限
- setAcl setAcl 設置ACL權限
- addauth addauth 添加認證用戶
zookeeper事件監聽機制
watcher概念
- zookeeper提供了數據的發佈/訂閱功能,多個訂閱者可同時監聽某一特定主題對
象,當該主題對象的自身狀態發生變化時(例如節點內容改變、節點下的子節點列表改變等),會實時、主動通知所有訂閱者
- zookeeper採用了Watcher機制實現數據的發佈/訂閱功能。該機制在被訂閱對
象發生變化時會異步通知客戶端,因此客戶端不必在Watcher註冊後輪詢阻塞,從而減輕了客戶端壓力。
- watcher機制實際上與觀察者模式類似,也可看作是一種觀察者模式在分佈式場
景下的實現方式。
watcher架構
Watcher實現由三個部分組成:
- Zookeeper服務端
- Zookeeper客戶端
- 客戶端的ZKWatchManager對象
客戶端首先將Watcher註冊到服務端,同時將Watcher對象保存到客戶端的Watch管
理器中。當ZooKeeper服務端監聽的數據狀態發生變化時,服務端會主動通知客戶端,接着客戶端的Watch管理器會觸發相關Watcher來回調相應處理邏輯,從而完成整體的數據發佈/訂閱流程
watcher特性
watcher接口設計
Watcher是一個接口,任何實現了Watcher接口的類就是一個新的Watcher。Watcher內部包含了兩個枚舉類:KeeperState、EventType
- Watcher通知狀態(KeeperState)
KeeperState是客戶端與服務端連接狀態發生變化時對應的通知類型。路徑爲
org.apache.zookeeper.Watcher.Event.KeeperState,是一個枚舉類,其枚舉屬性
如下:
- Watcher事件類型(EventType)
EventType是數據節點(znode)發生變化時對應的通知類型。EventType變化時
KeeperState永遠處於SyncConnected通知狀態下;當KeeperState發生變化時,
EventType永遠爲None。其路徑爲org.apache.zookeeper.Watcher.Event.EventType,
是一個枚舉類,枚舉屬性如下:
注:客戶端接收到的相關事件通知中只包含狀態及類型等信息,不包括節點變化前後的具體內容,變化前的數據需業務自身存儲,變化後的數據需調用get等方法重新獲取;
應用
瞭解完zk的基本特性,據此分析一下具體的使用場景案例
配置中心
eg: 數據庫用戶名和密碼信息放在一個配置文件中,應用讀取該配置文件,配置文件信息放入緩存。若數據庫的用戶名和密碼改變時候,還需要重新加載緩存,比較麻煩,通過ZooKeeper可以輕鬆完成,當數據庫發生變化時自動完成緩存同步。
設計思路:
- 連接zookeeper服務器
- 讀取zookeeper中的配置信息,註冊watcher監聽器,存入本地變量
- 當zookeeper中的配置信息發生變化時,通過watcher的回調方法捕獲數據變化事件
- 重新獲取配置信息
生成分佈式唯一ID
在單庫單表型系統中,通常可以使用數據庫字段自帶的auto_increment屬性來自動爲每條記錄生成一個唯一的ID。但是分庫分表後,就無法在依靠數據庫的auto_increment屬性來唯一標識一條記錄了。可以用zookeeper在分佈式環境下生成全局唯一ID。
設計思路:
- 連接zookeeper服務器
- 指定路徑生成臨時有序節點
- 取序列號及爲分佈式環境下的唯一ID
注:生產環境,一般採用別的方式:例如雪花算法等
分佈式鎖
分佈式鎖有多種實現方式,比如通過數據庫、redis都可實現。作爲分佈式協同
工具ZooKeeper,當然也有着標準的實現方式。下面介紹在zookeeper中如何實現排他鎖。
設計思路:
- 每個客戶端往/Locks下創建臨時有序節點/Locks/Lock 000000001
- 客戶端取得/Locks下子節點,並進行排序,判斷排在最前面的是否爲自己,如果自己的鎖節點在第一位,代表獲取鎖成功
- 如果自己的鎖節點不在第一位,則監聽自己前一位的鎖節點。例如,自己鎖節點
Lock 000000001 - 當前一位鎖節點(Lock 000000002)的邏輯
- 監聽客戶端重新執行第2步邏輯,判斷自己是否獲得了鎖
一致性協議:zab協議
- zab協議 的全稱是 Zookeeper Atomic Broadcast (zookeeper原子廣播)。
- zookeeper 是通過 zab協議來保證分佈式事務的最終一致性基於zab協議,zookeeper集羣中的角色主要有以下三類,如下表所示:
- zab廣播模式工作原理,通過類似兩階段提交協議的方式解決數據一致性:
- leader從客戶端收到一個寫請求
- leader生成一個新的事務併爲這個事務生成一個唯一的ZXID
- leader將這個事務提議(propose)發送給所有的follows節點
- follower節點將收到的事務請求加入到歷史隊列(history queue)中,併發送ack leader
- 當leader收到大多數follower(半數以上節點)的ack消息,leader會發送commit請求
- 當follower收到commit請求時,從歷史隊列中將事務請求commit
zookeeper的leader選舉
服務器狀態
- looking:尋找leader狀態。當服務器處於該狀態時,它會認爲當前集羣中沒有
leader,因此需要進入leader選舉狀態。 - leading: 領導者狀態。表明當前服務器角色是leader。
- following: 跟隨者狀態。表明當前服務器角色是follower。
- observing:觀察者狀態。表明當前服務器角色是observer
服務器啓動時期的leader選舉
在集羣初始化階段,當有一臺服務器server1啓動時,其單獨無法進行和完成
leader選舉,當第二臺服務器server2啓動時,此時兩臺機器可以相互通信,每臺機器都試圖找到leader,於是進入leader選舉過程。選舉過程如下:
- 每個server發出一個投票。由於是初始情況,server1和server2都會將自己作爲
leader服務器來進行投票,每次投票會包含所推舉的服務器的myid和zxid,使用(myid, zxid)來表示,此時server1的投票爲(1, 0),server2的投票爲(2, 0),然後各自將這個投票發給集羣中其他機器。- 集羣中的每臺服務器接收來自集羣中各個服務器的投票。
- 處理投票。針對每一個投票,服務器都需要將別人的投票和自己的投票進行pk,pk規則如下優先檢查zxid。zxid比較大的服務器優先作爲leader。如果zxid相同,那麼就比較myid。myid較大的服務器作爲leader服務器。對於Server1而言,它的投票是(1, 0),接收Server2的投票爲(2, 0),首先會比較兩者的zxid,均爲0,再比較myid,此時server2的myid最大,於是更新自己的投票爲(2, 0),然後重新投票,對於server2而言,其無須更新自己的投票,只是再次向集羣中所有機器發出上一次投票信息即可。
- 統計投票。每次投票後,服務器都會統計投票信息,判斷是否已經有過半機器接受到相同的投票信息,對於server1、server2而言,都統計出集羣中已經有兩臺機器接受了(2, 0)的投票信息,此時便認爲已經選出了leader
- 改變服務器狀態。一旦確定了leader,每個服務器就會更新自己的狀態,如果是follower,那麼就變更爲following,如果是leader,就變更爲leading
服務器運行時期的Leader選舉
- 在zookeeper運行期間,leader與非leader服務器各司其職,即便當有非leader
服務器宕機或新加入,此時也不會影響leader,但是一旦leader服務器掛了,那麼整個集羣將暫停對外服務,進入新一輪leader選舉,其過程和啓動時期的Leader選舉過程基本一致。 - 假設正在運行的有server1、server2、server3三臺服務器,當前leader是
server2,若某一時刻leader掛了,此時便開始Leader選舉。選舉過程如下:
- 變更狀態。leader掛後,餘下的服務器都會將自己的服務器狀態變更爲looking,然後開始進入leader選舉過程。
- 每個server會發出一個投票。在運行期間,每個服務器上的zxid可能不同,此時假定server1的zxid爲122,server3的zxid爲122,在第一輪投票中,server1和server3都會投自己,產生投票(1, 122),(3, 122),然後各自將投票發送給集羣中所有機器。
- 接收來自各個服務器的投票。與啓動時過程相同
- 處理投票。與啓動時過程相同,此時,server3將會成爲leader。
- 統計投票。與啓動時過程相同。
- 改變服務器的狀態。與啓動時過程相同。
observer角色及其配置
observer角色特點:
- 不參與集羣的leader選舉
- 不參與集羣中寫數據時的ack反饋
- 爲了使用observer角色,在任何想變成observer角色的配置文件中加入如下配
置:peerType=observer- 並在所有server的配置文件中,配置成observer模式的server的那行配置追
加:observer,例如:server.3=192.168.60.130:2289:3389:observer
zookeeper 在java中訪問API
1、zookeeper java API
https://mvnrepository.com/artifact/org.apache.zookeeper/zookeeper
2、開源客戶端curator
https://curator.apache.org/dependency-info.html