簡介
分布式系統和應用,不僅能提供更強的計算能力,還能為我們提供更好的容災性和擴展性。
ZooKeeper是Google的Chubby項目的開源實現,它曾經作為Hadoop的子項目,在大數據領域得到廣泛應用
ZooKeeper以Fast Paxos算法為基礎,同時為了解決活鎖問題,對Fast Paxos算法進行了優化,因此也可以廣泛用於大數據之外的其他分布式系統,為大型分布式系統提供可靠的協作處理功能。
Apache ZooKeeper旨在減輕構建健壯的分布式系統的任務。ZooKeeper基於分布式計算的核心概念而設計,主要目的是給開發人員提供一套容易理解和開發的接口,從而簡化分布式系統構建的任務。
ZooKeeper的設計保證了其健壯性,這就使得應用開發人員可以更多關注應用本身的邏輯,而不是協同工作上。ZooKeeper從文件系統API
ZooKeeper從文件系統API得到啟發,提供一組簡單的API,使得開發人員可以實現通用的協作任務,包括選舉主節點、管理組內成員關系、管理元數據等。
ZooKeeper包括一個應用開發庫(主要提供Java和C兩種語言的API)和一個用Java實現的服務組件。ZooKeeper的服務組件運行在一組專用服務器之上,實現的服務組件。
當你決定使用ZooKeeper來設計應用時,最好將應用數據和協同數據獨立開。整個ZooKeeper服務所管理的就是后者(協同數據,或稱元數據)
ZooKeeper的使命
它可以在分布式系統中協作多個任務。
一個協作任務是指一個包含多個進程的任務。這個任務可以是為了協作或者是為了管理競爭。
協作意味着多個進程需要一同處理某些事情,一些進程采取某些行動使得其他進程可以繼續工作
Apache HBase
HBase是一個通常與Hadoop一起使用的數據存儲倉庫。在HBase中,ZooKeeper用於選舉一個集群內的主節點,以便跟蹤可用的服務器,並保存集群的元數據。
Apache Kafka
Kafka是一個基於發布-訂閱(pub-sub)模型的消息系統。其中ZooKeeper用於檢測崩潰,實現主題(topic)的發現,並保持主題的生產和消費狀態。
Apache Solr
Solr是一個企業級的搜索平台。Solr的分布式版本命名為SolrCloud,它使用ZooKeeper來存儲集群的元數據,並協作更新這些元數據。
Yahoo!Fetching Service
Yahoo!Fetching Service是爬蟲實現的一部分,通過緩存內容的方式高效地獲取網頁信息,同時確保滿足網頁服務器的管理規則(比如robots.txt文件)。該服務采用ZooKeeper實現主節點選舉、崩潰檢測和元數據存儲。
Facebook Messages
Facebook推出的這個應用(http://on.fb.me/1a7uViK )集成了email、短信、Facebook聊天和Facebook收件箱等通信通道。該應用將ZooKeeper作為控制器,用來實現數據分片、故障恢復和服務發現等功能。
Zookeep的客戶端API功能強大,其中包括:
- 保障強一致性、有序性和持久性。
- 實現通用的同步原語的能力。
- 在實際分布式系統中,並發往往導致不正確的行為。ZooKeeper提供了一種簡單的並發處理機制。
ZooKeeper之前的其他一些系統采用分布式鎖管理器或者分布式數據庫來實現協作。實際上,ZooKeeper也從這些系統中借鑒了很多概念。
但是,ZooKeeper的設計更專注於任務協作,並不提供任何鎖的接口或通用存儲數據接口。同時,ZooKeeper沒有給開發人員強加任何特殊的同步原語,使用起來非常靈活。
ZooKeeper可以讓開發人員更專注於其應用本身的邏輯而不是神秘的分布式系統概念
ZooKeeper不適用的場景
整個ZooKeeper的服務器集群管理着應用協作的關鍵數據。ZooKeeper不適合用作海量數據存儲。最佳實踐還是應該將應用數據和協同數據獨立開
ZooKeeper中實現了一組核心操作,通過這些可以實現很多常見分布式應用的任務。ZooKeeper並沒有為你實現這些任務(應用服務采用主節點方式或進程響應跟蹤方式?),也沒有為應用實現主節點選舉,或者進程存活與否的跟蹤的功能,但是,ZooKeeper提供了實現這些任務的工具,對於實現什么樣的協同任務,由開發人員自己決定。
通過ZooKeeper構建分布式系統
分布式系統是同時跨越多個物理主機,獨立運行的多個軟件組件所組成的系統
采用分布式去設計系統有很多原因,
分布式系統能夠利用多處理器的運算能力來運行組件,比如並行復制任務。
一個系統也許由於戰略原因,需要分布在不同地點,比如一個應用由多個不同地點的服務器提供服務。
使用一個獨立的協調組件有幾個重要的好處:
首先,我們可以獨立地設計和實現該組件,這樣獨立的組件可以跨多個應用共享。
其次,系統架構師可以簡化協作方面的工作,這些並不是瑣碎的小事
最后,系統可以獨立地運行和協作這些組件,獨立這樣的組件,也簡化了生產環境中解決實際問題的任務。
軟件組件以操作系統的進程方式運行,很多時候還涉及多線程的執行。因此ZooKeeper的服務端和客戶端也是以進程的方式運行,一個單獨的物理主機(無論是一個獨立主機還是一個虛擬環境中的操作系統)上運行一個單獨的應用進程,盡管進程可能采用多線程運行的方式,以便利用現代處理器的多核(multicore)處理能力。
分布式系統中的進程通信有兩種選擇:直接通過網絡進行信息交換,或讀寫某些共享存儲。
ZooKeeper使用共享存儲模型來實現應用間的協作和同步原語。
對於共享存儲本身,又需要在進程和存儲間進行網絡通信。我們強調網絡通信的重要性,因為它是分布式系統中並發設計的基礎。
在真實的系統中,我們需要特別注意以下問題:
- 消息延遲:消息傳輸可能會發生任意延遲
- 處理器性能:操作系統的調度和超載也可能導致消息處理的任意延遲。
- 時鍾偏移:處理器時鍾並不可靠,它們之間也會發生任意的偏移。因此,依賴處理器時鍾也許會導致錯誤的決策
關於這些問題的一個重要結果是,在實際情況中,我們很難判斷一個進程是崩潰了還是某些因素導致了延時
ZooKeeper的精確設計簡化了這些問題的處理,ZooKeeper並不是完全消除這些問題,而是將這些問題在應用服務層面上完全透明化,使得這些問題更容易處理。
ZooKeeper實現了重要的分布式計算問題的解決方案,直觀為開發人員提供某種程度上實現的封裝,至少這是我們一直希望的。
示例:主-從應用
一般在這種架構中,主節點進程負責跟蹤從節點狀態和任務的有效性,並分配任務到從節點。
要實現主-從模式的系統,我們必須解決以下三個關鍵問題:
- 主節點崩潰:無法分配新的任務或重新分配已失敗的任務。
- 從節點崩潰:如果從節點崩潰,已分配的任務將無法完成
- 通信故障:主節點和從節點之間無法進行信息交換,從節點將無法得知新任務分配給它。
為了處理這些問題,
之前的主節點出現問題時,系統需要可靠地選舉一個新的主節點,
判斷哪些從節點有效,並判定一個從節點的狀態相對於系統其他部分是否失效。
主節點失效
主節點失效時,我們需要有一個備份主節點(backup master)。
備份主節點接管主要主節點的角色,進行故障轉移
狀態恢復:新的主要主節點需要能夠恢復到舊的主要主節點崩潰時的狀態
對於主節點狀態的可恢復性,我們不能依靠從已經崩潰的主節點來獲取這些信息,而需要從其他地方獲取,也就是通過ZooKeeper來獲取。
錯誤的假設:假如主節點有效,備份主節點卻認為主節點已經崩潰
例如主節點負載很高,導致消息任意延遲,備份主節點將會接管成為主節點的角色,執行所有必需的程序,最終可能以主節點的角色開始執行,成為第二個主要主節點。
更糟的是,如果一些從節點無法與主要主節點通信,如由於網絡分區(network partition)錯誤導致,這些從節點可能會停止與主要主節點的通信,而與第二個主要主節點建立主-從關系。針對這個場景中導致的問題,我們一般稱之為腦裂(split-brain):系統中兩個或者多個部分開始獨立工作,導致整體行為不一致性。我們需要找出一種方法來處理主節點失效的情況,關鍵是我們需要避免發生腦裂的情況。
從節點失效
如果從節點崩潰了,所有已派發給這個從節點且尚未完成的任務需要重新派發。
首要需求是讓主節點具有檢測從節點的崩潰的能力。
一個從節點崩潰時,從節點也許執行了部分任務,也許全部執行完,但沒有報告結果。如果整個運算過程產生了其他作用,我們還有必要執行某些恢復過程來清除之前的狀態。
通信故障
如果一個從節點與主節點的網絡連接斷開,比如網絡分區(network partition)導致,重新分配一個任務可能會導致兩個從節點執行相同的任務。
如果一個任務允許多次執行,我們在進行任務再分配時可以不用驗證第一個從節點是否完成了該任務。
如果一個任務不允許,那么我們的應用需要適應多個從節點執行相同任務的可能性。
通信故障導致的另一個重要問題是對鎖等同步原語的影響。
首先,客戶端可以告訴ZooKeeper某些數據的狀態是臨時狀態(ephemeral);
其次,同時ZooKeeper需要客戶端定時發送是否存活的通知,如果一個客戶端未能及時發送通知,那么所有從屬於這個客戶端的臨時狀態的數據將全部被刪除。
通過這兩個機制,在崩潰或通信故障發生時,我們就可以預防客戶端獨立運行而發生的應用宕機。
任務總結
- 主節點選舉
- 崩潰檢測:檢測從節點崩潰或失去連接的能力
- 組成員關系管理:主節點必須具有知道哪一個從節點可以執行任務的能力
- 元數據管理:主節點和從節點必須具有通過某種可靠的方式來保存分配狀態和執行狀態的能力。
理想的方式是,以上每一個任務都需要通過原語的方式暴露給應用,對開發者完全隱藏實現細節。
ZooKeeper提供了實現這些原語的關鍵機制,因此,開發者可以通過這些實現一個最適合他們需求、更加關注應用邏輯的分布式應用。
分布式協作的難點
配置信息也許發生了變化,我們可以停止所有進程,重新分發配置信息的文件,然后重新啟動,但是重新配置就會延長應用的停機時間。
組成員關系的問題,當負載變化時,我們希望增加或減少新機器和進程。
當你在開發分布式應用時,你就會遇到真正困難的問題,你就不得不面對故障,如崩潰、通信故障等各種情況。這些問題會在任何可能的點突然出現,甚至無法列舉需要處理的所有的情況。
拜占庭將軍問題(Byzantine Faults)
在獨立主機上運行的應用與分布式應用發生的故障存在顯著的區別:
在分布式應用中,可能會發生局部故障,
當獨立主機崩潰,這個主機上運行的所有進程都會失敗,
如果是獨立主機上運行多個進程,一個進程執行的失敗,其他進程可以通過操作系統獲得這個故障,操作系統提供了健壯的多進程消息通信的保障。
在分布式環境中這一切發生了改變:如果一個主機或進程發生故障,其他主機繼續運行,並會接管發生故障的進程,為了能夠處理故障進程,這些仍在運行的進程必須能夠檢測到這個故障,無論是消息丟失或發生了時間偏移。
FLP(由其作者命名:Fischer,Lynch,Patterson),這個結論證明了在異步通信的分布式系統中,進程崩潰,所有進程可能無法在這個比特位的配置上達成一致 [1] 。
類似的定律稱為CAP,表示一致性(Consistency)、可用性(Availability)和分區容錯性(Partitiontolerance),該定律指出,當設計一個分布式系統時,我們希望這三種屬性全部滿足,但沒有系統可以同時滿足這三種屬性 [2] 。
因此ZooKeeper的設計盡可能滿足一致性和可用性,當然,在發生網絡分區時ZooKeeper也提供了只讀能力。
因此,我們無法擁有一個理想的故障容錯的、分布式的、真實環境存在的系統來處理可能發生的所有問題。但我們還是可以爭取一個稍微不那么宏偉的目標。
首先,我們只好對我們的假設或目標適當放松,例如,我們可以假設時鍾在某種范圍內是同步的,我們也可以犧牲一些網絡分區容錯的能力並認為其一直是一致的,當一個進程運行中,也許多次因無法確定系統中的狀態而被認為已經發生故障。雖然這些是一些折中方案,而這些折中方案允許我們建立一些印象非常深刻的分布式系統。
ZooKeeper的成功和注意事項
不得不指出,完美的解決方案是不存在的,我們重申ZooKeeper無法解決分布式應用開發者面對的所有問題,而是為開發者提供了一個優雅的框架來處理這些問題。
多年以來,ZooKeeper在分布式計算領域進行了大量的工作。Paxos算法 [1] 和虛擬同步技術(virtual synchrony) [2]給ZooKeeper的設計帶來了很大影響,通過這些技術可以無縫地處理所發生的某些變化或情況,並提供給開發者一個框架,來應對無法自動處理的某些情況。
可以很容易地部署ZooKeeper集群,輕松通過這個集群開發應用,但實際上,在使用ZooKeeper時,有些情況ZooKeeper自身無法進行決策而是需要開發者自己做出決策,有些開發者並不完全了解這些。