分佈式協調服務ZooKeeper

  • 概念

    ZooKeeper是一個分佈式的,開放源碼的分佈式應用程序協調服務,是Google的Chubby一個開源的實現。
    ZooKeeper中的角色主要有以下三類,如下表所示:
     

     
  • 系統模型

              

    一個ZK集羣稱爲“ZooKeeper ensemble”。
 

  • 數據模型

              

    每個節點在zookeeper中叫做znode,並且其有一個唯一的路徑標識,如/p_3節點的標識就爲/app1/p_3。
    Znode可以有子znode,並且znode裏可以存數據,但是EPHEMERAL類型的節點不能有子節點。
    Znode中的數據可以有多個版本,比如某一個路徑下存有多個數據版本,那麼查詢這個路徑下的數據就需要帶上版本。
    Znode 可以是臨時節點,一旦創建這個znode的客戶端與服務器失去聯繫,這個znode也將自動刪除,Zookeeper的客戶端和服務器通信採用長連接方式,每個客戶端和服務器通過心跳來保持連接,這個連接狀態稱爲session,如果znode是臨時節點,這個session失效,znode也就刪除了。
    Znode的目錄名可以自動編號,如App1已經存在,再創建的話,將會自動命名爲App2。 
    Znode可以被監控,包括這個目錄節點中存儲的數據的修改,子節點目錄的變化等,一旦變化可以通知設置監控的客戶端,這個功能是zookeeper對於應用最重要的特性,通過這個特性可以實現的功能包括配置的集中管理,集羣管理,分佈式鎖等等。  
     
  • 特點

    順序一致性:按照客戶端發送請求的順序更新數據。
    原子性:更新要麼成功,要麼失敗,不會出現部分更新。
    單一性 :無論客戶端連接哪個server,都會看到同一個視圖。
    可靠性:一旦數據更新成功,將一直保持,直到新的更新。
    實時性:客戶端會在一個確定的時間內得到最新的數據。
    
    

    注意:Zookeeper提供的一致性是弱一致性,首先數據的複製有如下規則:ZooKeeper確保對znode樹的每一個修改都會被複制到集合體中超過半數的機器上。那麼就有可能有節點的數據不是最新的而被客戶端訪問到。並且會有一個時間點,在集羣中是不一致的。

    也就是Zookeeper只保證最終一致性(數據可以在十幾秒Sync到各個節點),但是實時的一致性可以由客戶端調用自己來保證,通過調用sync()方法。

     
  • 應用場景

     1、命名服務(Naming Service)
      命名服務也是分佈式系統中比較常見的一類場景。在分佈式系統中,通過使用命名服務,客戶端應用能夠根據指定名字來獲取資源或服務的地址,提供者等信息。被命名的實體通常可以是集羣中的機器,提供的服務地址,遠程對象等等——這些我們都可以統稱他們爲名字(Name)。其中較爲常見 
    
      的就是一些分佈式服務框架中的服務地址列表。通過調用ZK提供的創建節點的API,能夠很容易創建一個全局唯一的path,這個path就可以作爲一個名稱。阿里巴巴集團開源的分佈式服務框架Dubbo中使用ZooKeeper來作爲其命名服務,維護全局的服務地址列表。在Dubbo實現中:服務提供者在啓動 
    
      的時候,向ZK上的指定節點/dubbo/${serviceName}/providers目錄下寫入自己的URL地址,這個操作就完成了服務的發佈。服務消費者啓動的時候,訂閱/dubbo/${serviceName}/providers目錄下的提供者URL地址, 並向/dubbo/${serviceName} /consumers目錄下寫入自己的URL地址。注意,所有 
    
      向ZK上註冊的地址都是臨時節點,這樣就能夠保證服務提供者和消費者能夠自動感應資源的變化。 另外,Dubbo還有針對服務粒度的監控,方法是訂閱/dubbo/${serviceName}目錄下所有提供者和消費者的信息。


    
    

    2、數據發佈與訂閱(配置中心)

    ZooKeeper中特有watcher註冊與異步通知機制,能夠很好的實現分佈式環境下不同系統之間的通知與協調,實現對數據變更的實時處理。使用方法通常是不同系統都對ZK上同一個znode進行註冊,監聽znode的變化(包括znode本身內容及子節點的),其中一個系統update了znode,那麼另一個系

    統能夠收到通知,並作出相應處理。

    心跳檢測機制:檢測系統和被檢測系統之間並不直接關聯起來,而是通過zk上某個節點關聯,大大減少系統耦合。

    系統調度模式:某系統有控制檯和推送系統兩部分組成,控制檯的職責是控制推送系統進行相應的推送工作。管理人員在控制檯作的一些操作,實際上是修改了ZK上某些節點的狀態,而ZK就把這些變化通知給他們註冊Watcher的客戶端,即推送系統,於是,作出相應的推送任務。

    工作彙報模式:一些類似於任務分發系統,子任務啓動後,到zk來註冊一個臨時節點,並且定時將自己的進度進行彙報(將進度寫回這個臨時節點),這樣任務管理者就能夠實時知道任務進度。

    使用zookeeper來進行分佈式通知和協調能夠大大降低系統之間的耦合。

    注意:Watcher是一次性的,觸發之後如果想繼續關注需要重新設置監聽。

    3、集羣管理

    可以構建集羣機器存活性監控系統。


    4、Master選舉

    在搜索系統中,如果集羣中每個機器都生成一份全量索引,不僅耗時,而且不能保證彼此之間索引數據一致。因此讓集羣中的Master來進行全量索引的生成,然後同步到集羣中其它機器。

    解決單點失效。

     

    5、 分佈式鎖

    分佈式鎖,這個主要得益於ZooKeeper爲我們保證了數據的一致性。鎖服務可以分爲兩類,一個是 保持獨佔,另一個是 控制時序。

    所謂保持獨佔,就是所有試圖來獲取這個鎖的客戶端,最終只有一個可以成功獲得這把鎖。通常的做法是把zk上的一個znode看作是一把鎖,通過create znode的方式來實現。所有客戶端都去創建 /distribute_lock 節點,最終成功創建的那個客戶端也即擁有了這把鎖。

    控制時序,就是所有視圖來獲取這個鎖的客戶端,最終都是會被安排執行,只是有個全局時序了。做法和上面基本類似,只是這裏 /distribute_lock 已經預先存在,客戶端在它下面創建臨時有序節點(這個可以通過節點的屬性控制:CreateMode.EPHEMERAL_SEQUENTIAL來指定)。如果我們在指

    定的路徑上創建順序節點,則Zookeeper會自動的在我們給定的path上加上一個順序編號。這個特性就是實現分佈式鎖的關鍵。假設我們有幾個節點共享一個資源,我們這幾個節點都想爭用這個資源,那我們就都向某個路徑創建臨時順序節點。然後順序最小的那個就獲得鎖,然後如果某個節點釋放

    了鎖,那順序第二小的那個就獲得鎖,以此類推,這樣一個分佈式的公平鎖就實現了。

     6、分佈式隊列

     隊列方面,簡單地講有兩種,一種是常規的先進先出隊列,另一種是要等到隊列成員聚齊之後的才統一按序執行。對於第一種先進先出隊列,和分佈式鎖服務中的控制時序場景基本原理一致,這裏不再贅述。 第二種隊列其實是在FIFO隊列的基礎上作了一個增強。通常可以在 /queue 這個znode下預先建立一個/queue/num 節點,並且賦值爲n(或者直接給/queue賦值n),表示隊列大小,之後每次有隊列成員加入後,就判斷下是否已經到達隊列大小,決定是否可以開始執行了。這種用法的典型場景是,分佈式環境中,一個大任務Task A,需要在很多子任務完成(或條件就緒)情況下才能進 行。這個時候,凡是其中一個子任務完成(就緒),那麼就去 /taskList 下建立自己的臨時時序節點(CreateMode.EPHEMERAL_SEQUENTIAL),當 /taskList 發現自己下面的子節點滿足指定個數,就可以進行下一步按序進行處理了。 

    7、負載均衡

    這裏說的負載均衡是指軟負載均衡。在分佈式環境中,爲了保證高可用性,通常同一個應用或同一個服務的提供方都會部署多份,達到對等服務。而消費者就須要在這些對等的服務器中選擇一個來執行相關的業務邏輯,其中比較典型的是消息中間件中的生產者,消費者負載均衡。

    消息中間件中發佈者和訂閱者的負載均衡,linkedin開源的KafkaMQ和阿里開源的 metaq都是通過zookeeper來做到生產者、消費者的負載均衡。這裏以metaq爲例如講下:

    生產者負載均衡:metaq發送消息的時候,生產者在發送消息的時候必須選擇一臺broker上的一個分區來發送消息,因此metaq在運行過程中,會把所有broker和對應的分區信息全部註冊到ZK指定節點上,默認的策略是一個依次輪詢的過程,生產者在通過ZK獲取分區列表之後,會按照brokerId和

    partition的順序排列組織成一個有序的分區列表,發送的時候按照從頭到尾循環往復的方式選擇一個分區來發送消息。


  • 安裝配置

    安裝略(分佈式,僞集羣)。
    
    啓動時需要至少啓動一半以上機器才行,否則會一直拋錯。 

    配置說明:

    tickTime=2000 
    initLimit=5 
    syncLimit=2 
    dataDir=/Users/apple/zookeeper/data 
    dataLogDir=/Users/apple/zookeeper/logs 
    clientPort=2181
    server.0=127.0.0.1:8880:7770 
    server.1=127.0.0.1:8881:7771 
    server.2=127.0.0.1:8882:7772

    tickTime:這個時間是作爲 Zookeeper 服務器之間或客戶端與服務器之間維持心跳的時間間隔,也就是每個 tickTime 時間就會發送一個心跳。

    initLimit::zookeeper集羣中的包含多臺server,,其中一臺爲leader,,集羣中其餘的server爲follower.。initLimit參數配置初始化連接時, follower和leader之間的最長心跳時間.。此時該參數設置爲5, 說明時間限制爲5倍tickTime, 即5*2000=10000ms=10s。

    syncLimit::該參數配置leader和follower之間發送消息, 請求和應答的最大時間長度.。此時該參數設置爲2,說明時間限制爲2倍tickTime,即4000ms。

    server.X=A:B:C 其中X是一個數字,表示這是第幾號server。A是該server所在的IP地址。B配置該server和集羣中的leader交換消息所使用的端口。 C配置選舉leader時所使用的端口。 如果配置的是僞集羣模式,則各個server的B、C參數必須不同。

    dataDir:就是把內存中的數據存儲成快照文件snapshot的目錄,同時myid也存儲在這個目錄下(myid中的內容爲本機server服務的標識)。寫快照不需要單獨的磁盤,而且是使用後臺線程進行異步寫數據到磁盤,因此不會對內存數據有影響。默認情況下,事務日誌也會存儲在這裏。建議同時配置參數dataLogDir,事務日誌的寫性能直接影響zk性能。

    dataLogDir:事務日誌輸出目錄。儘量給事務日誌的輸出配置單獨的磁盤或是掛載點,這將極大的提升ZK性能。 由於事務日誌輸出時是順序且同步寫到磁盤,只有從磁盤寫完日誌後纔會觸發followerleader發回事務日誌確認消息(zk事務採用兩階段提交),因此需要單獨磁盤避免隨機讀寫和磁盤緩存導致事務日誌寫入較慢或存儲在緩存中沒有寫入。

    clientPort:這個端口就是客戶端連接 Zookeeper 服務器的端口,Zookeeper 會監聽這個端口,接受客戶端的訪問請求。 

    minSessionTimeout~maxSessionTimeout:Session超時時間限制,如果客戶端設置的超時時間不在這個範圍,那麼會被強制設置爲最大或最小時間。


    注意:Session超時時間由服務器和ZK客戶端共同決定,建議最大超時時間不要太短,以避免頻繁連接超時情況(HBase使用的3分鐘)。
     

  • 客戶端工具

    Apache CuratorCurator是Netflix開源的一套ZooKeeper客戶端框架。Netflix在使用ZooKeeper的過程中發現ZooKeeper自帶的客戶端太底層,應用方在使用的時候需要自己處理很多事情,於是在它的基礎上包裝了一下, 提供了一套更好用的客戶端框架。 

    Curator幾個組成部分 :

    1、Client: 是ZooKeeper客戶端的一個替代品, 提供了一些底層處理和相關的工具方法
    2、Framework: 用來簡化ZooKeeper高級功能的使用, 並增加了一些新的功能, 比如管理到ZooKeeper集羣的連接,重試處理
    3、Recipes: 實現了通用ZooKeeper的recipe,該組件建立在Framework的基礎之上
          Curator實現ZooKeeper的所有recipe(除了兩段提交) ,比如領導者選舉、鎖、Barrier等等
    4、Utilities:各種ZooKeeper的工具類
    5、Errors:異常處理,連接, 恢復等
    6、Extensions: recipe擴展
     
    101tec:I0Itec-zkClient並不是一個非常大衆化的工具,也沒有提供完美的功能列表,不過它的簡單和已用對我們的一些常規應用,似乎已經足夠了。

    I0Itec特性一覽:

    1、提供了zookeeper斷鏈重連的特性:這個特性似乎每個開發者都會設計,而且代碼風格幾乎"如出一轍",在大部分zookeeper使用場景中,我們都要求它能夠在斷鏈的時候,重新建立連接,無論session失效與否。

    2、便捷的event監聽器機制:向znode節點註冊watch,每個開發者都使用過,儘管watch機制並不能確保數據變更的實時性,watch-event屬於“即發即失”,因爲我們需要得到event時候,再去註冊一遍,這也是一個非常繁瑣的事情,I0Itec- zkClient提供了event-listener的小技巧,可以幫助我們解     脫。

    3、zookeeper異常處理:ZooKeeper中繁多的Exception,以及每個Exception所需要關注的事情各有不同,你應該記得那一堆try-catch給你帶來的煩惱,I0Itec簡單的做了封裝。

    4、data序列化:簡單的data序列化(Serialzer/Deserialzer)
     

  • 可視化工具

    淘寶ZooKeeper監控工具taokeeper
    
    ZooKeeper for eclipse插件
    
    ZooInspector 
     
  • 集羣擴展

    高可用性是指一半以上機器可用時服務可用,比如5臺ZK服務器組成的集羣3臺可用時ZK服務可用。建議配置奇數臺ZK服務器。
    
    節點多了Leader選舉非常耗時,投票壓力增大,吞吐量下降,可以通過引入observer節點解決這個問題。
    
    
  • 性能

    Zookeeper 被設計爲高性能。但實際是否如此呢?在雅虎研發中心的 Zookeeper 開發團隊的研究結果表明的確如此。(參見下圖:Zookeeper 吞吐量隨讀寫比的變化)。在“讀”多於“寫”的應用程序中尤其地高性能,因爲“寫”會導致在所有的服務器間同步狀態。(“讀”多於“寫”是協調服務的典型場景。) 
     

  • ZooKeeper不適合做什麼


    不能做存儲使用,單個節點最大存儲4M。

發佈了27 篇原創文章 · 獲贊 2 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章