學習zookeeper

最近研究了一下zookeeper(后續(xù)以zk簡稱),對于一個自認為泡在服務器領(lǐng)域多年的老油條來說,現(xiàn)在才開始關(guān)注zk這個東西,其實有點晚了,但沒辦法,以前的工作經(jīng)歷讓我壓根用不到這個玩意。只是最近因為要考慮做ledisdb的cluster方案,以及重新考慮mixer的協(xié)調(diào)管理,才讓我真正開始嘗試去了解zk。

什么是zookeeper

根據(jù)官網(wǎng)的介紹,zookeeper是一個分布式協(xié)調(diào)服務,主要用來處理分布式系統(tǒng)中各系統(tǒng)之間的協(xié)作問題的。

其實這么說有點抽象,初次接觸zk,很多人真不知道用它來干啥,你可以將它想成一個總控節(jié)點(當然它能用多機實現(xiàn)自身的HA),能對所有服務進行操作。這樣就能實現(xiàn)對整個分布式系統(tǒng)的統(tǒng)一管理。

譬如我現(xiàn)在有n臺機器,需要動態(tài)更新某一個配置,一些做法可能是通過puppet或者salt將配置先分發(fā)到不同機器,然后運行指定的reload命令。zk的做法可能是所有服務都監(jiān)聽一個配置節(jié)點,直接更改這個節(jié)點的數(shù)據(jù),然后各個服務就能收到更新消息,然后同步最新的配置,再自行reload了。

上面只是一個很簡單的例子,其實通過它并不能過多的體現(xiàn)zk的優(yōu)勢(沒準salt可能還更簡單),但zk不光只能干這些,還能干更awesome的事情。網(wǎng)上有太多關(guān)于zk應用場景一覽的文章了,這里就不詳細說明,后續(xù)我只會說一下自己需要用zk解決的棘手問題。

架構(gòu)

zk使用類paxos算法來保證其HA,每次通過選舉得到一個master用來處理client的請求,client可以掛載到任意一臺zk server上面,因為paxos這種是強一致同步算法,所以zk能保證每一臺server上面數(shù)據(jù)都是一致的。架構(gòu)如下:

?????????????????????????????????????????????????????????????????????
??????????????????????+-------------------------------+?????????????????????????
??????????????????????|???????????????????????????????|?????????????????????????
??????????????+----+--++??????????+----+---+????????+-+--+---+??????????????????
??????????????|?server?|??????????|?server?|????????|?server?|??????????????????
??????????????|????????+----------+?master?+--------+????????|??????????????????
??????????????+--^--^--+??????????+----^---+????????+----^---+??????????????????
?????????????????|??|??????????????????|?????????????????|??????????????????????
?????????????????|??|??????????????????|?????????????????|??????????????????????
?????????????????|??|??????????????????|?????????????????|??????????????????????
???????????+-----+??+-----+????????????+------+??????????+---------+????????????
???????????|??????????????|???????????????????|????????????????????|????????????
???????????|??????????????|???????????????????|????????????????????|????????????
??????+----+---+????????+-+------+?????????+--+-----+???????????+--+-----+??????
??????|?client?|????????|?client?|?????????|?client?|???????????|?client?|??????
??????+--------+????????+--------+?????????+--------+???????????+--------+??????

Data Model

zk內(nèi)部是按照類似文件系統(tǒng)層級方式進行數(shù)據(jù)存儲的,就像這樣:

                        +---+             
                        | / |             
                        +++-+             
                         ||               
                         ||               
          +-------+------++----+-------+  
          | /app1 |            | /app2 |  
          +-+--+--+            +---+---+  
            |  |                   |      
            |  |                   |      
            |  |                   |      
+----------++ ++---------+    +----+-----+
| /app1/p1 |  | /app1/p2 |    | /app2/p1 |
+----------+  +----------+    +----------+

對于任意一個節(jié)點,我們稱之為znode,znode有很多屬性,譬如Zxid(每次更新的事物ID)等,具體可以詳見zk的文檔。znode有ACL控制,我們可以很方便的設(shè)置其讀寫權(quán)限等,但個人感覺對于內(nèi)網(wǎng)小集群來說意義不怎么大,所以也就沒深入研究。

znode有一種Ephemeral Node,也就是臨時節(jié)點,它是session有效的,當session結(jié)束之后,這個node自動刪除,所以我們可以用這種node來實現(xiàn)對服務的監(jiān)控。譬如一個服務啟動之后就向zk掛載一個ephemeral node,如果這個服務崩潰了,那么連接斷開,session無效了,這個node就刪除了,我們也就知道該服務出了問題。

znode還有一種Sequence Node,用來實現(xiàn)序列化的唯一節(jié)點,我們可以通過這個功能來實現(xiàn)一個簡單地leader服務選舉,譬如每個服務啟動的時候都向zk注冊一個sequence node,誰最先注冊,zk給的sequence最小,這個最小的就是leader了,如果leader當?shù)袅?,那么具有第二小sequence node的節(jié)點就成為新的leader。

