場景一
有這樣一個場景:系統(tǒng)中有大約100w的用戶,每個用戶平 均有3個郵箱賬號,每隔5分鐘,每個郵箱賬需要收取100封郵件,最多3億份郵件需要下載到服務(wù)器中(不含附件和正文)。用20臺機器劃分計算的壓力,從 多個不同的網(wǎng)路出口進行訪問外網(wǎng),計算的壓力得到緩解,那么每臺機器的計算壓力也不會很大了。
通過我們的討論和以往的經(jīng)驗判斷在這場景中可以實現(xiàn)并行計算,但我們還期望能對并行計算的節(jié)點進行動態(tài)的添加/刪除,做到在線更新并行計算的數(shù)目并且不會影響計算單元中的其他計算節(jié)點,但是有4個問題需要解決,否則會出現(xiàn)一些嚴重的問題:
1、20臺機器同時工作時,有一臺機器down掉了,其他機器怎么進行接管計算任務(wù),否則有些用戶的業(yè)務(wù)不會被處理,造成用戶服務(wù)終斷。
2、隨著用戶數(shù)量增加,添加機器是可以解決計算的瓶頸,但需要重啟所有計算節(jié)點,如果需要,那么將會造成整個系統(tǒng)的不可用。
3、用戶數(shù)量增加或者減少,計算節(jié)點中的機器會出現(xiàn)有的機器資源使用率繁忙,有的卻空閑,因為計算節(jié)點不知道彼此的運行負載狀態(tài)。
4、怎么去通知每個節(jié)點彼此的負載狀態(tài),怎么保證通知每個計算節(jié)點方式的可靠性和實時性。
先不說那么多專業(yè)名詞,白話來說我們需要的是:1記錄狀態(tài),2事件通知 ,3可靠穩(wěn)定的中央調(diào)度器,4易上手、管理簡單。
采用Zookeeper完全可以解決我們的問題,分布式計算中的協(xié)調(diào)員,觀察者,分布式鎖 都可以作為zookeeper的關(guān)鍵詞,在系統(tǒng)中利用Zookeeper來處理事件通知,隊列,優(yōu)先隊列,鎖,共享鎖等功能,利用這些特色在分布式計算中發(fā)揮重要的作用。
場景二
假設(shè)我們我們有個20個搜索引擎的服務(wù)器(每個負責總索引中的一部分的搜索任務(wù))和一個總服務(wù)器(負責向這20個搜索引擎的服務(wù)器發(fā)出搜索請求并合并 結(jié)果集),一個備用的總服務(wù)器(負責當總服務(wù)器宕機時替換總服務(wù)器),一個web的cgi(向總服務(wù)器發(fā)出搜索請求).搜索引擎的服務(wù)器中的15個服務(wù)器現(xiàn)在提供搜索服務(wù),5個服務(wù)器正在生成索引.這20個搜索引擎的服務(wù)器經(jīng)常要讓正在 提供搜索服務(wù)的服務(wù)器停止提供服務(wù)開始生成索引,或生成索引的服務(wù)器已經(jīng)把索引生成完成可以搜索提供服務(wù)了。使用Zookeeper可以保證總服務(wù)器自動感知有多少提供搜索引擎的服務(wù)器并向這些服務(wù)器發(fā)出搜索請求,備用的總服務(wù)器宕機時自動啟用備用的總服務(wù)器,web的cgi能夠自動地獲知總服務(wù)器的網(wǎng)絡(luò)地址變化。這些又如何做到呢?
1、提供搜索引擎的服務(wù)器都在Zookeeper中創(chuàng)建znode,zk.create("/search/nodes/node1",
"hostname".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateFlags.EPHEMERAL);
2、總服務(wù)器可以從Zookeeper中獲取一個znode的子節(jié)點的列表,zk.getChildren("/search/nodes", true);
3、總服務(wù)器遍歷這些子節(jié)點,并獲取子節(jié)點的數(shù)據(jù)生成提供搜索引擎的服務(wù)器列表.
4、當總服務(wù)器接收到子節(jié)點改變的事件信息,重新返回第二步.
5、總服務(wù)器在Zookeeper中創(chuàng)建節(jié)點,zk.create("/search/master", "hostname".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateFlags.EPHEMERAL);
6、備用的總服務(wù)器監(jiān)控Zookeeper中的"/search/master"節(jié)點.當這個znode的節(jié)點數(shù)據(jù)改變時,把自己啟動變成總服務(wù)器,并把自己的網(wǎng)絡(luò)地址數(shù)據(jù)放進這個節(jié)點.
7、web的cgi從Zookeeper中"/search/master"節(jié)點獲取總服務(wù)器的網(wǎng)絡(luò)地址數(shù)據(jù)并向其發(fā)送搜索請求.
8、web的cgi監(jiān)控Zookeeper中的"/search/master"節(jié)點,當這個znode的節(jié)點數(shù)據(jù)改變時,從這個節(jié)點獲取總服務(wù)器的網(wǎng)絡(luò)地址數(shù)據(jù),并改變當前的總服務(wù)器的網(wǎng)絡(luò)地址。
在我的測試中:一個Zookeeper的集群中,3個Zookeeper節(jié)點.一個leader,兩個follower的情況下,停掉leader,然后兩個follower選舉出一個leader.獲取的數(shù)據(jù)不變.我想Zookeeper能夠幫助Hadoop做到:
Hadoop,使用Zookeeper的事件處理確保整個集群只有一個NameNode,存儲配置信息等。
HBase,使用Zookeeper的事件處理確保整個集群只有一個HMaster,察覺HRegionServer聯(lián)機和宕機,存儲訪問控制列表等.
zookeeper是什么
官方說辭:Zookeeper 分布式服務(wù)框架是Apache Hadoop 的一個子項目,它主要是用來解決分布式應(yīng)用中經(jīng)常遇到的一些數(shù)據(jù)管理問題,如:統(tǒng)一命名服務(wù)、狀態(tài)同步服務(wù)、集群管理、分布式應(yīng)用配置項的管理等。
好抽象,我們改變一下方式,先看看它都提供了哪些功能,然后再看看使用它的這些功能能做點什么。
zookeeper提供了什么
1、ZNode
這個應(yīng)該算是Zookeeper中的基礎(chǔ),數(shù)據(jù)存儲的最小單元。在Zookeeper中,類似文件系統(tǒng)的存儲結(jié)構(gòu),被Zookeeper抽象成了樹,樹中的每一個節(jié)點(Node)被叫做ZNode。ZNode中維護了一個數(shù)據(jù)結(jié)構(gòu),用于記錄ZNode中數(shù)據(jù)更改的版本號以及ACL(Access Control List)的變更。
有了這些數(shù)據(jù)的版本號以及其更新的Timestamp,Zookeeper就可以驗證客戶端請求的緩存是否合法,并協(xié)調(diào)更新。
而且,當Zookeeper的客戶端執(zhí)行更新或者刪除操作時,都必須要帶上要修改的對應(yīng)數(shù)據(jù)的版本號。如果Zookeeper檢測到對應(yīng)的版本號不存在,則不會執(zhí)行這次更新。如果合法,在ZNode中數(shù)據(jù)更新之后,其對應(yīng)的版本號也會一起更新。
這套版本號的邏輯,其實很多框架都在用,例如RocketMQ中,Broker向NameServer注冊的時候,也會帶上這樣一個版本號,叫DateVersion。
接下來我們來詳細看一下這個維護版本號相關(guān)數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu),它叫Stat Structure,其字段有:
舉個例子,通過stat命令,我們可以查看某個ZNode中Stat Structure具體的值。
關(guān)于這里的epoch、zxid是Zookeeper集群相關(guān)的東西。
2、ACL
ACL(Access Control List)用于控制ZNode的相關(guān)權(quán)限,其權(quán)限控制和Linux中的類似。Linux中權(quán)限種類分為了三種,分別是讀、寫、執(zhí)行,分別對應(yīng)的字母是r、w、x。其權(quán)限粒度也分為三種,分別是擁有者權(quán)限、群組權(quán)限、其他組權(quán)限,舉個例子:
drwxr-xr-x3USERNAMEGROUP1.0K31518:19dir_name
什么叫粒度?粒度是對權(quán)限所作用的對象的分類,把上面三種粒度換個說法描述就是對用戶(Owner)、用戶所屬的組(Group)、其他組(Other)的權(quán)限劃分,這應(yīng)該算是一種權(quán)限控制的標準了,典型的三段式。
Zookeeper中雖然也是三段式,但是兩者對粒度的劃分存在區(qū)別。Zookeeper中的三段式為Scheme、ID、Permissions,含義分別為權(quán)限機制、允許訪問的用戶和具體的權(quán)限。
Scheme代表了一種權(quán)限模式,有以下5種類型:
- world:在此中Scheme下,ID只能是anyone,代表所有人都可以訪問
- auth:代表已經(jīng)通過了認證的用戶
- digest:使用用戶名+密碼來做校驗。
- ip:只允許某些特定的IP訪問ZNode
- X509:通過客戶端的證書進行認證
同時權(quán)限種類也有五種:
- CREATE:創(chuàng)建節(jié)點
- READ:獲取節(jié)點或列出其子節(jié)點
- WRITE:能設(shè)置節(jié)點的數(shù)據(jù)
- DELETE:能夠刪除子節(jié)點
- ADMIN:能夠設(shè)置權(quán)限
同Linux中一樣,這個權(quán)限也有縮寫,舉個例子:
getAcl方法用戶查看對應(yīng)的ZNode的權(quán)限,如圖,我們可以輸出的結(jié)果呈三段式。分別是:
- scheme:使用了world
- id:值為anyone,代表所有用戶都有權(quán)限
- permissions:其具體的權(quán)限為cdrwa,分別是CREATE、DELETE、READ、WRITE和ADMIN的縮寫
3、文件系統(tǒng)
Zookeeper維護一個類似文件系統(tǒng)的數(shù)據(jù)結(jié)構(gòu):
每個子目錄項如 NameService 都被稱作為 znode,和文件系統(tǒng)一樣,我們能夠自由的增加、刪除znode,在一個znode下增加、刪除子znode,唯一的不同在于znode是可以存儲數(shù)據(jù)的。
有四種類型的znode:
1、PERSISTENT-持久化目錄節(jié)點
客戶端與zookeeper斷開連接后,該節(jié)點依舊存在
2、 PERSISTENT_SEQUENTIAL-持久化順序編號目錄節(jié)點
客戶端與zookeeper斷開連接后,該節(jié)點依舊存在,只是Zookeeper給該節(jié)點名稱進行順序編號
3、EPHEMERAL-臨時目錄節(jié)點
客戶端與zookeeper斷開連接后,該節(jié)點被刪除
4、EPHEMERAL_SEQUENTIAL-臨時順序編號目錄節(jié)點
客戶端與zookeeper斷開連接后,該節(jié)點被刪除,只是Zookeeper給該節(jié)點名稱進行順序編號
4、通知機制(事件機制:watcher特性)
watcher特性:客戶端注冊監(jiān)聽它關(guān)心的目錄節(jié)點,當目錄節(jié)點發(fā)生變化(數(shù)據(jù)改變、被刪除、子目錄節(jié)點增加刪除)時,zookeeper會產(chǎn)生一個watcher事件,并且會通知到注冊監(jiān)聽的客戶端,但是客戶端只會收到一次通知。如果后續(xù)這個節(jié)點在發(fā)生變化,那么之前設(shè)置watcher的客戶端不會再次收到消息(watcher是一次性的操作)。可以通過循環(huán)監(jiān)聽去達到永久監(jiān)聽效果。
如何注冊事件機制
通過這三個操作來綁定事件:getData、Exists、getChildren
如何觸發(fā)事件?
凡是事物類型的操作,都會觸發(fā)監(jiān)聽事件。如:create、delete、setData
watcher事件類型
None(-1), 客戶端連接狀態(tài)發(fā)生變化的時候,會收到None的事件
NodeCreated(1), 創(chuàng)建節(jié)點的事件
NodeDeleted(2), 刪除節(jié)點的事件
NodeDataChanged(3), 節(jié)點數(shù)據(jù)發(fā)生變更
NodeChildrenChanged(4); 子節(jié)點被創(chuàng)建、被刪除、被更改,會觸發(fā)的事件
什么樣的操作會產(chǎn)生什么類型的事件呢?
/zk-persis-yibo(監(jiān)聽事件) | /zk-persis-yibo/child(監(jiān)聽事件) | |
---|---|---|
create(/zk-persis-yibo) | NodeCreated(getData、Exists) | 無 |
delete(/zk-persis-yibo) | NodeDeleted(getData、Exists) | 無 |
setData(/zk-persis-yibo) | NodeDataChanged(getData、Exists) | 無 |
create(/zk-persis-yibo/children) | NodeChildrenChanged(getChildren) | NodeCreated |
delete(/zk-persis-yibo/children) | NodeChildrenChanged(getChildren) | NodeDeleted |
setData(/zk-persis-yibo/children) | NodeDataChanged |
事件的實現(xiàn)原理:
和ZNode中有多種類型一樣,Watch也有多種類型,分別是一次性Watch和永久性Watch。
- 一次性Watch 在被觸發(fā)之后,該Watch就會移除
- 永久性Watch 在被觸發(fā)之后,仍然保留,可以繼續(xù)監(jiān)聽ZNode上的變更,是Zookeeper 3.6.0版本新增的功能
一次性的Watch可以在調(diào)用getData()、getChildren()和exists()等方法時在參數(shù)中進行設(shè)置,永久性的Watch則需要調(diào)用addWatch()來實現(xiàn)。
并且一次性的Watch會存在問題,因為在Watch觸發(fā)的事件到達客戶端、再到客戶端設(shè)立新的Watch,是有一個時間間隔的。而如果在這個時間間隔中發(fā)生的變更,客戶端則無法感知。
簡單的說,zookeeper=文件系統(tǒng)+通知機制。
事件的實現(xiàn)原理詳解:
zookeeper 事件監(jiān)聽機制
Zookeeper的Watcher 機制的實現(xiàn)原理
zookeeper(四):核心原理(Watcher、事件和狀態(tài))
就這么簡單,下面我們看看能做點什么呢?
我們能用zookeeper做什么
1、 命名服務(wù)
這個似乎最簡單,在zookeeper的文件系統(tǒng)里創(chuàng)建一個目錄,即有唯一的path。在我們使用tborg無法確定上游程序的部署機器時即可與下游程序約定好path,通過path即能互相探索發(fā)現(xiàn),不見不散了。
2、 配置管理
程序總是需要配置的,如果程序分散部署在多臺機器上,要逐個改變配置就變得困難。好吧,現(xiàn)在把這些配置全部放到zookeeper上去,保存在 Zookeeper 的某個目錄節(jié)點中,然后所有相關(guān)應(yīng)用程序?qū)@個目錄節(jié)點進行監(jiān)聽,一旦配置信息發(fā)生變化,每個應(yīng)用程序就會收到 Zookeeper 的通知,然后從 Zookeeper 獲取新的配置信息應(yīng)用到系統(tǒng)中就好。
3、 集群管理
所謂集群管理無在乎兩點:是否有機器退出和加入、選舉master。
對于第一點,所有機器約定在父目錄GroupMembers下創(chuàng)建臨時目錄節(jié)點,然后監(jiān)聽父目錄節(jié)點的子節(jié)點變化消息。一旦有機器掛掉,該機器與 zookeeper的連接斷開,其所創(chuàng)建的臨時目錄節(jié)點被刪除,所有其他機器都收到通知:某個兄弟目錄被刪除,于是,所有人都知道:它上船了。新機器加入 也是類似,所有機器收到通知:新兄弟目錄加入,highcount又有了。
對于第二點,我們稍微改變一下,所有機器創(chuàng)建臨時順序編號目錄節(jié)點,每次選取編號最小的機器作為master就好。
4、 分布式鎖
有了zookeeper的一致性文件系統(tǒng),鎖的問題變得容易。鎖服務(wù)可以分為兩類,一個是保持獨占,另一個是控制時序。
對于第一類,我們將zookeeper上的一個znode看作是一把鎖,通過createznode的方式來實現(xiàn)。所有客戶端都去創(chuàng)建 /distribute_lock 節(jié)點,最終成功創(chuàng)建的那個客戶端也即擁有了這把鎖。廁所有言:來也沖沖,去也沖沖,用完刪除掉自己創(chuàng)建的distribute_lock 節(jié)點就釋放出鎖。
對于第二類, /distribute_lock 已經(jīng)預(yù)先存在,所有客戶端在它下面創(chuàng)建臨時順序編號目錄節(jié)點,和選master一樣,編號最小的獲得鎖,用完刪除,依次方便。
5、隊列管理
兩種類型的隊列:
1、 同步隊列,當一個隊列的成員都聚齊時,這個隊列才可用,否則一直等待所有成員到達。
2、隊列按照 FIFO 方式進行入隊和出隊操作。
第一類,在約定目錄下創(chuàng)建臨時目錄節(jié)點,監(jiān)聽節(jié)點數(shù)目是否是我們要求的數(shù)目。
第二類,和分布式鎖服務(wù)中的控制時序場景基本原理一致,入列有編號,出列按編號。
終于了解完我們能用zookeeper做什么了,可是作為一個程序員,我們總是想狂熱了解zookeeper是如何做到這一點的,單點維護一個文件系統(tǒng)沒有什么難度,可是如果是一個集群維護一個文件系統(tǒng)保持數(shù)據(jù)的一致性就非常困難了。
分布式與數(shù)據(jù)復(fù)制
Zookeeper作為一個集群提供一致的數(shù)據(jù)服務(wù),自然,它要在所有機器間做數(shù)據(jù)復(fù)制。數(shù)據(jù)復(fù)制的好處:
1、 容錯
一個節(jié)點出錯,不致于讓整個系統(tǒng)停止工作,別的節(jié)點可以接管它的工作;
2、提高系統(tǒng)的擴展能力
把負載分布到多個節(jié)點上,或者增加節(jié)點來提高系統(tǒng)的負載能力;
3、提高性能
讓客戶端本地訪問就近的節(jié)點,提高用戶訪問速度。
從客戶端讀寫訪問的透明度來看,數(shù)據(jù)復(fù)制集群系統(tǒng)分下面兩種:
1、寫主(WriteMaster)
對數(shù)據(jù)的修改提交給指定的節(jié)點。讀無此限制,可以讀取任何一個節(jié)點。這種情況下客戶端需要對讀與寫進行區(qū)別,俗稱讀寫分離;
2、寫任意(Write Any)
對數(shù)據(jù)的修改可提交給任意的節(jié)點,跟讀一樣。這種情況下,客戶端對集群節(jié)點的角色與變化透明。
對zookeeper來說,它采用的方式是寫任意。通過增加機器,它的讀吞吐能力和響應(yīng)能力擴展性非常好,而寫,隨著機器的增多吞吐能力肯定下降(這 也是它建立observer的原因),而響應(yīng)能力則取決于具體實現(xiàn)方式,是延遲復(fù)制保持最終一致性,還是立即復(fù)制快速響應(yīng)。
我們關(guān)注的重點還是在如何保證數(shù)據(jù)在集群所有機器的一致性,這就涉及到paxos算法。
數(shù)據(jù)一致性與paxos算法
據(jù)說Paxos算法的難理解與算法的知名度一樣令人敬仰,所以我們先看如何保持數(shù)據(jù)的一致性,這里有個原則就是:
在一個分布式數(shù)據(jù)庫系統(tǒng)中,如果各節(jié)點的初始狀態(tài)一致,每個節(jié)點都執(zhí)行相同的操作序列,那么他們最后能得到一個一致的狀態(tài)。
Paxos算法解決的什么問題呢,解決的就是保證每個節(jié)點執(zhí)行相同的操作序列。好吧,這還不簡單,master維護一個全局寫隊列,所有寫操作都必須 放入這個隊列編號,那么無論我們寫多少個節(jié)點,只要寫操作是按編號來的,就能保證一致性。沒錯,就是這樣,可是如果master掛了呢。
Paxos算法通過投票來對寫操作進行全局編號,同一時刻,只有一個寫操作被批準,同時并發(fā)的寫操作要去爭取選票,只有獲得過半數(shù)選票的寫操作才會被 批準(所以永遠只會有一個寫操作得到批準),其他的寫操作競爭失敗只好再發(fā)起一輪投票,就這樣,在日復(fù)一日年復(fù)一年的投票中,所有寫操作都被嚴格編號排 序。編號嚴格遞增,當一個節(jié)點接受了一個編號為100的寫操作,之后又接受到編號為99的寫操作(因為網(wǎng)絡(luò)延遲等很多不可預(yù)見原因),它馬上能意識到自己 數(shù)據(jù)不一致了,自動停止對外服務(wù)并重啟同步過程。任何一個節(jié)點掛掉都不會影響整個集群的數(shù)據(jù)一致性(總2n+1臺,除非掛掉大于n臺)。
總結(jié):
Zookeeper 作為 Hadoop 項目中的一個子項目,是 Hadoop 集群管理的一個必不可少的模塊,它主要用來控制集群中的數(shù)據(jù),如它管理 Hadoop 集群中的 NameNode,還有 Hbase 中 Master Election、Server 之間狀態(tài)同步等。
ZooKeeper是一個分布式的,開放源碼的分布式應(yīng)用程序協(xié)調(diào)服務(wù),它包含一個簡單的原語集,分布式應(yīng)用程序可以基于它實現(xiàn)同步服務(wù),配置維護和 命名服務(wù)等。Zookeeper是hadoop的一個子項目,其發(fā)展歷程無需贅述。在分布式應(yīng)用中,由于工程師不能很好地使用鎖機制,以及基于消息的協(xié)調(diào) 機制不適合在某些應(yīng)用中使用,因此需要有一種可靠的、可擴展的、分布式的、可配置的協(xié)調(diào)機制來統(tǒng)一系統(tǒng)的狀態(tài)。Zookeeper的目的就在于此。本文簡 單分析zookeeper的工作原理,對于如何使用zookeeper不是本文討論的重點。
Zookeeper的基本概念
1.1 角色
Zookeeper中的角色主要有以下三類,如下表所示:
系統(tǒng)模型如圖所示:
1.2 設(shè)計目的
1.最終一致性:client不論連接到哪個Server,展示給它都是同一個視圖,這是zookeeper最重要的性能。
2 .可靠性:具有簡單、健壯、良好的性能,如果消息m被到一臺服務(wù)器接受,那么它將被所有的服務(wù)器接受。
3 .實時性:Zookeeper保證客戶端將在一個時間間隔范圍內(nèi)獲得服務(wù)器的更新信息,或者服務(wù)器失效的信息。但由于網(wǎng)絡(luò)延時等原因,Zookeeper不能保證兩個客戶端能同時得到剛更新的數(shù)據(jù),如果需要最新數(shù)據(jù),應(yīng)該在讀數(shù)據(jù)之前調(diào)用sync()接口。
4 .等待無關(guān)(wait-free):慢的或者失效的client不得干預(yù)快速的client的請求,使得每個client都能有效的等待。
5.原子性:更新只能成功或者失敗,沒有中間狀態(tài)。
6 .順序性:包括全局有序和偏序兩種:全局有序是指如果在一臺服務(wù)器上消息a在消息b前發(fā)布,則在所有Server上消息a都將在消息b前被發(fā)布;偏序是指如果一個消息b在消息a后被同一個發(fā)送者發(fā)布,a必將排在b前面。
ZooKeeper的工作原理
Zookeeper的核心是原子廣播,這個機制保證了各個Server之間的同步。實現(xiàn)這個機制的協(xié)議叫做Zab協(xié)議。Zab協(xié)議有兩種模式,它們分 別是恢復(fù)模式(選主)和廣播模式(同步)。當服務(wù)啟動或者在領(lǐng)導者崩潰后,Zab就進入了恢復(fù)模式,當領(lǐng)導者被選舉出來,且大多數(shù)Server完成了和 leader的狀態(tài)同步以后,恢復(fù)模式就結(jié)束了。狀態(tài)同步保證了leader和Server具有相同的系統(tǒng)狀態(tài)。
為了保證事務(wù)的順序一致性,zookeeper采用了遞增的事務(wù)id號(zxid)來標識事務(wù)。所有的提議(proposal)都在被提出的時候加上 了zxid。實現(xiàn)中zxid是一個64位的數(shù)字,它高32位是epoch用來標識leader關(guān)系是否改變,每次一個leader被選出來,它都會有一個 新的epoch,標識當前屬于那個leader的統(tǒng)治時期。低32位用于遞增計數(shù)。
每個Server在工作過程中有三種狀態(tài):
- LOOKING:當前Server不知道leader是誰,正在搜尋
- LEADING:當前Server即為選舉出來的leader
- FOLLOWING:leader已經(jīng)選舉出來,當前Server與之同步
2.1 選主流程
當leader崩潰或者leader失去大多數(shù)的follower,這時候zk進入恢復(fù)模式,恢復(fù)模式需要重新選舉出一個新的leader,讓所有的 Server都恢復(fù)到一個正確的狀態(tài)。Zk的選舉算法有兩種:一種是基于basic paxos實現(xiàn)的,另外一種是基于fast paxos算法實現(xiàn)的。系統(tǒng)默認的選舉算法為fast paxos。先介紹basic paxos流程:
1、選舉線程由當前Server發(fā)起選舉的線程擔任,其主要功能是對投票結(jié)果進行統(tǒng)計,并選出推薦的Server;
2、選舉線程首先向所有Server發(fā)起一次詢問(包括自己);
3、選舉線程收到回復(fù)后,驗證是否是自己發(fā)起的詢問(驗證zxid是否一致),然后獲取對方的id(myid),并存儲到當前詢問對象列表中,最后獲取對方提議的leader相關(guān)信息( id,zxid),并將這些信息存儲到當次選舉的投票記錄表中;
4、收到所有Server回復(fù)以后,就計算出zxid最大的那個Server,并將這個Server相關(guān)信息設(shè)置成下一次要投票的Server;
5、線程將當前zxid最大的Server設(shè)置為當前Server要推薦的Leader,如果此時獲勝的Server獲得n/2 + 1的Server票數(shù), 設(shè)置當前推薦的leader為獲勝的Server,將根據(jù)獲勝的Server相關(guān)信息設(shè)置自己的狀態(tài),否則,繼續(xù)這個過程,直到leader被選舉出來。
通過流程分析我們可以得出:要使Leader獲得多數(shù)Server的支持,則Server總數(shù)必須是奇數(shù)2n+1,且存活的Server的數(shù)目不得少于n+1.
每個Server啟動后都會重復(fù)以上流程。在恢復(fù)模式下,如果是剛從崩潰狀態(tài)恢復(fù)的或者剛啟動的server還會從磁盤快照中恢復(fù)數(shù)據(jù)和會話信息,zk會記錄事務(wù)日志并定期進行快照,方便在恢復(fù)時進行狀態(tài)恢復(fù)。選主的具體流程圖如下所示:
fast paxos流程是在選舉過程中,某Server首先向所有Server提議自己要成為leader,當其它Server收到提議以后,解決epoch和 zxid的沖突,并接受對方的提議,然后向?qū)Ψ桨l(fā)送接受提議完成的消息,重復(fù)這個流程,最后一定能選舉出Leader。其流程圖如下所示:
2.2 同步流程
選完leader以后,zk就進入狀態(tài)同步過程。
1、 leader等待server連接;
2、Follower連接leader,將最大的zxid發(fā)送給leader;
3、Leader根據(jù)follower的zxid確定同步點;
4、完成同步后通知follower 已經(jīng)成為uptodate狀態(tài);
5、Follower收到uptodate消息后,又可以重新接受client的請求進行服務(wù)了。
流程圖如下所示:
2.3 工作流程
2.3.1 Leader工作流程
Leader主要有三個功能:
1、恢復(fù)數(shù)據(jù);
2、維持與Learner的心跳,接收Learner請求并判斷Learner的請求消息類型;
3、Learner的消息類型主要有PING消息、REQUEST消息、ACK消息、REVALIDATE消息,根據(jù)不同的消息類型,進行不同的處理。
PING消息是指Learner的心跳信息;REQUEST消息是Follower發(fā)送的提議信息,包括寫請求及同步請求;ACK消息是 Follower的對提議的回復(fù),超過半數(shù)的Follower通過,則commit該提議;REVALIDATE消息是用來延長SESSION有效時間。
Leader的工作流程簡圖如下所示,在實際實現(xiàn)中,流程要比下圖復(fù)雜得多,啟動了三個線程來實現(xiàn)功能。
2.3.2 Follower工作流程
Follower主要有四個功能:
1、 向Leader發(fā)送請求(PING消息、REQUEST消息、ACK消息、REVALIDATE消息);
2、接收Leader消息并進行處理;
3、接收Client的請求,如果為寫請求,發(fā)送給Leader進行投票;
4、返回Client結(jié)果。
Follower的消息循環(huán)處理如下幾種來自Leader的消息:
1、PING消息: 心跳消息;
2、PROPOSAL消息:Leader發(fā)起的提案,要求Follower投票;
3、COMMIT消息:服務(wù)器端最新一次提案的信息;
4、UPTODATE消息:表明同步完成;
5、REVALIDATE消息:根據(jù)Leader的REVALIDATE結(jié)果,關(guān)閉待revalidate的session還是允許其接受消息;
6、SYNC消息:返回SYNC結(jié)果到客戶端,這個消息最初由客戶端發(fā)起,用來強制得到最新的更新。
Follower的工作流程簡圖如下所示,在實際實現(xiàn)中,F(xiàn)ollower是通過5個線程來實現(xiàn)功能的。
對于observer的流程不再敘述,observer流程和Follower的唯一不同的地方就是observer不會參加leader發(fā)起的投票。
附錄:
ZooKeeper典型使用場景一覽
ZooKeeper是一個高可用的分布式數(shù)據(jù)管理與系統(tǒng)協(xié)調(diào)框架。基于對Paxos算法的實現(xiàn),使該框架保證了分布式環(huán)境中數(shù)據(jù)的強一致性,也正是基 于這樣的特性,使得zookeeper能夠應(yīng)用于很多場景。網(wǎng)上對zk的使用場景也有不少介紹,本文將結(jié)合作者身邊的項目例子,系統(tǒng)的對zk的使用場景進 行歸類介紹。 值得注意的是,zk并不是生來就為這些場景設(shè)計,都是后來眾多開發(fā)者根據(jù)框架的特性,摸索出來的典型使用方法。因此,也非常歡迎你分享你在ZK使用上的奇 技淫巧。
場景類別 | 典型場景描述(ZK特性,使用方法) | 應(yīng)用中的具體使用 |
---|---|---|
數(shù)據(jù)發(fā)布與訂閱 | 發(fā)布與訂閱即所謂的配置管理,顧名思義就是將數(shù)據(jù)發(fā)布到zk節(jié)點上,供訂閱者動態(tài)獲取數(shù)據(jù),實現(xiàn)配置信息的集中式管理和動態(tài)更新。例如全局的配置信息,地址列表等就非常適合使用。 | 1、索引信息和集群中機器節(jié)點狀態(tài)存放在zk的一些指定節(jié)點,供各個客戶端訂閱使用。2、系統(tǒng)日志(經(jīng)過處理后的)存儲,這些日志通常2-3天后被清除。3、應(yīng)用中用到的一些配置信息集中管理,在應(yīng)用啟動的時候主動來獲取一次,并且在節(jié)點上注冊一個Watcher,以后每次配置有更新,實時通知到應(yīng)用,獲取最新配置信息。4、業(yè)務(wù)邏輯中需要用到的一些全局變量,比如一些消息中間件的消息隊列通常有個offset,這個offset存放在zk上,這樣集群中每個發(fā)送者都能知道當前的發(fā)送進度。5、系統(tǒng)中有些信息需要動態(tài)獲取,并且還會存在人工手動去修改這個信息。以前通常是暴露出接口,例如JMX接口,有了zk后,只要將這些信息存放到zk節(jié)點上即可。 |
Name Service | 這個主要是作為分布式命名服務(wù),通過調(diào)用zk的create node api,能夠很容易創(chuàng)建一個全局唯一的path,這個path就可以作為一個名稱。 | |
分布通知/協(xié)調(diào) | ZooKeeper 中特有watcher注冊與異步通知機制,能夠很好的實現(xiàn)分布式環(huán)境下不同系統(tǒng)之間的通知與協(xié)調(diào),實現(xiàn)對數(shù)據(jù)變更的實時處理。使用方法通常是不同系統(tǒng)都對 ZK上同一個znode進行注冊,監(jiān)聽znode的變化(包括znode本身內(nèi)容及子節(jié)點的),其中一個系統(tǒng)update了znode,那么另一個系統(tǒng)能 夠收到通知,并作出相應(yīng)處理。 | 1、另一種心跳檢測機制:檢測系統(tǒng)和被檢測系統(tǒng)之間并不直接關(guān)聯(lián)起來,而是通過zk上某個節(jié)點關(guān)聯(lián),大大減少系統(tǒng)耦合。2、另一種系統(tǒng)調(diào)度模式:某系統(tǒng)有控制臺和推送系統(tǒng)兩部分組成,控制臺的職責是控制推送系統(tǒng)進行相應(yīng)的推送工作。管理人員在控制臺作的一些操作,實際上是修改 了ZK上某些節(jié)點的狀態(tài),而zk就把這些變化通知給他們注冊Watcher的客戶端,即推送系統(tǒng),于是,作出相應(yīng)的推送任務(wù)。3、另一種工作匯報模式:一些類似于任務(wù)分發(fā)系統(tǒng),子任務(wù)啟動后,到zk來注冊一個臨時節(jié)點,并且定時將自己的進度進行匯報(將進度寫回這個臨時節(jié)點),這樣任務(wù)管理者就能夠?qū)崟r知道任務(wù)進度。總之,使用zookeeper來進行分布式通知和協(xié)調(diào)能夠大大降低系統(tǒng)之間的耦合。 |
分布式鎖 | 分布式鎖,這個主要得益于ZooKeeper為我們保證了數(shù)據(jù)的強一致性,即用戶只要完全相信每時每刻,zk集群中任意節(jié)點(一個zk server)上的相同znode的數(shù)據(jù)是一定是相同的。鎖服務(wù)可以分為兩類,一個是保持獨占,另一個是控制時序。所謂保持獨占,就是所有試圖來獲取這個鎖的客戶端,最終只有一個可以成功獲得這把鎖。通常的做法是把zk上的一個znode看作是一把鎖,通過create znode的方式來實現(xiàn)。所有客戶端都去創(chuàng)建 /distribute_lock 節(jié)點,最終成功創(chuàng)建的那個客戶端也即擁有了這把鎖。控制時序,就是所有視圖來獲取這個鎖的客戶端,最終都是會被安排執(zhí)行,只是有個全局時序了。做法和上面基本類似,只是這里 /distribute_lock 已經(jīng)預(yù)先存在,客戶端在它下面創(chuàng)建臨時有序節(jié)點(這個可以通過節(jié)點的屬性控制:CreateMode.EPHEMERAL_SEQUENTIAL來指 定)。Zk的父節(jié)點(/distribute_lock)維持一份sequence,保證子節(jié)點創(chuàng)建的時序性,從而也形成了每個客戶端的全局時序。 | |
集群管理 | 1、集群機器監(jiān)控:這通常用于那種對集群中機器狀態(tài),機器在線率有較高要求的場景,能夠快速對集群中機器變化作出響應(yīng)。這樣的場景中,往往有一個監(jiān)控系統(tǒng),實時檢測集群 機器是否存活。過去的做法通常是:監(jiān)控系統(tǒng)通過某種手段(比如ping)定時檢測每個機器,或者每個機器自己定時向監(jiān)控系統(tǒng)匯報“我還活著”。 這種做法可行,但是存在兩個比較明顯的問題:1、集群中機器有變動的時候,牽連修改的東西比較多。2、有一定的延時。利用ZooKeeper有兩個特性,就可以實時另一種集群機器存活性監(jiān)控系統(tǒng):a. 客戶端在節(jié)點 x 上注冊一個Watcher,那么如果 x 的子節(jié)點變化了,會通知該客戶端。b. 創(chuàng)建EPHEMERAL類型的節(jié)點,一旦客戶端和服務(wù)器的會話結(jié)束或過期,那么該節(jié)點就會消失。例如,監(jiān)控系統(tǒng)在 /clusterServers 節(jié)點上注冊一個Watcher,以后每動態(tài)加機器,那么就往 /clusterServers 下創(chuàng)建一個 EPHEMERAL類型的節(jié)點:/clusterServers/{hostname}. 這樣,監(jiān)控系統(tǒng)就能夠?qū)崟r知道機器的增減情況,至于后續(xù)處理就是監(jiān)控系統(tǒng)的業(yè)務(wù)了。2. Master選舉則是zookeeper中最為經(jīng)典的使用場景了。在分布式環(huán)境中,相同的業(yè)務(wù)應(yīng)用分布在不同的機器上,有些業(yè)務(wù)邏輯(例如一些耗時的計算,網(wǎng)絡(luò)I/O處理),往往只需要讓整個集群中的某一臺機器進行執(zhí)行, 其余機器可以共享這個結(jié)果,這樣可以大大減少重復(fù)勞動,提高性能,于是這個master選舉便是這種場景下的碰到的主要問題。利用ZooKeeper的強一致性,能夠保證在分布式高并發(fā)情況下節(jié)點創(chuàng)建的全局唯一性,即:同時有多個客戶端請求創(chuàng)建 /currentMaster節(jié)點,最終一定只有一個客戶端請求能夠創(chuàng)建成功。利用這個特性,就能很輕易的在分布式環(huán)境中進行集群選取了。另外,這種場景演化一下,就是動態(tài)Master選舉。這就要用到 EPHEMERAL_SEQUENTIAL類型節(jié)點的特性了。上文中提到,所有客戶端創(chuàng)建請求,最終只有一個能夠創(chuàng)建成功。在這里稍微變化下,就是允許所有請求都能夠創(chuàng)建成功,但是得有個創(chuàng)建順序,于是所有的請求最終 在ZK上創(chuàng)建結(jié)果的一種可能情況是這樣: /currentMaster/{sessionId}-1,/currentMaster/{sessionId}-2 , /currentMaster/{sessionId}-3 ….. 每次選取序列號最小的那個機器作為Master,如果這個機器掛了,由于他創(chuàng)建的節(jié)點會馬上小時,那么之后最小的那個機器就是Master了。 | 1、在搜索系統(tǒng)中,如果集群中每個機器都生成一份全量索引,不僅耗時,而且不能保證彼此之間索引數(shù)據(jù)一致。因此讓集群中的Master來進行全量索引的生成, 然后同步到集群中其它機器。2、另外,Master選舉的容災(zāi)措施是,可以隨時進行手動指定master,就是說應(yīng)用在zk在無法獲取master信息時,可以通過比如http方式,向 一個地方獲取master。 |
分布式隊列 | 隊列方面,我目前感覺有兩種,一種是常規(guī)的先進先出隊列,另一種是要等到隊列成員聚齊之后的才統(tǒng)一按序執(zhí)行。對于第二種先進先出隊列,和分布式鎖服務(wù)中的控制時序場景基本原理一致,這里不再贅述。第二種隊列其實是在FIFO隊列的基礎(chǔ)上作了一個增強。通常可以在 /queue 這個znode下預(yù)先建立一個/queue/num 節(jié)點,并且賦值為n(或者直接給/queue賦值n),表示隊列大小,之后每次有隊列成員加入后,就判斷下是否已經(jīng)到達隊列大小,決定是否可以開始執(zhí)行 了。這種用法的典型場景是,分布式環(huán)境中,一個大任務(wù)Task A,需要在很多子任務(wù)完成(或條件就緒)情況下才能進行。這個時候,凡是其中一個子任務(wù)完成(就緒),那么就去 /taskList 下建立自己的臨時時序節(jié)點(CreateMode.EPHEMERAL_SEQUENTIAL),當 /taskList 發(fā)現(xiàn)自己下面的子節(jié)點滿足指定個數(shù),就可以進行下一步按序進行處理了。 |
數(shù)據(jù)發(fā)布與訂閱(配置中心)
發(fā)布與訂閱模型,即所謂的配置中心,顧名思義就是發(fā)布者將數(shù)據(jù)發(fā)布到ZK節(jié)點上,供訂閱者動態(tài)獲取數(shù)據(jù),實現(xiàn)配置信息的集中式管理和動態(tài)更新。例如全局的配置信息,服務(wù)式服務(wù)框架的服務(wù)地址列表等就非常適合使用。
- 應(yīng)用中用到的一些配置信息放到ZK上進行集中管理。這類場景通常是這樣:應(yīng)用在啟動的時候會主動來獲取一次配置,同時,在節(jié)點上注冊一個Watcher,這樣一來,以后每次配置有更新的時候,都會實時通知到訂閱的客戶端,從來達到獲取最新配置信息的目的。
- 分布式搜索服務(wù)中,索引的元信息和服務(wù)器集群機器的節(jié)點狀態(tài)存放在ZK的一些指定節(jié)點,供各個客戶端訂閱使用。
- 分布式日志收集系統(tǒng)。這個系統(tǒng)的核心工作是收集分布在不同機器的日志。收集器通常是按照應(yīng)用來分配收集任務(wù)單元,因此需要在ZK上創(chuàng)建一個以應(yīng)用名作為path的節(jié)點P,并將這個應(yīng)用的所有機器ip,以子節(jié)點的形式注冊到節(jié)點P上,這樣一來就能夠?qū)崿F(xiàn)機器變動的時候,能夠?qū)崟r通知到收集器調(diào)整任務(wù)分配。
- 系統(tǒng)中有些信息需要動態(tài)獲取,并且還會存在人工手動去修改這個信息的發(fā)問。通常是暴露出接口,例如JMX接口,來獲取一些運行時的信息。引入ZK之后,就不用自己實現(xiàn)一套方案了,只要將這些信息存放到指定的ZK節(jié)點上即可。
注意:在上面提到的應(yīng)用場景中,有個默認前提是:數(shù)據(jù)量很小,但是數(shù)據(jù)更新可能會比較快的場景。
負載均衡
這里說的負載均衡是指軟負載均衡。在分布式環(huán)境中,為了保證高可用性,通常同一個應(yīng)用或同一個服務(wù)的提供方都會部署多份,達到對等服務(wù)。而消費者就須要在這些對等的服務(wù)器中選擇一個來執(zhí)行相關(guān)的業(yè)務(wù)邏輯,其中比較典型的是消息中間件中的生產(chǎn)者,消費者負載均衡。
消息中間件中發(fā)布者和訂閱者的負載均衡,linkedin開源的KafkaMQ和阿里開源的metaq都是通過zookeeper來做到生產(chǎn)者、消費者的負載均衡。這里以metaq為例如講下:
生產(chǎn)者負載均衡:metaq發(fā)送消息的時候,生產(chǎn)者在發(fā)送消息的時候必須選擇一臺broker上的一個分區(qū)來發(fā)送消息,因此metaq在運行過程中,會把所有broker和對應(yīng)的分區(qū)信息全部注冊到ZK指定節(jié)點上,默認的策略是一個依次輪詢的過程,生產(chǎn)者在通過ZK獲取分區(qū)列表之后,會按照brokerId和partition的順序排列組織成一個有序的分區(qū)列表,發(fā)送的時候按照從頭到尾循環(huán)往復(fù)的方式選擇一個分區(qū)來發(fā)送消息。
消費負載均衡:
在消費過程中,一個消費者會消費一個或多個分區(qū)中的消息,但是一個分區(qū)只會由一個消費者來消費。MetaQ的消費策略是:
- 每個分區(qū)針對同一個group只掛載一個消費者。
- 如果同一個group的消費者數(shù)目大于分區(qū)數(shù)目,則多出來的消費者將不參與消費。
- 如果同一個group的消費者數(shù)目小于分區(qū)數(shù)目,則有部分消費者需要額外承擔消費任務(wù)。
在某個消費者故障或者重啟等情況下,其他消費者會感知到這一變化(通過 zookeeper watch消費者列表),然后重新進行負載均衡,保證所有的分區(qū)都有消費者進行消費。
命名服務(wù)(Naming Service)
命名服務(wù)也是分布式系統(tǒng)中比較常見的一類場景。在分布式系統(tǒng)中,通過使用命名服務(wù),客戶端應(yīng)用能夠根據(jù)指定名字來獲取資源或服務(wù)的地址,提供者等信息。被命名的實體通常可以是集群中的機器,提供的服務(wù)地址,遠程對象等等——這些我們都可以統(tǒng)稱他們?yōu)槊郑∟ame)。其中較為常見的就是一些分布式服務(wù)框架中的服務(wù)地址列表。通過調(diào)用ZK提供的創(chuàng)建節(jié)點的API,能夠很容易創(chuàng)建一個全局唯一的path,這個path就可以作為一個名稱。
阿里巴巴集團開源的分布式服務(wù)框架Dubbo中使用ZooKeeper來作為其命名服務(wù),維護全局的服務(wù)地址列表,點擊這里查看Dubbo開源項目。在Dubbo實現(xiàn)中:
服務(wù)提供者在啟動的時候,向ZK上的指定節(jié)點/dubbo/${serviceName}/providers目錄下寫入自己的URL地址,這個操作就完成了服務(wù)的發(fā)布。
服務(wù)消費者啟動的時候,訂閱/dubbo/{serviceName} /consumers目錄下寫入自己的URL地址。
注意,所有向ZK上注冊的地址都是臨時節(jié)點,這樣就能夠保證服務(wù)提供者和消費者能夠自動感應(yīng)資源的變化。
另外,Dubbo還有針對服務(wù)粒度的監(jiān)控,方法是訂閱/dubbo/${serviceName}目錄下所有提供者和消費者的信息。
分布式通知/協(xié)調(diào)
ZooKeeper中特有watcher注冊與異步通知機制,能夠很好的實現(xiàn)分布式環(huán)境下不同系統(tǒng)之間的通知與協(xié)調(diào),實現(xiàn)對數(shù)據(jù)變更的實時處理。使用方法通常是不同系統(tǒng)都對ZK上同一個znode進行注冊,監(jiān)聽znode的變化(包括znode本身內(nèi)容及子節(jié)點的),其中一個系統(tǒng)update了znode,那么另一個系統(tǒng)能夠收到通知,并作出相應(yīng)處理
- 另一種心跳檢測機制:檢測系統(tǒng)和被檢測系統(tǒng)之間并不直接關(guān)聯(lián)起來,而是通過zk上某個節(jié)點關(guān)聯(lián),大大減少系統(tǒng)耦合。
- 另一種系統(tǒng)調(diào)度模式:某系統(tǒng)有控制臺和推送系統(tǒng)兩部分組成,控制臺的職責是控制推送系統(tǒng)進行相應(yīng)的推送工作。管理人員在控制臺作的一些操作,實際上是修改了ZK上某些節(jié)點的狀態(tài),而ZK就把這些變化通知給他們注冊Watcher的客戶端,即推送系統(tǒng),于是,作出相應(yīng)的推送任務(wù)。
- 另一種工作匯報模式:一些類似于任務(wù)分發(fā)系統(tǒng),子任務(wù)啟動后,到zk來注冊一個臨時節(jié)點,并且定時將自己的進度進行匯報(將進度寫回這個臨時節(jié)點),這樣任務(wù)管理者就能夠?qū)崟r知道任務(wù)進度。
總之,使用zookeeper來進行分布式通知和協(xié)調(diào)能夠大大降低系統(tǒng)之間的耦合
集群管理與Master選舉
- 集群機器監(jiān)控:這通常用于那種對集群中機器狀態(tài),機器在線率有較高要求的場景,能夠快速對集群中機器變化作出響應(yīng)。這樣的場景中,往往有一個監(jiān)控系統(tǒng),實時檢測集群機器是否存活。過去的做法通常是:監(jiān)控系統(tǒng)通過某種手段(比如ping)定時檢測每個機器,或者每個機器自己定時向監(jiān)控系統(tǒng)匯報“我還活著”。 這種做法可行,但是存在兩個比較明顯的問題:
- 集群中機器有變動的時候,牽連修改的東西比較多。
- 有一定的延時。
利用ZooKeeper有兩個特性,就可以實時另一種集群機器存活性監(jiān)控系統(tǒng):
- 客戶端在節(jié)點 x 上注冊一個Watcher,那么如果 x?的子節(jié)點變化了,會通知該客戶端。
- 創(chuàng)建EPHEMERAL類型的節(jié)點,一旦客戶端和服務(wù)器的會話結(jié)束或過期,那么該節(jié)點就會消失。
例如,監(jiān)控系統(tǒng)在 /clusterServers 節(jié)點上注冊一個Watcher,以后每動態(tài)加機器,那么就往 /clusterServers 下創(chuàng)建一個 EPHEMERAL類型的節(jié)點:/clusterServers/{hostname}. 這樣,監(jiān)控系統(tǒng)就能夠?qū)崟r知道機器的增減情況,至于后續(xù)處理就是監(jiān)控系統(tǒng)的業(yè)務(wù)了。
- Master選舉則是zookeeper中最為經(jīng)典的應(yīng)用場景了。
在分布式環(huán)境中,相同的業(yè)務(wù)應(yīng)用分布在不同的機器上,有些業(yè)務(wù)邏輯(例如一些耗時的計算,網(wǎng)絡(luò)I/O處理),往往只需要讓整個集群中的某一臺機器進行執(zhí)行,其余機器可以共享這個結(jié)果,這樣可以大大減少重復(fù)勞動,提高性能,于是這個master選舉便是這種場景下的碰到的主要問題。
利用ZooKeeper的強一致性,能夠保證在分布式高并發(fā)情況下節(jié)點創(chuàng)建的全局唯一性,即:同時有多個客戶端請求創(chuàng)建 /currentMaster 節(jié)點,最終一定只有一個客戶端請求能夠創(chuàng)建成功。利用這個特性,就能很輕易的在分布式環(huán)境中進行集群選取了。
另外,這種場景演化一下,就是動態(tài)Master選舉。這就要用到?EPHEMERAL_SEQUENTIAL類型節(jié)點的特性了。
上文中提到,所有客戶端創(chuàng)建請求,最終只有一個能夠創(chuàng)建成功。在這里稍微變化下,就是允許所有請求都能夠創(chuàng)建成功,但是得有個創(chuàng)建順序,于是所有的請求最終在ZK上創(chuàng)建結(jié)果的一種可能情況是這樣: /currentMaster/{sessionId}-1 ,?/currentMaster/{sessionId}-2 ,?/currentMaster/{sessionId}-3 ….. 每次選取序列號最小的那個機器作為Master,如果這個機器掛了,由于他創(chuàng)建的節(jié)點會馬上小時,那么之后最小的那個機器就是Master了。
- 在搜索系統(tǒng)中,如果集群中每個機器都生成一份全量索引,不僅耗時,而且不能保證彼此之間索引數(shù)據(jù)一致。因此讓集群中的Master來進行全量索引的生成,然后同步到集群中其它機器。另外,Master選舉的容災(zāi)措施是,可以隨時進行手動指定master,就是說應(yīng)用在zk在無法獲取master信息時,可以通過比如http方式,向一個地方獲取master。
- 在Hbase中,也是使用ZooKeeper來實現(xiàn)動態(tài)HMaster的選舉。在Hbase實現(xiàn)中,會在ZK上存儲一些ROOT表的地址和HMaster的地址,HRegionServer也會把自己以臨時節(jié)點(Ephemeral)的方式注冊到Zookeeper中,使得HMaster可以隨時感知到各個HRegionServer的存活狀態(tài),同時,一旦HMaster出現(xiàn)問題,會重新選舉出一個HMaster來運行,從而避免了HMaster的單點問題
分布式鎖
分布式鎖,這個主要得益于ZooKeeper為我們保證了數(shù)據(jù)的強一致性。鎖服務(wù)可以分為兩類,一個是保持獨占,另一個是控制時序。
- 所謂保持獨占,就是所有試圖來獲取這個鎖的客戶端,最終只有一個可以成功獲得這把鎖。通常的做法是把zk上的一個znode看作是一把鎖,通過create znode的方式來實現(xiàn)。所有客戶端都去創(chuàng)建 /distribute_lock 節(jié)點,最終成功創(chuàng)建的那個客戶端也即擁有了這把鎖。
- 控制時序,就是所有視圖來獲取這個鎖的客戶端,最終都是會被安排執(zhí)行,只是有個全局時序了。做法和上面基本類似,只是這里 /distribute_lock 已經(jīng)預(yù)先存在,客戶端在它下面創(chuàng)建臨時有序節(jié)點(這個可以通過節(jié)點的屬性控制:CreateMode.EPHEMERAL_SEQUENTIAL來指定)。Zk的父節(jié)點(/distribute_lock)維持一份sequence,保證子節(jié)點創(chuàng)建的時序性,從而也形成了每個客戶端的全局時序。
分布式鎖實現(xiàn)原理:
分布式隊列
隊列方面,簡單地講有兩種,一種是常規(guī)的先進先出隊列,另一種是要等到隊列成員聚齊之后的才統(tǒng)一按序執(zhí)行。對于第一種先進先出隊列,和分布式鎖服務(wù)中的控制時序場景基本原理一致,這里不再贅述。
第二種隊列其實是在FIFO隊列的基礎(chǔ)上作了一個增強。通常可以在 /queue 這個znode下預(yù)先建立一個/queue/num 節(jié)點,并且賦值為n(或者直接給/queue賦值n),表示隊列大小,之后每次有隊列成員加入后,就判斷下是否已經(jīng)到達隊列大小,決定是否可以開始執(zhí)行了。這種用法的典型場景是,分布式環(huán)境中,一個大任務(wù)Task A,需要在很多子任務(wù)完成(或條件就緒)情況下才能進行。這個時候,凡是其中一個子任務(wù)完成(就緒),那么就去 /taskList 下建立自己的臨時時序節(jié)點(CreateMode.EPHEMERAL_SEQUENTIAL),當 /taskList 發(fā)現(xiàn)自己下面的子節(jié)點滿足指定個數(shù),就可以進行下一步按序進行處理了。
參考:
http://www.wuzesheng.com/?p=2609
https://www.2cto.com/kf/201808/768816.html
http://zookeeper.apache.org/
http://blog.csdn.net/cutesource/article/details/5822459
http://blog.csdn.net/pwlazy/article/details/8080626
http://nileader.blog.51cto.com/1381108/795265
http://nileader.blog.51cto.com/1381108/926753
http://nileader.blog.51cto.com/1381108/795230
http://netcome.iteye.com/blog/1474255