zookeeper是一個樹形結構,相似於前端開發中的 tree.js 組件;node
zk的數據模型也能夠理解爲 linux/unix 的文件目錄:/usr/locallinux
每個節點都稱之爲 znode,它能夠有子節點,也能夠有數據git
子節點: 就是父目錄下的一個子目錄,在 zk中稱之爲節點,每個節點中都有一些相應的數據,就像目錄下有一些文件數據同樣github
每一個節點分爲臨時節點和永久節點,臨時節點在客戶端斷開後消失數據庫
永久節點:其實就是一個持久化的過程,咱們存了一些數據,只有人爲的狀況在纔會刪除 如是session超時或者是session丟失,數據仍是會一直存在的。臨時節點生命週期依賴建立它的會話,一旦會話結束,臨時節點將會被刪除。臨時節點不容許有子節點。設計模式
每一個zk節點都是有各自的版本號,能夠經過命令行來顯示節點信息;session
節點的信息其實就是節點的詳情,詳情中包含了一些版本號,版本號是累加的,每當節點中的數據發生變化,版本號就會累加(樂觀鎖)併發
刪除/修改過期節點,版本號不匹配則會報錯框架
例如咱們在查詢某一個節點的時候,好比說它的節點是1,通過兩我的進行刪除或者修改以後,那麼它的節點會由1變爲2,在變爲3,此時咱們須要去修改或者刪除這個節點,那麼刪除這個節點的時候,咱們傳入的版本號是一個老的版本號,那麼這個時候就會報一個版本號不匹配的異常,這也是在數據庫中是使用樂觀鎖的一種表現
每個zk節點存儲的數據不宜過大,幾k便可(官方推薦);
節點是能夠設置權限acl ,能夠經過權限來限定用戶的訪問
acl:權限控制列表,後續會講到;
在分佈式系統中,Master每每用來協調集羣中其餘系統單元,具備對分佈式系統狀態變動的決定權,如在讀寫分離的應用場景中,客戶端的寫請求每每是由Master來處理,或者其經常處理一些複雜的邏輯並將處理結果同步給其餘系統單元。利用Zookeeper的強一致性,可以很好地保證在分佈式高併發狀況下節點的建立必定可以保證全局惟一性,即Zookeeper將會保證客戶端沒法重複建立一個已經存在的數據節點。
首先建立/master_election/2019-07-05節點,客戶端集羣天天會定時往該節點下建立臨時節點,如/master_election/2019-07-05/binding,這個過程當中,只有一個客戶端可以成功建立,此時其變成master,其餘節點都會在節點/master_election/2019-07-05上註冊一個子節點變動的Watcher,用於監控當前的Master機器是否存活,一旦發現當前Master掛了,其他客戶端將會從新進行Master選舉。
這也就是所謂的首腦模式,從而保證咱們的集羣是高可用的;
數據發佈/訂閱系統,即配置中心。須要發佈者將數據發佈到Zookeeper的節點上,供訂閱者進行數據訂閱,進而達到動態獲取數據的目的,實現配置信息的集中式管理和數據的動態更新。發佈/訂閱通常有兩種設計模式:推模式和拉模式,服務端主動將數據更新發送給全部訂閱的客戶端稱爲推模式;客戶端主動請求獲取最新數據稱爲拉模式,Zookeeper採用了推拉相結合的模式,客戶端向服務端註冊本身須要關注的節點,一旦該節點數據發生變動,那麼服務端就會向相應的客戶端推送Watcher事件通知,客戶端接收到此通知後,主動到服務端獲取最新的數據。
若將配置信息存放到Zookeeper上進行集中管理,在一般狀況下,應用在啓動時會主動到Zookeeper服務端上進行一次配置信息的獲取,同時,在指定節點上註冊一個Watcher監聽,這樣在配置信息發生變動,服務端都會實時通知全部訂閱的客戶端,從而達到實時獲取最新配置的目的。
Dubbo是集團開源的分佈式服務框架,致力於提供高性能和透明化的遠程服務調用解決方案和基於服務框架展開的完整SOA服務治理方案。
其中服務自動發現是最核心的模塊之一,該模塊提供基於註冊中心的目錄服務,使服務消費方可以動態的查找服務提供方,讓服務地址透明化,同時服務提供方能夠平滑的對機器進行擴容和縮容,其註冊中心能夠基於提供的外部接口來實現各類不一樣類型的註冊中心,例如數據庫、ZK和Redis等。接下來看一下基於ZK實現的Dubbo註冊中心。
/dubbo: 這是Dubbo在ZK上建立的根節點。
/dubbo/com.foo.BarService:這是服務節點,表明了Dubbo的一個服務。
/dubbo/com.foo.BarService/Providers:這是服務提供者的根節點,其子節點表明了每一個服務的真正的提供者。
/dubbo/com.foo.BarService/Comsumers:這是服務消費者的根節點,其子節點表明了每個服務的真正的消費者。
Dubbo基於ZK實現註冊中心的工做流程:
服務提供者:在初始化啓動的時候首先在/dubbo/com.foo.BarService/Providers節點下建立一個子節點,同時寫入本身的URL地址,表明這個服務的一個提供者。
服務消費者 : 在啓動的時候讀圖並訂閱zookeeper上/dubbo/com.foo.BarService/Providers節點下的全部節點,並解析全部提供者的URL地址做爲該服務類的地址列表,開始發起正常的調用。同時在Consumers節點下建立一個臨時節點,寫入本身的URL地址,表明本身是BarService的一個消費者
監控中心 : 監控中心是Dubbo服務治理體系的重要一部分,它須要知道一個服務的全部提供者和訂閱者及變化狀況。監控中心在啓動的時候會經過ZK的/dubbo/com.foo.BarService節點來獲取全部提供者和消費者的url地址,並註冊Watcher來監聽其子節點變化狀況。
全部服務提供者在ZK上建立的節點都是臨時節點,利用的是臨時節點的生命週期和客戶端會話綁定的特性,一旦提供者機器掛掉沒法對外提供服務時該臨時節點就會從ZK上摘除,這樣服務消費者和監控中心都能感知到服務提供者的變化。
命名服務也是分佈式系統中比較常見的一類場景,被命名的實體一般能夠是集羣中的機器、提供的服務地址或遠程對象,其中較爲常見的是一些分佈式服務框架中的服務地址列表,經過使用命名服務客戶端應用可以制定名字來獲取資源的實體、服務地址和提供者的信息等。
上層應用使用命名服務時可能僅須要一個全局惟一的名字,相似於數據庫中的惟一主鍵,用數據庫自增id是能夠的,但分庫分表的狀況下就沒法依靠數據庫的自增屬性來惟一標識一條記錄了。另外UUID也是一種普遍應用的ID實現方式,但若是是用UUID對服務進行命名的話就太不直觀了,從字面意思根本看不出其表達的含義。下面看下用ZK如何實現全局惟一ID的生成。
以前在ZNode介紹時提過,建立節點時能夠設定爲SEQUENTIAL順序節點,建立後API會返回這個節點的完整名字,利用這個特性咱們就能夠來生成全局惟一ID了。
全部客戶端根據本身的任務類型,在指定類型的任務下建立一個順序節點,例如「Job-」節點
節點建立完畢後會返回一個完整的節點名稱,如Job-0000000001
客戶端拿到這個返回值後拼接上type類型,例如type1-Job-000000001,這樣就能夠做爲一個全局惟一的ID了
在ZK中每一個數據節點都能維護一份子節點的順序序列,當客戶端對其建立一個順序子節點時ZK會自動之後綴的形式在其子節點上添加一個序號,該場景就利用了ZK的這個特性。
分佈式鎖用於控制分佈式系統之間同步訪問共享資源的一種方式,能夠保證不一樣系統訪問一個或一組資源時的一致性,主要分爲排它鎖和共享鎖。
排它鎖又稱爲寫鎖或獨佔鎖,若事務T1對數據對象O1加上了排它鎖,那麼在整個加鎖期間,只容許事務T1對O1進行讀取和更新操做,其餘任何事務都不能再對這個數據對象進行任何類型的操做,直到T1釋放了排它鎖。
若是不一樣系統或同一系統不一樣機器之間共享了同一資源,那訪問這些資源時一般須要一些互斥手段來保證一致性,這種狀況下就須要用到分佈式鎖了。
使用關係型數據庫是一種簡單、普遍的實現方案,但大多數大型分佈式系統中數據庫已是性能瓶頸了,若是再給數據庫添加額外的鎖會更加不堪重負;另外,使用數據庫作分佈式鎖,當搶到鎖的機器掛掉的話如何釋放鎖也是個頭疼的問題。
接下來看下使用ZK如何實現排他鎖。排他鎖的核心是如何保證當前有且只有一個事務得到鎖,而且鎖被釋放後全部等待獲取鎖的事務可以被通知到。
獲取鎖,在須要獲取排它鎖時,全部客戶端經過調用接口,在/exclusive_lock節點下建立臨時子節點/exclusive_lock/lock。Zookeeper能夠保證只有一個客戶端可以建立成功,沒有成功的客戶端須要註冊/exclusive_lock節點監聽。
釋放鎖,當獲取鎖的客戶端宕機或者正常完成業務邏輯都會致使臨時節點的刪除,此時,全部在/exclusive_lock節點上註冊監聽的客戶端都會收到通知,能夠從新發起分佈式鎖獲取。
共享鎖又稱爲讀鎖,若事務T1對數據對象O1加上共享鎖,那麼當前事務只能對O1進行讀取操做,其餘事務也只能對這個數據對象加共享鎖,直到該數據對象上的全部共享鎖都被釋放。
獲取鎖: 在須要獲取共享鎖時,全部客戶端都會到/shared_lock下面建立一個臨時順序節點,若是是讀請求,那麼就建立例如/shared_lock/host1-R-00000001的節點,若是是寫請求,那麼就建立例如/shared_lock/host2-W-00000002的節點。
判斷讀寫順序:不一樣事務能夠同時對一個數據對象進行讀寫操做,而更新操做必須在當前沒有任何事務進行讀寫狀況下進行,經過zookeeper來肯定分佈式讀寫順序,大體分爲四步。
建立完節點後,獲取/shared_lock節點下全部字節點,並對該節點變動註冊監聽。
肯定本身的節點序號在全部子節點中的順序
對於讀請求:若沒有比本身序號小的子節點或者全部比本身序號小的子節點都是讀請求,那麼代表本身已經成功獲取共享鎖,同時開始執行讀取邏輯,如有寫請求,則須要等待。對於寫請求:若本身不是序號最小的子節點,那麼須要等待。
接收到Watcher通知後,重複步驟1.
釋放鎖:其釋放鎖的流程與獨佔鎖的流程一致。