Zookeeper做爲一個分佈式協調系統提供了一項基本服務:分佈式鎖服務,分佈式鎖是分佈式協調技術實現的核心內容。像配置管理、任務分發、組服務、分佈式消息隊列、分佈式通知/協調等,這些應用實際上都是基於這項基礎服務由用戶本身摸索出來的。node
zookeeper做爲分佈式協調系統在大數據領域很是經常使用,它是一個很好的中心化管理工具。下面舉幾個常見的應用場景。算法
HA(分佈式鎖的應用):Master掛掉以後迅速切換到slave節點。數據庫
任務發佈:regionserver掛了一臺,master須要從新分配region,會把任務放在zookeeper等regionserver來獲取服務器
任務分配:給topic分配partitions和replication網絡
ZooKeeper命名空間中的Znode,兼具文件和目錄兩種特色。既像文件同樣維護着數據、元信息、ACL、時間戳等數據結構,又像目錄同樣能夠做爲路徑標識的一部分。 每一個Znode由3部分組成:數據結構
ZooKeeper中的每一個節點存儲的數據要被原子性的操做。也就是說讀操做將獲取與節點相關的全部數據,寫操做也將替換掉節點的全部數據。另外,每個節點都擁有本身的ACL(訪問控制列表),這個列表規定了用戶的權限,即限定了特定用戶對目標節點能夠執行的操做。架構
ZooKeeper能夠爲全部的讀操做設置watch,包括:exists()、getChildren()及getData()。當節點狀態發生改變時(Znode的增、刪、改)將會觸發watch所對應的操做。當watch被觸發時,ZooKeeper將會向客戶端發送且僅發送一條通知,由於watch只能被觸發一次,這樣能夠減小網絡流量。分佈式
孩子watch(child watches):getChildren負責設置孩子watch工具
永久節點:該節點的生命週期不依賴於會話,而且只有在客戶端顯示執行刪除操做的時候,他們才能被刪除。性能
兩種方式:
存儲集羣元數據提供給client使用,體如今好比須要對HBase和Kafka操做時,都會直接連到zookeeper,zookeeper記錄了數據存儲的位置,存活的節點等元數據信息。
Master要監視/works和/tasks兩個永久節點,以便能感知到由哪些slave當前可用,當前有新任務須要分配。
分配過程:在/assign下建立當前可用的workA,找到須要分配的taskA,建立/assign/workA/taskA
zookeeper做爲一個分佈式協調系統,不少組件都會依賴它,那麼此時它的可用性就很是重要了,那麼保證可用性的同時做爲分佈式系統的它是怎麼保證擴展性的?問題不少,讀完接下來的內容你會有答案。
上圖來自zookeeper的官方文檔,我解釋下這張圖的各個角色(observer在上圖中能夠理解爲特殊的follower)
角色 | 分工 | 數量 |
---|---|---|
client客戶端 | 請求發起方 | 不限 |
observer觀察者 | 接受用戶讀寫請求,寫轉發給leader,讀直接返回(選主過程不參加投票) | 不限 |
follower跟隨者 | 接受用戶讀寫請求,寫轉發給leader,讀直接返回(選主過程參加投票) | 奇數個(不可過多) |
leader領導者 | 負責提議,更新系統狀態 | 1個 |
另外:follower和observer同時均爲learner(學習者)角色,learner的分工是同步leader的狀態。
zookeeper的各個複製集節點(follower,leader,observer)都包含了集羣全部的數據且存在內存中,像個內存數據庫。更新操做會以日誌的形式記錄到磁盤以保證可恢復性,而且寫入操做會在寫入內存數據庫以前序列化到磁盤。
每一個ZooKeeper服務器都爲客戶端服務。客戶端只鏈接到一臺服務器以提交請求。讀取請求由每一個服務器數據庫的本地副本提供服務。更改服務狀態,寫請求的請求由zab協議處理。
做爲協議協議的一部分,來自客戶端的全部寫入請求都被轉發到稱爲leader的單個服務器。其他的ZooKeeper服務器(稱爲followers)接收來自領導者leader的消息提議並贊成消息傳遞。消息傳遞層負責替換失敗的leader並將followers與leader同步。
ZooKeeper使用自定義原子消息傳遞協議zab。因爲消息傳遞層是原子的,當領導者收到寫入請求時,它會計算應用寫入時系統的狀態,並將其轉換爲捕獲此新狀態的事務。
cap原則是指做爲一個分佈式系統,一致性,可用性,分區容錯性這三個方面,最多隻能任意選擇兩種。就是一定會要有取捨。
Zookeeper是強一致性系統,同步數據很快。可是在不用sync()操做的前提下沒法保證各節點的數據徹底一致。zookeeper爲了保證一致性使用了基於paxos協議且爲zookeeper量身定作的zab協議。這兩個協議是什麼東西以後的文章會講。
Zookeeper數據存儲在內存中,且各個節點均可以相應讀請求,具備好的響應性能。Zookeeper保證了可用性,數據老是可用的,沒有鎖.而且有一大半的節點所擁有的數據是最新的,實時的。
有2點須要分析的
嚴格地意義來說zk把取捨這個問題拋給了開發者即用戶。
爲了協調CA(一致性和可用性),用戶能夠本身選擇是否使用Sync()操做。使用則保證全部節點強一致,可是這個操做同步數據會有必定的延遲時間。反過來若不是必須保證強一致性的場景,可不使用sync,雖然zookeeper同步的數據很快,可是此時是沒有辦法保證各個節點的數據必定是一致的,這一點用戶要注意。實際的開發中就要開發者根據實際場景來作取捨了,看更關注一致性仍是可用性。
爲了協調AP(一致性和擴展性),用戶能夠本身選擇是否添加obsever以及添加個數,observer是3.3.0 之後版本新增角色,它不會參加選舉和投票過程,目的就是提升集羣擴展性。由於follower的數量不能過多,follower須要參加選舉和投票,過多的話選舉的收斂速度會很是慢,寫數據時的投票過程也會好久。observer的增長能夠提升可用性和擴展性,集羣可接受client請求的點多了,可用性天然會提升,可是一致性的問題依然存在,這時又回到了上面CA的取捨問題上。
FastLeaderElection原理
每一個Zookeeper服務器,都須要在數據文件夾下建立一個名爲myid的文件,該文件包含整個Zookeeper集羣惟一的ID(整數)。例如某Zookeeper集羣包含三臺服務器,hostname分別爲zoo一、zoo2和zoo3,其myid分別爲一、2和3,則在配置文件中其ID與hostname必須一一對應,以下所示。在該配置文件中,server.後面的數據即爲myid
server.1=zoo1:2888:3888 server.2=zoo2:2888:3888 server.3=zoo3:2888:3888
相似於RDBMS中的事務ID,用於標識一次更新操做的Proposal ID。爲了保證順序性,該zkid必須單調遞增。所以Zookeeper使用一個64位的數來表示,高32位是Leader的epoch,從1開始,每次選出新的Leader,epoch加一。低32位爲該epoch內的序號,每次epoch變化,都將低32位的序號重置。這樣保證了zkid的全局遞增性。
可經過electionAlg配置項設置Zookeeper用於領導選舉的算法。
到3.4.10版本爲止,可選項有
0 基於UDP的LeaderElection 1 基於UDP的FastLeaderElection 2 基於UDP和認證的FastLeaderElection 3 基於TCP的FastLeaderElection
在3.4.10版本中,默認值爲3,也即基於TCP的FastLeaderElection。另外三種算法已經被棄用,而且有計劃在以後的版本中將它們完全刪除而再也不支持。
FastLeaderElection選舉算法是標準的Fast Paxos算法實現,可解決LeaderElection選舉算法收斂速度慢的問題。
服務器狀態
OBSERVING 觀察者狀態。代表當前服務器角色是Observer,與Folower惟一的不一樣在於不參與選舉,也不參與集羣寫操做時的投票
vote_zxid 被推舉的服務器上所保存的數據的最大zxid
自增選舉輪次
Zookeeper規定全部有效的投票都必須在同一輪次中。每一個服務器在開始新一輪投票時,會先對本身維護的logicClock進行自增操做。
每一個服務器在廣播本身的選票前,會將本身的投票箱清空。該投票箱記錄了所收到的選票。例:服務器2投票給服務器3,服務器3投票給服務器1,則服務器1的投票箱爲(2, 3), (3, 1), (1, 1)。票箱中只會記錄每一投票者的最後一票,如投票者更新本身的選票,則其它服務器收到該新選票後會在本身票箱中更新該服務器的選票。
每一個服務器最開始都是經過廣播把票投給本身。
服務器會嘗試從其它服務器獲取投票,並記入本身的投票箱內。若是沒法獲取任何外部投票,則會確認本身是否與集羣中其它服務器保持着有效鏈接。若是是,則再次發送本身的投票;若是否,則立刻與之創建鏈接。
收到外部投票後,首先會根據投票信息中所包含的logicClock來進行不一樣處理
外部投票的logicClock大於本身的logicClock。說明該服務器的選舉輪次落後於其它服務器的選舉輪次,當即清空本身的投票箱並將本身的logicClock更新爲收到的logicClock,而後再對比本身以前的投票與收到的投票以肯定是否須要變動本身的投票,最終再次將本身的投票廣播出去。
外部投票的logicClock小於本身的logicClock。當前服務器直接忽略該投票,繼續處理下一個投票。
外部投票的logickClock與本身的相等。當時進行選票PK。
選票PK是基於(self_id, self_zxid)與(vote_id, vote_zxid)的對比
外部投票的logicClock大於本身的logicClock,則將本身的logicClock及本身的選票的logicClock變動爲收到的logicClock
若logicClock一致,則對比兩者的vote_zxid,若外部投票的vote_zxid比較大,則將本身的票中的vote_zxid與vote_myid更新爲收到的票中的vote_zxid與vote_myid並廣播出去,另外將收到的票及本身更新後的票放入本身的票箱。若是票箱內已存在(self_myid, self_zxid)相同的選票,則直接覆蓋
若兩者vote_zxid一致,則比較兩者的vote_myid,若外部投票的vote_myid比較大,則將本身的票中的vote_myid更新爲收到的票中的vote_myid並廣播出去,另外將收到的票及本身更新後的票放入本身的票箱
若是已經肯定有過半服務器承認了本身的投票(多是更新後的投票),則終止投票。不然繼續接收其它服務器的投票。
投票終止後,服務器開始更新自身狀態。若過半的票投給了本身,則將本身的服務器狀態更新爲LEADING,不然將本身的狀態更新爲FOLLOWING
《每日五分鐘搞定大數據》原創系列,每週不定時更新。評論不能及時回覆可直接加公衆號提問或交流,知無不答,謝謝 。