秒懂zookeeper

前言

看了很多zookeeper的文章和視頻,幾乎前篇一律先講一遍基本使用再講使用場景,看完還是云里霧里。
所以本文換一個角度講解zookeeper,從它出現(xiàn)的背景,要解決的問題入手,一步步反推它的功能。用大白話的方式試著從設(shè)計者的角度思考zookeeper為何如此設(shè)計。

注:本文只是幫助理解zookeeper,具體的搭建和操作可以參考官方文檔或百度,從設(shè)計者的角度思考也只是一種假設(shè),只是為了方便理解,實際上zookeeper最初的定位可能壓根沒考慮這么多

協(xié)調(diào)者

現(xiàn)如今上網(wǎng)用戶越來越多,單機(jī)服務(wù)無法滿足各種高并發(fā)場景帶來的壓力,所以采用多個物理節(jié)點來共同完成任務(wù),組成一個分布式應(yīng)用,而zookeeper就是分布式應(yīng)用的協(xié)調(diào)者。
什么是協(xié)調(diào)者?你們公司的領(lǐng)導(dǎo)就是協(xié)調(diào)者,負(fù)責(zé)協(xié)調(diào)每個人的工作。
如果把公司比做動物園,每個員工是一個動物,那么負(fù)責(zé)協(xié)調(diào)工作的領(lǐng)導(dǎo)就是動物園管理員(zookeeper的直譯)。
分布式應(yīng)用也是這樣,比如有n個訂單服務(wù)、n個用戶服務(wù),兩種服務(wù)雖然各處理一類事情但總歸是需要配合的,需要配合就有公共的數(shù)據(jù)(比如節(jié)點的信息,公共的配置),那這公共的數(shù)據(jù)存哪里好?單獨交給任何一個服務(wù)都不合適,因為大家都是平等的,如果所有服務(wù)都給,一致性怎么保證,萬一不一樣到底以哪個為準(zhǔn)。
所以就需要一個中間件來協(xié)調(diào),就是zookeeper,它的存在就是給分布式應(yīng)用提供一致性服務(wù)。


zookeeper

服務(wù)

如果我們是zookeeper的設(shè)計者,現(xiàn)在用戶的痛點找到了,產(chǎn)品的定位也有了:協(xié)調(diào)分布式應(yīng)用。
那么接下來就要想了,要提供怎樣的服務(wù),或者說分布式應(yīng)用中哪些工作需要協(xié)調(diào)?

分布式注冊中心

比如分布式應(yīng)用有A服務(wù)、B服務(wù),A服務(wù)需要調(diào)用B服務(wù),那A怎么知道B服務(wù)在哪些臺機(jī)器吶?ip是多少?port是多少?
如果想簡單解決,那就把B服務(wù)的ip和port在A服務(wù)存儲一份就可以了,但是如果有多個B服務(wù)就需要存儲多份,某個B服務(wù)宕機(jī)了還要改。如果又加了個依賴B服務(wù)的C服務(wù),又要再C服務(wù)存一份,甚至以后有更多服務(wù)彼此依賴,那光維護(hù)這些信息就能累死。
zookeeper作為分布式協(xié)調(diào)者,這種事情肯定要管,怎么管吶,所有服務(wù)都到我這里注冊(ip、port等信息),這樣每臺機(jī)器的每個服務(wù)只需注冊一次,將來A想調(diào)用B,先來注冊中心找到一個B的位置,然后去調(diào)用就可以了,某個服務(wù)宕機(jī)了,就把它注冊的信息刪掉,起到一個注冊中心的作用。

文件系統(tǒng)數(shù)據(jù)結(jié)構(gòu)

有了具體的需求,下面就是設(shè)計了,如果自己來設(shè)計zookeeper,這個數(shù)據(jù)結(jié)構(gòu)如何設(shè)計合理?
假設(shè)場景如下:

  • 當(dāng)前有多個項目
  • 每個項目按功能拆分成多個微服務(wù)
  • 每個微服務(wù)有多個節(jié)點,節(jié)點信息包含ip和port等所在機(jī)器信息