Znode Watch

我們可以watch一個znode,用來監(jiān)聽對應的消息,zk會負責通知,但只會通知一次。所以需要我們再次重新watch這個znode。那么如果再次watch之前,znode又有更新了,client不是收不到了嗎?這個就需要client不光要處理watch,同時也需要適當?shù)闹鲃觛et相關(guān)的數(shù)據(jù),這樣就能保證得到最新的消息了。也就是消息系統(tǒng)里面典型的推拉結(jié)合的方式。推只是為了提升性能,快速響應,而拉則為了更好的保證消息不丟失。

但是,我們需要注意一點,zk并不能保證client收到消息之后同時處理,譬如配置文件更新,zk可能通知了所有client,但client并不能全部在同一個時間同時reload,所以為了處理這樣的問題,我們需要額外的機制來保證,這個后續(xù)說明。

watch只能應用于data(通過get,exists函數(shù))以及children(通過getChildren函數(shù))。也就是監(jiān)控znode數(shù)據(jù)更新以及znode的子節(jié)點的改變。

API

zk的API時很簡單的,如下:

  • create
  • delete
  • exists
  • set data
  • get data
  • get chilren
  • sync

就跟通常的文件系統(tǒng)操作差不多,就不過多說明了。

Example

總的來說,如果我們不深入zk的內(nèi)部實現(xiàn),譬如paxos等,zk還是很好理解的,而且使用起來很簡單。通常我們需要考慮的就是用zk來干啥,而不是為了想引入一個牛的新特性而用zk。

Lock

用zk可以很方便的實現(xiàn)一個分布式lock,記得最開始做企業(yè)群組盤的時候,我需要實現(xiàn)一個分布式lock,然后就用redis來弄了一個,其實當時就很擔心redis單點當?shù)舻膯栴},如果那時候我就引入了zk,可能就沒這個擔心了。

官方文檔已經(jīng)很詳細的給出了lock的實現(xiàn)流程:

  1. create一個類似path/lock-n的臨時序列節(jié)點
  2. getChilren相應的path,注意這里千萬不能watch,不然驚群很恐怖的
  3. 如果1中n是最小的,則獲取lock
  4. 否則,調(diào)用exists watch到上一個比自己小的節(jié)點,譬如我現(xiàn)在n是5,我就可能watch node-4
  5. 如果exists失敗,表明前一個節(jié)點沒了,則進入步驟2,否則等待,直到watch觸發(fā)重新進入步驟2

Codis

最近在考慮ledisdb的cluster方案,本來也打算用proxy來解決的,然后就在想用zk來處理rebalance的問題,結(jié)果這時候codis橫空出世,發(fā)現(xiàn)不用自己整了,于是就好好的研究了一下codis的數(shù)據(jù)遷移問題。其實也很簡單:

  1. config發(fā)起pre migrate action
  2. proxy接收到這個action之后,將對應的slot設(shè)置為pre migrate狀態(tài),同時等待config發(fā)起migrate action
  3. config等待所有的proxy返回pre migrate之后,發(fā)起migrate action
  4. proxy收到migrate action,將對應的slot設(shè)置為migrate狀態(tài)

上面這些,都是通過zk來完成的,這里需要關(guān)注一下為啥要有pre migrate這個狀態(tài),如果config直接發(fā)起migrate,那么zk并不能保證proxy同一時間全部更新成migrate狀態(tài),所以我們必須有一個中間狀態(tài),在這個中間狀態(tài)里面,proxy對于特定的slot不會干任何事情,只能等待config將其設(shè)置為migrate。雖然proxy對于相應slot一段時間無法處理外部請求,但這個時間是很短的(不過此時config當?shù)袅司蛻K了)。config知道所有proxy都變成pre migrate狀態(tài)之后,就可以很放心的發(fā)送migrate action了。因為這時候,proxy只有兩種可能,變成migrate狀態(tài),能正常工作,仍然還是pre migrate狀態(tài),不能工作,也自然不會對數(shù)據(jù)造成破壞。

其實上面也就是一個典型的2PC,雖然仍然可能有隱患,譬如config當?shù)?,但并不會對實際數(shù)據(jù)造成破壞。而且config當?shù)袅宋覀円材芎芸熘獣圆⒅匦聠樱詥栴}不大。

總結(jié)

總的來說,zk的使用還是挺簡單的,只要我們知道它到底能用到什么地方,那zk就真的是分布式開發(fā)里面一把瑞士軍刀了。不過我挺不喜歡裝java那套東西,為了zk也沒辦法,雖然go現(xiàn)在也有etcd這些類zk的東西了,但畢竟還沒經(jīng)受過太多的考驗,所以現(xiàn)在還是老老實實的zk吧。

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

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

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