分佈式協調技術Zookeeper
2.1 zookeeper集羣安裝部署(略)node
2.2 zookeeper的基本原理,數據模型redis
2.3 zookeeper Java api的使用算法
2.4 zookeeper實際應用場景分析及實戰api
2.5 zookeeper+dubbo的實戰練習(略)服務器
zookeeper的基本原理,數據模型:數據結構
dubbo的服務註冊中心就是基於zookeeper。zookeeper是一種分佈式協調服務(能夠在分佈式系統中共享配置,協調鎖資源,提供命名服務)。異步
zookeeper的目標:封裝好複雜易出錯的關鍵職務,將簡單易用的接口和性能高效、功能穩定的系統提供給用戶;分佈式
zookeeper的特色:oop
- 最終一致性:爲客戶端展現同一視圖,這是Zookeeper最重要的性能;
- 可靠性:若是消息被一臺服務器接受,那麼它將被全部的服務器接受;
- 原子性:更新只能成功或失敗,沒有中間狀態;
zookeeper的數據結構:zookeeper的數據存儲是基於節點,叫Znode;它的引用方式是路徑引用,相似於文件路徑;Znode包含數據,子節點引用,訪問權限等;性能
data:
Znode存儲的數據信息。
ACL:
記錄Znode的訪問權限,即哪些人或哪些IP能夠訪問本節點。
stat:
包含Znode的各類元數據,好比事務ID、版本號、時間戳、大小等等。
child:
當前節點的子節點引用,相似於二叉樹的左孩子右孩子。
需注意:zookeeper是爲讀多寫少的場景所設計的,Znode並非用來存儲大規模業務數據的,而是用於存儲少許的狀態和配置信息的,每一個節點的數據最大不能超過1MB;
Znode分爲四種類型:
1.持久節點:默認的節點類型,建立節點的客戶端和zookeeper斷開鏈接後,該節點一直存在
2.持久節點順序節點:建立節點時,zookeeper根據建立的時間順序給該節點名稱進行編號
3.臨時節點:當建立節點的客戶端和zookeeper斷開鏈接後,臨時節點就會被刪除
4.臨時順序節點:根據建立時間排序的臨時節點
如何實現分佈式一致性:爲防止單機掛掉的狀況,Zookeeper維護了一個集羣,如圖所示:
zookeeper的集羣是一主多從的結構,在更新數據時,首先更新到主節點(主節點:服務器,不是Znode),再同步到從節點;在讀取數據時,直接讀取任意從節點
爲了保證主從節點的數據一致性,zookeeper採用了ZAB協議,這種協議很是相似於一致性算法Paxos和Raft
ZAB(Zookeeper Atomic Broadcast):有效解決Zookeeper集羣崩潰恢復,以及主從同步數據的問題;
ZAB協議是單調一致性,依靠事物ID和版本號,保證了數據的更新和讀取是有序的;
ZAB協議所定義的三種節點狀態:looking(選舉狀態),following(follower節點所處的狀態),leading(leader節點所處的狀態)
ZXID:節點本地的最新事物編號,包含epoch和計數兩部分;epoch是紀元的意思,至關於Raft算法選主時的term
------假如zookeeper當前的主節點掛掉了,集羣會進行崩潰恢復:
階段一:
leader election:選舉階段,此時集羣中的節點處於looking狀態,它們會各自向其餘節點發起投票,投票當中包含本身的服務器ID和最新事物ID(zxid)節點自身會與從其餘節點接收到的zxid作對比,對方大則對方數據更新,則從新發起投票,投票給目前已知最大的zxid所屬節點每次投票後,服務器都會統計投票數,判斷是否有某個節點獲得半數以上的投票,若是存在,該節點變爲leader,狀態變爲leading,其餘則爲following
階段二:
Discovery:發現階段,用於從節點中發現最新的zxid和事物日誌,爲防止某些意外狀況產生多個leader。若是出現兩個準leader,都會發出new epoch消息給各個Follower,必然有一個Leader的 epoch並非最新的。Follower發現接收的epoch沒有自身保存的epoch新,就會從新變爲Looking狀態,從新尋找真正的Leader。因此這一階段,leader接收全部來自follower發來的各自最新的epoch值,leader從中選出最大的epoch,基於此值加1,生成新的epoch分發給各個follower,各個follower接收到全新的epoch後,返回ACK給leader,帶上各自最大的zxid和歷史事物日誌。leader選出最大的zxid,並更新自身歷史日誌
階段三:
Synchronization:同步階段,把leader剛收集到的最新歷史事物日誌,同步給集羣中全部的follower,只有半數follower同步成功,這個準leader才能稱爲正式leader自此,故障恢復正式完成。
------ZAB實現寫入數據,涉及ZAB協議的Broadcast階段:Zookeeper常規狀況下更新數據的時候,由leader廣播到全部follower
-
- 客戶端發出寫入數據請求給任意follower
- follower把寫入數據請求轉法給leader
- leader採用二階段提交方式,先發送propose廣播給follower
- follower接到propose消息,寫入日誌成功後,返回ACK消息給leader
- leader接到半數以上ACK消息,返回成功給客戶端,而且廣播commit請求給follower
都有哪些應用場景:
-
- 分佈式鎖
- 服務註冊和發現:利用Znode和Watcher,能夠實現分佈式服務的註冊和發現,如dubbo
- 共享配置和狀態信息:redis的分佈式解決方案codis,利用了zookeeper來存放數據路由表和codis-proxy節點的元信息。同時codis-config發起的命令都會經過zookeeper同步到各個存活的codis-proxy
此外kafka,hbase,hadoop,也都依靠zookeeper同步節點信息,實現高可用
zookeeper Java api的使用
create:建立節點 ;
delete:刪除節點;
exists:判斷節點是否存在;
getData:得到一個節點的數據;
setData:設置一個節點的數據;
getChildren:獲取節點下的全部子節點
這其中,exists,getData,getChildren屬於讀操做。Zookeeper客戶端在請求讀操做的時候,能夠選擇是否設置Watch。
Watch是什麼意思呢?
咱們能夠理解成是註冊在特定Znode上的觸發器。當這個Znode發生改變,也就是調用了create,delete,setData方法的時候,將會觸發Znode上註冊的對應事件,請求Watch的客戶端會接收到異步通知。
具體交互過程以下:
1.客戶端調用getData方法,watch參數是true。服務端接到請求,返回節點數據,而且在對應的哈希表裏插入被Watch的Znode路徑,以及Watcher列表。
2.當被Watch的Znode已刪除,服務端會查找哈希表,找到該Znode對應的全部Watcher,異步通知客戶端,而且刪除哈希表中對應的Key-Value。
zookeeper實現分佈式鎖: (轉自:https://mp.weixin.qq.com/s/u8QDlrDj3Rl1YjY4TyKMCA)
zookeeper分佈式鎖應用了臨時順序節點,支持可重入
獲取鎖:首先在zookeeper中建立一個持久節點的ParentLock,當第一個客戶端想要得到鎖時,須要在ParentLock這個節點下建立一個臨時順序節點Lock1
以後,Client1查找ParentLock下面全部的臨時順序節點並排序,判斷本身所建立的節點Lock1是否是順序最靠前的一個。若是是第一個節點,則成功得到鎖。
這時候,若是再有一個客戶端 Client2 前來獲取鎖,則在ParentLock下載再建立一個臨時順序節點Lock2。
Client2查找ParentLock下面全部的臨時順序節點並排序,判斷本身所建立的節點Lock2是否是順序最靠前的一個,結果發現節點Lock2並非最小的。
因而,Client2向排序僅比它靠前的節點Lock1註冊Watcher,用於監聽Lock1節點是否存在。這意味着Client2搶鎖失敗,進入了等待狀態。
這時候,若是又有一個客戶端Client3前來獲取鎖,則在ParentLock下載再建立一個臨時順序節點Lock3。
Client3查找ParentLock下面全部的臨時順序節點並排序,判斷本身所建立的節點Lock3是否是順序最靠前的一個,結果一樣發現節點Lock3並非最小的。
因而,Client3向排序僅比它靠前的節點Lock2註冊Watcher,用於監聽Lock2節點是否存在。這意味着Client3一樣搶鎖失敗,進入了等待狀態。
這樣一來,Client1獲得了鎖,Client2監聽了Lock1,Client3監聽了Lock2。這偏偏造成了一個等待隊列,很像是Java當中ReentrantLock所依賴的AQS(AbstractQueuedSynchronizer)。
釋放鎖:
1.任務完成,客戶端顯示釋放:當任務完成時,Client1會顯示調用刪除節點Lock1的指令。
2.任務執行過程當中,客戶端崩潰:得到鎖的Client1在任務執行過程當中,若是Duang的一聲崩潰,則會斷開與Zookeeper服務端的連接。根據臨時節點的特性,相關聯的節點Lock1會隨之自動刪除。因爲Client2一直監聽着Lock1的存在狀態,當Lock1節點被刪除,Client2會馬上收到通知。這時候Client2會再次查詢ParentLock下面的全部節點,確認本身建立的節點Lock2是否是目前最小的節點。若是是最小,則Client2瓜熟蒂落得到了鎖。