如果人工處理,把這些信息交給某個人(注冊員)來用電腦存儲記錄,那么他有兩種選擇:1.建一個文件夾把所有節(jié)點信息存入。2.建多個文件夾把節(jié)點信息分類存儲。
很明顯第一種一但信息多了,找太費勁了,所以一定會用第二種,存儲的信息大概如下:

注冊員記錄

這樣存的好處一看便知:方便歸納,方便查找
這時候再回頭看zookeeper的數(shù)據(jù)結(jié)構(gòu),就能理解為什么要這么設(shè)計了:
zookeeper數(shù)據(jù)結(jié)構(gòu)

上例中分為文件夾txt文件,zookeeper統(tǒng)稱為節(jié)點,任何節(jié)點都可以包含信息(文件),任何節(jié)點也可以包含子節(jié)點(文件夾),節(jié)點本身可以create、delete來創(chuàng)建刪除,也可以通過set、get存放獲取數(shù)據(jù)(所以也是一個key-value模式)。

接下來展示兩個使用zookeeper做注冊中心的數(shù)據(jù)存儲

  • spring使用zookeeper做注冊中心:


    spring-zookeeper
  • dubbo使用zookeeper做注冊中心:


    dubbo-zookeeper

可以看到有些信息直接用節(jié)點名來存儲,有些信息用set存儲,其實隨意,怎么存怎么取唄,但節(jié)點名太長如果再有子節(jié)點就很不方便

問題?

以上只能算是一個基礎(chǔ)版的注冊中心,可以注冊可以獲取僅此而已,但在微服務(wù)開發(fā)中會有這樣的場景,比如1個A服務(wù),2個B服務(wù),A調(diào)用B且從zookeeper處獲取了2個B服務(wù)的位置并緩存起來,可以選一個調(diào)用或者負(fù)載均衡輪番調(diào)用,這時突然有一個B服務(wù)宕機(jī)了,就會出現(xiàn)問題:

  • 問題1,此時zookeeper中存儲的兩個B的節(jié)點信息有一個是錯誤信息,因為服務(wù)已經(jīng)停了,你不可能依靠宕機(jī)的B主動來刪除,那么zookeeper如何感知B的掉線?
  • 問題2,即使zookeeper中的宕機(jī)節(jié)點信息刪除了,由于A緩存了B的信息,無法知道有一個B服務(wù)宕機(jī)了,那么如何通知A?
長鏈接和臨時節(jié)點

zookeeper的解決問題1是這樣的,作為一個注冊中心,他要求所有客戶端與其保持長鏈接,一次鏈接稱為一次會話,并通過心跳(ping,pong)不斷的檢驗會話是否依然存在,如果某個鏈接長時間不響應(yīng),那就說明服務(wù)掉線了,就可以把其注冊的節(jié)點信息刪除。
實際上刪除的方式并不是主動去刪,而是zookeeper給每個節(jié)點添加一個是否是臨時節(jié)點的屬性,并規(guī)定一個臨時節(jié)點在會話結(jié)束后自動刪除,所以每個服務(wù)注冊的都是臨時節(jié)點信息,如果服務(wù)長時間不響應(yīng)代表會話結(jié)束,臨時節(jié)點就會被自動刪除(后臺刪除,有一定延時)

建立連接創(chuàng)建臨時節(jié)點

會話結(jié)束刪除臨時節(jié)點

通過長鏈接和臨時節(jié)點的設(shè)計者,解決了zookeeper如何感知某服務(wù)的掉線,并自動刪除掉線服務(wù)注冊的節(jié)點信息

創(chuàng)建臨時節(jié)點的方法:

create -e /ephemeral data // 其中-e代表臨時節(jié)點,非臨時節(jié)點會一直保存

那么問題1解決了,問題2:如何通知其它服務(wù)某服務(wù)的掉線事件

監(jiān)聽通知機(jī)制

