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