經典分佈式論文閱讀:Zookeeper

本文是ZooKeeper論文的閱讀筆記,ZooKeeper用於協調分佈式系統中的進程,爲分佈式系統提供消息羣發、共享寄存器、分佈式鎖這些中心化的服務。node

分佈式系統中須要的協調服務包括:配置、組成員關係、領導選舉和鎖服務。ZooKeeper並無直接提供這些服務,由於更強的原語能夠用來實現較弱的原語,ZooKeeper提供了API供開發者實現本身的原語。ZooKeeper的API操做相似文件系統的層級結構上的免等待數據對象,同時保證全部操做的客戶端先進先出串行寫入。ZooKpeer使用管道架構實現高吞吐和低延遲,更新操做採用Zab保證線性,讀取操做在服務器本地進行,不須要肯定順序。觀察機制在數據更新以後通知客戶端,使得客戶端可以快速獲取最新數據。數據庫

ZooKeeper服務

ZooKeeper以庫的形式向客戶端提供API,庫也負責客戶端到ZooKeeper服務器的鏈接。ZooKeeper中的數據節點稱爲znode,以樹型命名空間組織。客戶端鏈接服務器後創建會話,經過會話句柄發送請求。bash

服務總覽

ZooKeeper給客戶端提供了數據對象的抽象(znode)。服務器

znode有兩種類型:架構

  • 常規:數據對象正常建立和刪除。
  • 臨時:建立對象的會話終止以後,對象會被刪除。

若是在建立文件的時候設置SEQUENTIAL標誌,那麼會在文件名後增長一個自動增長的計數器。ZooKeeper實現了觀測(watch)機制,可以在數據對象更新後通知客戶端,觀測只會觸發一次。分佈式

數據模型:ZooKeeper中的數據模型是隻支持全量讀寫的文件系統,znode保存應用程序的抽象概念,用來存儲配置、元數據等信息。ui

會話:客戶端鏈接ZooKeeper後創建會話,會話用來標識客戶端。spa

客戶端API

  • create(path, data, flags):建立一個路徑爲path的znode,將data[]保存到其中,返回新znode的名稱,flags用來設置znode類型:普通或者臨時,以及設置SEQUENTIAL標誌。
  • delete(path, version):若是版本匹配,刪除path對應的znode。
  • exists(path, watch):若是path對應的znode存在,那麼返回真,不然返回假。watch標誌讓客戶端觀測這個znode。
  • getData(path, watch):返回znode對應的數據和元數據,watch功能相似。
  • setData(path, data, version):若是版本匹配,將data[]寫入到path對應的znode中。
  • getChildren(path, watch):返回znode的子節點集合。
  • sync(path):等待目前全部未決的更新,path沒什麼用。

以上所有的方法提供了阻塞版本和非阻塞版本,若是傳入版本號爲-1,那麼不進行版本檢查。.net

ZooKeeper保證

ZooKeeper有兩項基本的順序保證日誌

  • 線性寫入:全部改變ZooKeeper狀態的更新都是串行的;
  • 客戶端先進先出:全部來自客戶端的請求按照先進先出順序執行。

能夠舉個例子演示這兩個保證如何保障系統運行。假設一個系統選舉主節點管理其餘節點,主節點隨後須要更新一些配置,而後通知其餘節點,要求:

  • 主節點在修改配置過程,不但願其餘節點訪問正在被修改的配置
  • 主節點在更新完成前崩潰,不但願其餘節點訪問這些破碎的配置

能夠設置一個readyznode解決,主節點能夠在配置前刪除,完成後從新創建。當其餘節點看到ready不存在時就不讀取配置。

可是還會存在問題:若是其餘節點看到ready後讀取配置,可是主節點隨即刪除開始修改配置,那麼其餘節點將獲得過期的配置。這個問題能夠採用觀測機制來解決,ready刪除後會及時通知其餘節點。

ZooKeeper兩個耐久性保證:

  • 若是大部分服務器都活躍,那麼服務就是可用的
  • 若是ZooKeeper成功響應了一個修改請求,只要大部分的節點均可以最終恢復,那麼修改就能夠在無數次故障中保持持久。

原語例子

  • 配置管理:只須要將配置保存在一個znode中,各個進程能夠經過觀測來獲取配置更新通知。
  • 會合:不少分佈式系統包含主節點和工做節點,可是節點的調度由調度器決定,能夠將主節點信息放在一個znode,供工做節點找到主節點。
  • 組成員關係:組成員進程上線以後能夠在組對應的znode之下建立對應的臨時子znode,成員進程退出以後臨時znode也被刪除,所以能夠經過組znode的子znode獲取組成員狀態。
  • 簡單鎖:鎖能夠建立一個對應的znode實現。若是建立成功,那麼獲取鎖。若是已經存在,那麼須要等待鎖被釋放(znode被刪除)後才能獲取鎖(建立znode)。
  • 無羊羣效應的簡單鎖:簡單鎖會出現大量進程競爭的狀況,能夠將鎖請求排序後,按次序分配鎖。