問題2解決也很簡單,既然有了長鏈接保持會話,B服務(wù)掉了,zookeeper直接把掉線事件推送給A就行了。
但關(guān)鍵是zookeeper中有那么多節(jié)點,比如A只依賴B,那A只關(guān)心B服務(wù)的所有節(jié)點掉沒掉,其它節(jié)點跟我也沒關(guān)系啊,推送過多的垃圾信息浪費啊,但zookeeper也不知道誰關(guān)心誰啊,所以就推出了監(jiān)聽通知機(jī)制

  • 監(jiān)聽: 你告訴zookeeper你關(guān)心那些節(jié)點
  • 通知: 你關(guān)心的節(jié)點發(fā)生變化,zookeeper負(fù)責(zé)通知你

通過這種方式,A監(jiān)聽B節(jié)點,當(dāng)B節(jié)點下有某個節(jié)點掉了zookeeper通知A,到此問題解決~
為了實現(xiàn)各種維度的監(jiān)控,zookeeper提供了多種監(jiān)聽方法:

  • 針對節(jié)點監(jiān)聽
get  -w  /path   // 注冊監(jiān)聽的同時獲取數(shù)據(jù)
stat -w /path   // 對節(jié)點進(jìn)行監(jiān)聽,且獲取元數(shù)據(jù)信息
  • 針對目錄監(jiān)聽,目錄的變化,會觸發(fā)事件,且一旦觸發(fā),對應(yīng)的監(jiān)聽也會被移除,后續(xù)對節(jié)點的創(chuàng)建沒有觸發(fā)監(jiān)聽事件
ls -w /path
  • 針對遞歸子目錄的監(jiān)聽
ls -R -w /path : -R 區(qū)分大小寫,一定用大寫 

注:zookeeper的監(jiān)聽是一次性的

總結(jié)

通過文件系統(tǒng)數(shù)據(jù)結(jié)構(gòu),長鏈接,臨時節(jié)點監(jiān)聽通知機(jī)制,zookeeper就形成了一個完整的注冊中心。
實際使用我們可以用zookeeper配合spring來做微服務(wù)的注冊中心,dubbo也可以使用zookeeper做注冊中心,這兩個都有相關(guān)的支持。
類似的注冊中心還有eureka,nacos,目前主流應(yīng)該是nacos吧,我現(xiàn)在用的也是nacos。

分布式配置中心

這個其實很好理解了,集群里的每個節(jié)點都有一樣的配置,為了避免每個節(jié)點都去維護(hù),還不如直接存在一個配置中心里,zookeeper的文件系統(tǒng)結(jié)構(gòu)可以使用各種配置信息分類存儲,還可以通過監(jiān)聽機(jī)制來實時感知配置的變化。

發(fā)布/訂閱

有了監(jiān)聽通知機(jī)制,自然就支持了分布式發(fā)布/訂閱

分布式鎖

分布式應(yīng)用中還有一個需要協(xié)調(diào)的場景就是分布式鎖,比如給某個商品減庫存,分布式鎖的作用就是保證隸屬于不同服務(wù)所有線程同時要操作商品庫存時,只有拿到鎖的那個線程才可以操作,或者可以理解為只能排著隊一個個操作。
實際應(yīng)用中可能會有多個鎖,通過文件系統(tǒng)可以對鎖歸類整理,如下:


順序編號

如何實現(xiàn)吶,比如有A,B兩個線程都要操作商品1,B看/商品1鎖節(jié)點下沒有子節(jié)點,好,那B建一個臨時子節(jié)點B排,此時A線程又來了,看到/商品1鎖下有一個節(jié)點了,那說明B在操作商品1了,那就建一個臨時節(jié)點A排,并監(jiān)聽B排節(jié)點,B線程操作完之后刪除了B排,此時A線程感知到了,證明A排上號獲得鎖了,A開始操作商品1,操作完了刪除A排。

商品庫存鎖

