場景一
有這樣一個場景:系統(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ù)器(每個負責(zé)總索引中的一部分的搜索任務(wù))和一個總服務(wù)器(負責(zé)向這20個搜索引擎的服務(wù)器發(fā)出搜索請求并合并 結(jié)果集)葛峻,一個備用的總服務(wù)器(負責(zé)當總服務(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)導(dǎo)者崩潰后队魏,Zab就進入了恢復(fù)模式公般,當領(lǐng)導(dǎo)者被選舉出來,且大多數(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ā)起選舉的線程擔(dān)任唬渗,其主要功能是對投票結(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)兩部分組成恐锦,控制臺的職責(zé)是控制推送系統(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ù)目熟嫩,則有部分消費者需要額外承擔(dān)消費任務(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)兩部分組成,控制臺的職責(zé)是控制推送系統(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