Lock
1   n = create(l + 「/lock-」, EPHEMERAL|SEQUENTIAL)
2   C = getChildren(l, false)
3   if n is lowest znode in C, exit
4   p = znode in C ordered just before n
5   if exists(p, true) wait for watch event
6   goto 2
複製代碼
Unlock
1   delete(n)
複製代碼
  • 讀寫鎖:寫鎖和普通鎖相似,和其餘的鎖互斥。
Write Lock
1   n = create(l + 「/write-」, EPHEMERAL|SEQUENTIAL)
2   C = getChildren(l, false)
3   if n is lowest znode in C, exit
4   p = znode in C ordered just before n
5   if exists(p, true) wait for event
6   goto 2
複製代碼

讀鎖之間能夠互相兼容,和寫鎖互斥。

Read Lock
1   n = create(l + 「/read-」, EPHEMERAL|SEQUENTIAL)
2   C = getChildren(l, false)
3   if no write znodes lower than n in C, exit
4   p = write znode in C ordered just before n
5   if exists(p, true) wait for event
6   goto 3
複製代碼
  • 雙柵欄:雙柵欄用來保證多個客戶端的計算同時開始和同時結束。客戶端開始計算以前添加znode到柵欄對應的znode之下,結束計算以後刪除znode。客戶端須要等待柵欄znode的子znode數量到達必定閾值後才能開始計算,客戶端能夠等待一個特殊的ready的znode的建立,當數量到達閾值後建立。客戶端退出的時候須要等待子znode所有被刪除,一樣能夠經過刪除ready刪除。

ZooKeeper應用

  • 解析服務:在雅虎的爬蟲系統的解析服務中,主節點須要告知解析節點系統配置,解析節點須要報告本身的狀態。所以,解析服務使用ZooKeeper管理配置領導選舉。下圖是系統讀寫操做狀況,能夠發現讀取操做佔大頭。

  • Katta:Katta是一個分佈式索引,主節點將分片分配給從節點並追蹤進度,主要使用ZooKeeper進行組成員關係管理領導選舉配置管理
  • 雅虎消息中介:雅虎消息中介負責無數話題下的消息的發佈和接收,這些話題分佈在多個服務器上,每一個服務器採用主從備份。系統的znode結構以下圖所示,相似於shutdownmigration_prohibited是系統的配置信息,nodes保存了屬於組成員的服務器信息,而topics保存了負責具體話題對應的主服務器已經從服務器,另外在主節點奔潰後須要領導選舉

ZooKeeper實現

ZooKeeper的組件以下圖所示,ZooKeeper的數據副本保存在每個服務器上,寫操做須要經過一致性協議提交到數據庫,而讀取請求能夠直接訪問服務器本地數據庫得到。ZooKeeper在應用修改到數據庫以前會寫入到磁盤,故障後採用快照加日誌的方式進行故障。根據一致協議,寫入請求會轉發到領導(leader)節點。

請求處理器

請求處理器收到寫入請求以後,會將其轉換爲冪等的事務,根據請求內容計算出新的數據、版本號和時間戳,等待應用到數據庫中。

原子廣播

ZooKeeper使用Zab做爲原子廣播協議,使用簡單的多數認同達成一致性。Zab保證廣播發送和接受的順序是一致的,領導節點廣播以前須要確保已經收到了前一個領導的廣播。

多副本數據庫

當服務器故障後,使用週期性的快照和快照以後的日誌恢復。建立快照的時候並不須要鎖定,由於事務都是冪等的,所以再次應用已經應用的修改沒有影響。

客戶端-服務器交互

當服務器執行一個寫入操做後,會通知觀測的客戶端並清除觀測,每一個服務器只負責通知本身鏈接的客戶端。每一個讀取請求對應着一個zxid,對應服務器上看到的最後一個寫入事務的ID。由於讀取是在服務器本地進行,可能在讀取以前的一些寫入沒有同步到客戶端鏈接的服務器,ZooKeeper提供了sync操做,保證sync以後的讀取操做都可以得到發生在sync以前的寫入結果。客戶端會從服務器獲取最新zxidzxid另一個做用就是保證客戶端在切換服務器後,新服務器看到視圖不能比客戶端以前看到的視圖落後,也就是服務器zxid不能早於客戶端的zxid。若是檢測客戶端故障,會話是有超時時間的,客戶端在沒有活動期間也要發送心跳避免超時。

參考文獻

  1. Hunt, Patrick, et al. "ZooKeeper: Wait-free Coordination for Internet-scale Systems." USENIX annual technical conference. Vol. 8. No. 9. 2010.
相關文章
相關標籤/搜索