但是問題出現(xiàn)了,又來了個線程C,此時下面兩個節(jié)點A排,B排都在,線程C進(jìn)來監(jiān)聽誰啊,我們都只到肯定是A,但問題是C不知道這些信息,A排、B排對它來講都是一樣的,沒有辦法選擇了。
問題的根源是C不知道A和B誰先誰后,所以如果我們是zookeeper設(shè)計者,那么就要給節(jié)點加順序了:一個類似mysql自增主鍵的可以標(biāo)識先后的順序值。
這樣的話,C進(jìn)程根據(jù)順序就知道他要跟在A后面了

分布式鎖

實際上這種結(jié)構(gòu)就好比排隊買票,排隊就有了順序,隊伍的第一個人(順序號最小)就是正在買票的人(獲取到了鎖),隊伍里的每個人都只關(guān)注前面的人即可,如果前面的人走了,證明排到自己了(獲得鎖),而自己買完票自然也要離開隊伍(刪除節(jié)點)。

總結(jié)

以上通過注冊中心,配置中心分布式鎖等場景介紹了zookeeper主要特性:文件系統(tǒng)數(shù)據(jù)結(jié)構(gòu),CS長鏈接監(jiān)聽通知機(jī)制,臨時節(jié)點有序節(jié)點等主要特性,相信到此就對zookeeper有個基本的概念了。
粗暴總結(jié)一下不過就是一個:可監(jiān)控的文件系統(tǒng)結(jié)構(gòu)的數(shù)據(jù)庫,當(dāng)你的分布式應(yīng)用有數(shù)據(jù)要統(tǒng)一存儲,只要它的結(jié)構(gòu)合適,就可以用它存。

其他

除此之外zookeeper還有一些其它使用場景和特性,簡單提一下就不細(xì)說了:

持久化

zookeeper的持久化是日志+快照,這點和redis很相似

集群

zookeeper作為配置中心肯定不能輕易掛掉,為了高可用,提供了集群功能,并可以實現(xiàn)選舉,這也是一個大課題,篇幅有限,以后單說

樂觀鎖

節(jié)點信息包含版本號,做樂觀鎖再合適不過了(mysql為了實現(xiàn)樂觀鎖往往還要建一個版本號字段)

TTL

類似redis那種可以設(shè)置過期時間的節(jié)點,但是好像不準(zhǔn)確

權(quán)限控制

zookeeper作為數(shù)據(jù)存儲中心,如果暴露在非局域網(wǎng)中那就有點太可怕了,所以加了針對節(jié)點的權(quán)限控制,有些節(jié)點需要登錄才訪問,有些節(jié)點需要秘鑰,有些限制固定ip訪問,總之就是像要保證數(shù)據(jù)的安全

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1.Zookeeper簡介 1.1 分布式系統(tǒng)面臨的問題 分布式系統(tǒng)就是將一個獨立的系統(tǒng)拆分成很多個模塊,每個子模...
    HelloWorld打斷點閱讀 764評論 0 0
  • 場景一 有這樣一個場景:系統(tǒng)中有大約100w的用戶,每個用戶平 均有3個郵箱賬號,每隔5分鐘,每個郵箱賬需要收取1...
    小波同學(xué)閱讀 783評論 1 5
  • 出現(xiàn)背景 ZooKeeper是一款開源的針對分布式應(yīng)用 的 分布式協(xié)調(diào)服務(wù)。 使用一種類似于文件系統(tǒng)的目錄樹結(jié)構(gòu)的...
    GeekerLou閱讀 842評論 0 0
  • 什么是Zookeeper? 總結(jié)一句話,就是: ZooKeeper是一種分布式協(xié)調(diào)服務(wù),可以實現(xiàn)分布式應(yīng)用配置管理...
    Java程序員YY閱讀 263評論 0 3
  • 首先認(rèn)識ZooKeeper ZooKeeper—譯名為“動物園管理員”。動物園里當(dāng)然有好多的動物,游客可以根據(jù)動物...
    沉默羔羊121閱讀 10,198評論 0 2

友情鏈接更多精彩內(nèi)容