解讀什么是Zookeeper?
張大胖所在的公司這幾年發(fā)展得相當(dāng)不錯(cuò),業(yè)務(wù)激增,人員也迅速擴(kuò)展,轉(zhuǎn)眼之間,張大胖已經(jīng)成為公司的“資深”員工了,更重要的是,經(jīng)過(guò)這些年的不懈努力,他終于坐上了架構(gòu)師的寶座。
但是大胖很快發(fā)現(xiàn),這架構(gòu)師真不是好當(dāng)?shù)?,技術(shù)選型、架構(gòu)設(shè)計(jì),尤其是大家搞不定的技術(shù)難點(diǎn),最終都得自己扛起來(lái)。溝通、說(shuō)服、妥協(xié)、甚至爭(zhēng)吵都是家常便飯,比自己之前單純做開(kāi)發(fā)的時(shí)候難多了。
公司的IT系統(tǒng)早已經(jīng)從單機(jī)轉(zhuǎn)向了分布式,分布式系統(tǒng)帶來(lái)了巨大的挑戰(zhàn)。這周一剛上班,張大胖的郵箱里已經(jīng)塞滿(mǎn)了緊急郵件。
1小梁的郵件
小梁的郵件里說(shuō)了一個(gè)RPC調(diào)用的問(wèn)題,本來(lái)公司的架構(gòu)組開(kāi)發(fā)了一個(gè)RPC框架讓各個(gè)組去使用,但是各開(kāi)發(fā)小組紛紛抱怨:這個(gè)RPC框架不支持動(dòng)態(tài)的服務(wù)注冊(cè)和發(fā)現(xiàn)。
張大胖一看這個(gè)圖就明白怎么回事了,為了支持高并發(fā),OrderService被部署了4份,每個(gè)客戶(hù)端都保存了一份服務(wù)提供者的列表,但是這個(gè)列表是靜態(tài)的(在配置文件中寫(xiě)死的),如果服務(wù)的提供者發(fā)生了變化,例如有些機(jī)器down了,或者又新增了OrderService的實(shí)例,客戶(hù)端根本不知道,可能還在傻乎乎地嘗試那些已經(jīng)壞掉的實(shí)例呢!
想要得到***的服務(wù)提供者的URL列表,必須得手工更新配置文件才行,確實(shí)很不方便。
對(duì)于這樣的問(wèn)題,大胖馬上就意識(shí)到,這就是客戶(hù)端和服務(wù)提供者的緊耦合啊。
想解除這個(gè)耦合,非得增加一個(gè)中間層不可!
張大胖想到,應(yīng)該有個(gè)注冊(cè)中心,首先給這些服務(wù)命名(例如orderService),其次那些OrderService 都可以在這里注冊(cè)一下,客戶(hù)端就到這里來(lái)查詢(xún),只需要給出名稱(chēng)orderService,注冊(cè)中心就可以給出一個(gè)可以使用的url, 再也不怕服務(wù)提供者的動(dòng)態(tài)增減了。
不知道是不是下意識(shí)的行為,張大胖把這個(gè)注冊(cè)中心的數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)成為了一個(gè)樹(shù)形結(jié)構(gòu):
/orderService 表達(dá)了一個(gè)服務(wù)的概念, 下面的每個(gè)節(jié)點(diǎn)表示了一個(gè)服務(wù)的實(shí)例。 例如/orderService/node2表示的order service 的第二個(gè)實(shí)例, 每個(gè)節(jié)點(diǎn)上可以記錄下該實(shí)例的url , 這樣就可以查詢(xún)了。
當(dāng)然這個(gè)注冊(cè)中心必須得能和各個(gè)服務(wù)實(shí)例通信,如果某個(gè)服務(wù)實(shí)例不幸down掉了,那它在樹(shù)結(jié)構(gòu)中對(duì)于的節(jié)點(diǎn)也必須刪除, 這樣客戶(hù)端就查詢(xún)不到了。
嗯,可以在注冊(cè)中心和各個(gè)服務(wù)實(shí)例直接建立Session, 讓各個(gè)服務(wù)實(shí)例定期地發(fā)送心跳,如果過(guò)了特定時(shí)間收不到心跳,就認(rèn)為這個(gè)服務(wù)實(shí)例掛掉了,Session 過(guò)期, 把它從樹(shù)形結(jié)構(gòu)中刪除。
張大胖把自己的想法回復(fù)了小梁,接著看小王的郵件。
2小王的Master選舉
小王郵件中說(shuō)的是三個(gè)Batch Job的協(xié)調(diào)問(wèn)題,這三個(gè)Batch Job 部署在三臺(tái)機(jī)器上,但是這三個(gè)Batch Job同一個(gè)時(shí)刻只能有一個(gè)運(yùn)行,如果其中某個(gè)不幸down掉,剩下的兩個(gè)就需要做個(gè)選舉,選出來(lái)的那個(gè)Batch Job 需要“繼承遺志”,繼續(xù)工作。
其實(shí)這就是一個(gè)Master的選舉問(wèn)題,張大胖一眼就看出了本質(zhì)。
只是為了選舉出Master, 這三個(gè)Batch Job 需要互通有無(wú),互相協(xié)調(diào)才行,這就麻煩了!
要不弄個(gè)數(shù)據(jù)庫(kù)表? 利用數(shù)據(jù)庫(kù)表主鍵不能沖突的特性,讓這三個(gè)Batch Job 都向同一個(gè)表中插入同樣的數(shù)據(jù),誰(shuí)先成功誰(shuí)就是Master !
可是如果搶到Master的那個(gè)Batch Job掛掉了,別人永遠(yuǎn)就搶不到了! 因?yàn)橛涗浺呀?jīng)存在了, 別的Batch Job 沒(méi)法插入數(shù)據(jù)了!
嗯,還得加上定期更新的機(jī)制,如果一段時(shí)間內(nèi)沒(méi)有更新就認(rèn)為Master死掉了,別的Batch Job可以繼續(xù)搶..... 不過(guò)這么做好麻煩!
換個(gè)思路,讓他們也去一個(gè)注冊(cè)中心去大吼一聲:“我是master!”, 誰(shuí)的聲音大誰(shuí)是Master 。
其實(shí)不是吼一聲,三個(gè)Batch Job啟動(dòng)以后,都去注冊(cè)中心爭(zhēng)搶著去創(chuàng)建一個(gè)樹(shù)的節(jié)點(diǎn)(例如/master ),誰(shuí)創(chuàng)建成功誰(shuí)就是Master (當(dāng)然注冊(cè)中心必須保證只能創(chuàng)建成功一次,其他請(qǐng)求就失敗了),其他兩個(gè)Batch Job就對(duì)這個(gè)節(jié)點(diǎn)虎視眈眈地監(jiān)控,如果這個(gè)節(jié)點(diǎn)被刪除,就開(kāi)始新一輪爭(zhēng)搶?zhuān)?chuàng)建那個(gè)/master節(jié)點(diǎn)。
什么時(shí)候節(jié)點(diǎn)會(huì)被刪除呢? 對(duì),就是當(dāng)前Master的機(jī)器down掉了 ! 很明顯,注冊(cè)中心也需要和各個(gè)機(jī)器通信,看看他們是否活著。
等等,這里還有一個(gè)復(fù)雜的情況, 如果機(jī)器1并沒(méi)有死掉,只是和注冊(cè)中心長(zhǎng)時(shí)間連接不上,注冊(cè)中心會(huì)發(fā)現(xiàn)Session超時(shí),會(huì)把機(jī)器1創(chuàng)建的/master刪除。 讓機(jī)器2和機(jī)器3去搶?zhuān)绻麢C(jī)器3成為了master, 開(kāi)始運(yùn)行Batch Job, 但是機(jī)器1并不知道自己被解除了Master的職務(wù), 還在努力的運(yùn)行Batch Job,這就沖突了!
看來(lái)機(jī)器1必須得能感知到和注冊(cè)中心的連接斷開(kāi)了,需要停止Batch Job才行,等到和注冊(cè)中心再次連接上以后,才知道自己已經(jīng)不是master了,老老實(shí)實(shí)地等下一次機(jī)會(huì)吧。
無(wú)論哪種方案,實(shí)現(xiàn)起來(lái)都很麻煩,這該死的分布式!
先把思路給小王回復(fù)一下吧。接著看小蔡的郵件。
3小蔡的分布式鎖
小蔡的郵件里說(shuō)的問(wèn)題更加麻煩,有多個(gè)不同的系統(tǒng)(當(dāng)然是分布在不同的機(jī)器上?。?,要對(duì)同一個(gè)資源操作。
這要是在一個(gè)機(jī)器上,使用某個(gè)語(yǔ)言?xún)?nèi)置的鎖就可以搞定,例如Java的synchronized , 但是現(xiàn)在是分布式啊,程序都跑在不同機(jī)器的不同進(jìn)程中, synchcronized一點(diǎn)用都沒(méi)有了!
這是個(gè)分布式鎖的問(wèn)題??!
能不能考慮下Master選舉問(wèn)題中的方式,讓大家去搶?zhuān)?誰(shuí)能搶先在注冊(cè)中心創(chuàng)建一個(gè)/distribute_lock的節(jié)點(diǎn)就表示搶到這個(gè)鎖了,然后讀寫(xiě)資源,讀寫(xiě)完以后就把/distribute_lock節(jié)點(diǎn)刪除,大家再來(lái)?yè)尅?nbsp;
可是這樣的話(huà)某個(gè)系統(tǒng)可能會(huì)多次搶到,不太公平。
如果讓這些系統(tǒng)在注冊(cè)中心的/distribute_lock下都創(chuàng)建子節(jié)點(diǎn), 然后給每個(gè)系統(tǒng)一個(gè)編號(hào),會(huì)是這個(gè)樣子:
然后各個(gè)系統(tǒng)去檢查自己的編號(hào),誰(shuí)的編號(hào)小就認(rèn)為誰(shuí)持有了鎖, 例如系統(tǒng)1。
系統(tǒng)1持有了鎖,就可以對(duì)共享資源進(jìn)行操作了, 操作完成以后process_01這個(gè)節(jié)點(diǎn)刪除, 再創(chuàng)建一個(gè)新的節(jié)點(diǎn)(編號(hào)變成process_04了):
其他系統(tǒng)一看,編號(hào)為01的刪除了,再看看誰(shuí)是最小的吧,是process_02,那就認(rèn)為系統(tǒng)2持有了鎖,可以對(duì)共享資源操作了。 操作完成以后也要把process_02節(jié)點(diǎn)刪除,創(chuàng)建新的節(jié)點(diǎn)。這時(shí)候process_03就是最小的了,可以持有鎖了。
這樣循環(huán)往復(fù)下去...... 分布式鎖就可以實(shí)現(xiàn)了!
看看,我設(shè)計(jì)的這個(gè)集中式的樹(shù)形結(jié)構(gòu)很不錯(cuò)吧,能解決各種各樣的問(wèn)題! 張大胖不由得意起來(lái)。
好,先把這個(gè)想法告訴小蔡,實(shí)現(xiàn)細(xì)節(jié)下午開(kāi)個(gè)會(huì)討論。
4Zookeeper
正準(zhǔn)備回復(fù)小蔡的時(shí)候,大胖突然意識(shí)到,自己漏了一個(gè)重要的點(diǎn),那就是注冊(cè)中心的高可用性,如果注冊(cè)中心只有那么一臺(tái)機(jī)器,一旦掛掉,整個(gè)系統(tǒng)就玩完了。
這個(gè)注冊(cè)中心也得有多臺(tái)機(jī)器來(lái)保證高可用性,那個(gè)自己頗為得意的樹(shù)形結(jié)構(gòu)也需要在多個(gè)機(jī)器之間同步啊,要是有機(jī)器掛掉怎么辦? 通信超時(shí)怎么辦? 樹(shù)形結(jié)構(gòu)的數(shù)據(jù)怎么在各個(gè)機(jī)器之間保證強(qiáng)一致性?
小王、小梁、小蔡的原始問(wèn)題沒(méi)有解決,單單是這個(gè)注冊(cè)中心就要了命了。 以自己公司的技術(shù)實(shí)力,搞出一套這樣的注冊(cè)中心簡(jiǎn)直是Mission Impossible !
大胖趕緊上網(wǎng)搜索,看看有沒(méi)有類(lèi)似的解決方案,讓大胖感到萬(wàn)分幸運(yùn)的是,果然有一個(gè),叫做Zookeeper !
Zookeeper 所使用的樹(shù)形結(jié)構(gòu)和自己想象的非常類(lèi)似,更重要的是,人家實(shí)現(xiàn)了樹(shù)形結(jié)構(gòu)數(shù)據(jù)在多臺(tái)機(jī)器之間的可靠復(fù)制,達(dá)到了數(shù)據(jù)在多臺(tái)機(jī)器之間的一致性。并且這多臺(tái)機(jī)器中如果有部分掛掉了/或者由于網(wǎng)絡(luò)原因無(wú)法連接上了, 整個(gè)系統(tǒng)還可以工作。
大胖趕快去看Zookeeper的關(guān)鍵概念和API:
1. Session : 表示某個(gè)客戶(hù)系統(tǒng)(例如Batch Job)和ZooKeeper之間的連接會(huì)話(huà), Batch Job連上ZooKeeper以后會(huì)周期性地發(fā)送心跳信息, 如果Zookeepr在特定時(shí)間內(nèi)收不到心跳,就會(huì)認(rèn)為這個(gè)Batch Job已經(jīng)死掉了, Session 就會(huì)結(jié)束。
2. znode : 樹(shù)形結(jié)構(gòu)中的每個(gè)節(jié)點(diǎn)叫做znode, 按類(lèi)型可以分為***的znode(除非主動(dòng)刪除,否則一直存在),臨時(shí)的znode(Session結(jié)束就會(huì)刪除)和 順序znode(就是小蔡的分布式鎖中的process_01,process_02.....)。
3. Watch : 某個(gè)客戶(hù)系統(tǒng)(例如Batch Job)可以監(jiān)控znode, znode節(jié)點(diǎn)的變化(刪除,修改數(shù)據(jù)等)都可以通知Batch Job, 這樣Batch Job可以采取相應(yīng)的動(dòng)作,例如爭(zhēng)搶著去創(chuàng)建節(jié)點(diǎn)。
嗯,這些概念和接口應(yīng)該可以滿(mǎn)足我們的要求了, 就是它了,下午召集大家開(kāi)會(huì)開(kāi)始學(xué)習(xí)Zookeeper吧。
后記:本文從使用者的角度描述了Zookeeper有什么用處,至于它內(nèi)部是如何工作,那是另外一個(gè)Big topic了,我們以后再講。
【本文為51CTO專(zhuān)欄作者“劉欣”的原創(chuàng)稿件,轉(zhuǎn)載請(qǐng)通過(guò)作者微信公眾號(hào)coderising獲取授權(quán)】