ZooKeeper簡介(淺入)

ens發表於2018-09-27

1. 產生背景

當今是個分散式、叢集、雲端計算等名詞滿天飛的時代。造成這種局面的一個重要因素就是,單一機器的處理能力已經不能滿足我們的需求,不得不採用由多臺機器組成的服務叢集。
服務叢集對外提供服務的過程中,可以分解處理壓力,在一定程度上打破效能瓶頸,並提高服務的可用性(不會因為一臺機器當機而造成服務不可用)。

ZooKeeper簡介(淺入)
上圖中有三臺機器,每臺機器跑同樣的一個應用程式。然後我們將這三臺機器通過網路將其連線起來,構成一個系統來為使用者提供服務,對使用者來說這個系統的架構是透明的,他感覺不到這個系統是一個什麼樣的架構。那麼我們就可以把這種系統稱作一個分散式系統。
那麼,問題來了:
(1)程式的執行往往依賴很多配置檔案,比如資料庫地址、黑名單控制、服務地址列表等,而且有些配置資訊需要頻繁地進行動態變更,這時候怎麼保證所有機器共享的配置資訊保持一致?

(2)如果有一臺機器掛掉了,其他機器如何感知到這一變化並接管任務?如果使用者激增,需要增加機器來緩解壓力,如何做到不重啟叢集而完成機器的新增?

(3)使用者數量增加或者減少,會出現有的機器資源使用率繁忙,有的卻空閒,如何讓每臺機器感知到其他機器的負載狀態從而實現負載均衡?

(4)在一臺機器上要多個程式或者多個執行緒操作同一資源比較簡單,因為可以有大量的狀態資訊或者日誌資訊提供保證,比如兩個A和B程式同時寫一個檔案,加鎖就可以實現。但是分散式系統怎麼辦?需要一個三方的分配鎖的機制,幾百臺worker都對同一個網路中的檔案寫操作,怎麼協同?還有怎麼保證高效的執行?

2. ZooKeeper的前世今生

分散式系統的很多難題,都是由於缺少協調機制造成的。

目前,在分散式協調技術方面做得比較好的就是Google的Chubby還有Apache的ZooKeeper。 有人會問既然有了Chubby為什麼還要弄一個ZooKeeper,難道Chubby做得不夠好嗎?主要是Chubby是非開源的,Google自家用。後來雅虎模仿Chubby開發出了ZooKeeper,也實現了類似的分散式鎖的功能,並且將ZooKeeper作為一種開源的程式捐獻給了Apache,那麼這樣就可以使用ZooKeeper所提供鎖服務。

至於這個神器為什麼叫ZooKeeper,與外國人一貫的幽默精神有關。眾所周知,外國人喜歡給用一個動物作為吉祥物,在IT界也不例外。比如:

負責大資料工作的Hadoop是一個黃色的大象

ZooKeeper簡介(淺入)
負責資料倉儲的Hive是一個虛擬蜂巢
ZooKeeper簡介(淺入)
負責資料分析的Apache Pig是一頭聰明的豬
ZooKeeper簡介(淺入)
負責管理Web容器的Tomcat是一隻雄貓……
ZooKeeper簡介(淺入)
那好,負責分散式協調工作的角色就叫ZooKeeper(動物園飼養員)吧。
ZooKeeper簡介(淺入)

3. Zookeeper是什麼

隨著大資料的火熱,Hxx(根據MapReduce,於是我們有了Hadoop;根據GFS,於是我們有了HDFS;根據BigTable,於是我們有了HBase)們已經變得耳熟能詳,現在作為一個開發人員如果都不知道這幾個名詞出門都好像不好意思跟人打招呼。

但實際上對我們這些非大資料開發人員而言,Zookeeper是比Hxx們可能接觸到更多的一個基礎服務。但是,無奈的是它一直默默的位於二線,從來沒有Hxx們那麼耀眼。那麼到底什麼是Zookeeper呢?Zookeeper可以用來幹什麼?我們將如何使用Zookeeper?Zookeeper又是怎麼實現的?

伴隨著Zookeeper有兩篇論文:一篇是Zab,就是介紹Zookeeper背後使用的一致性協議的(Zookeeper atomic broadcast protocol),還有一篇就是介紹Zookeeper本身的。在這兩篇論文裡都提到Zookeeper是一個分散式協調服務(a service for coordinating processes of distributed applications)。那分散式協調服務又是個什麼東西呢?首先我們來看“協調”是什麼意思。

說到協調,我首先想到的是北京很多十字路口的交通協管,他們手握小紅旗,指揮車輛和行人是不是可以通行。如果我們把車輛和行人比喻成執行在計算機中的單元(執行緒),那麼這個協管是幹什麼的?很多人都會想到,這不就是鎖麼?對,在一個併發的環境裡,我們為了避免多個執行單元對共享資料同時進行修改,造成資料損壞的情況出現,我們就必須依賴像鎖這樣的協調機制,讓有的執行緒可以先操作這些資源,然後其他執行緒等待。對於程式內的鎖來講,我們使用的各種語言平臺都已經給我們準備很多種選擇。就拿Java來說,有最普通不過的同步方法或同步塊:

public synchronized void sharedMethod(){
   //對共享資料進行操作
}
複製程式碼

使用了這種方式後,多個執行緒對sharedMethod進行操作的時候,就會協調好步驟,不會對sharedMethod裡的資源進行破壞,產生不一致的情況。
這是最簡單的協調方法,但有的時候我們可能需要更復雜的協調。比如我們常常為了提高效能,我們使用讀寫鎖。因為大部分時候我們對資源是讀多寫少,而如果不管三七二十一全部使用排他的寫鎖,那麼效能有可能就會受到影響。還是用java舉例:

public class SharedSource{
   private ReadWriteLock rwlock = new ReentrantReadWriteLock();
   private Lock rlock = rwlock.readLock();
   private Lock wlock = rwlock.writeLock();

   public void read(){
      rlock.lock();
      try{
         //讀取資源
      }finally{
         rlock.unlock();
      }
   }
   
   public void write(){
     wlock.lock();
     try{
        //寫資源
     }finally{
        wlock.unlock();
     }
   }

}
複製程式碼

我們在程式內還有各種各樣的協調機制(一般我們稱之為同步機制)。現在我們大概瞭解了什麼是協調了,但是上面介紹的協調都是在程式內進行協調。在程式內進行協調我們可以使用語言,平臺,作業系統等為我們提供的機制。那麼如果我們在一個分散式環境中呢?也就是我們的程式執行在不同的機器上,這些機器可能位於同一個機架,同一個機房又或不同的資料中心。在這樣的環境中,我們要實現協調該怎麼辦?那麼這就是分散式協調服務要乾的事情。

ok,可能有人會講,這個好像也不難。無非是將原來在同一個程式內的一些原語通過網路實現在分散式環境中。是的,表面上是可以這麼說。但分散式系統中,說往往比做容易得多。在分散式系統中,所有同一個程式內的任何假設都不存在:因為網路是不可靠的。

比如,在同一個程式內,你對一個方法的呼叫如果成功,那就是成功(當然,如果你的程式碼有bug那就另說了),如果呼叫失敗,比如丟擲異常那就是呼叫失敗。在同一個程式內,如果這個方法先呼叫先執行,那就是先執行。但是在分散式環境中呢?

由於網路的不可靠,你對一個服務的呼叫失敗了並不表示一定是失敗的,可能是執行成功了,但是響應返回的時候失敗了。還有,A和B都去呼叫C服務,在時間上A還先呼叫一些,B後呼叫,那麼最後的結果是不是一定A的請求就先於B到達呢? 這些本來在同一個程式內的種種假設我們都要重新思考,我們還要思考這些問題給我們的設計和編碼帶來了哪些影響。還有,在分散式環境中為了提升可靠性,我們往往會部署多套服務,但是如何在多套服務中達到一致性,這在同一個程式內很容易解決的問題,但在分散式環境中確實一個大難題。

所以分散式協調遠遠比同一個程式裡的協調複雜得多,所以類似Zookeeper這類基礎服務就應運而生。這些系統都在各個系統久經考驗,它的可靠性,可用性都是經過理論和實踐的驗證的。所以我們在構建一些分散式系統的時候,就可以以這類系統為起點來構建我們的系統,這將節省不少成本,而且bug也將更少。

上述試圖從外圍介紹一下Zookeeper是一個什麼樣子的服務和我們為什麼需要這樣一種服務。接下來會介紹Zookeeper到底能幹些什麼。

4. Zookeeper可以幹什麼

在Zookeeper的官網上有這麼一句話:ZooKeeper is a centralized service for maintaining configuration information, naming, providing distributed synchronization, and providing group services.

這大概描述了Zookeeper主要可以幹哪些事情:配置管理名字服務分散式同步以及叢集管理。那這些服務又到底是什麼呢?我們為什麼需要這樣的服務?我們又為什麼要使用Zookeeper來實現呢,使用Zookeeper有什麼優勢?接下來我會挨個介紹這些到底是什麼,以及有哪些開源系統中使用了。

  • 配置管理
    在我們的應用中除了程式碼外,還有一些就是各種配置。比如資料庫連線等。一般我們都是使用配置檔案的方式,在程式碼中引入這些配置檔案。當我們只有一種配置、一臺伺服器,並且不經常修改的時候,使用配置檔案是一個很好的做法,但是如果我們配置非常多,有很多伺服器都需要這個配置,而且還可能是動態的話使用配置檔案就不是個好主意了。這個時候往往需要尋找一種集中管理配置的方法,我們在這個集中的地方修改了配置,所有對這個配置感興趣的都可以獲得變更。

    比如把這公用的配置檔案提取出來放到一個地方,對這個地方(目錄節點)進行監聽,一旦配置資訊發生變化,每個應用程式就會收到Zookeeper的通知,然後從Zookeeper獲取新的配置資訊應用到系統中。但是,因為很多服務的正常執行都非常依賴這個配置,所以需要這個集中提供配置服務的服務具備很高的可靠性。一般我們可以用一個叢集來提供這個配置服務,但是用叢集提升可靠性,那如何保證配置在叢集中的一致性呢?

    這個時候就需要使用一種實現了一致性協議的服務了。Zookeeper就是這種服務,它使用Zab這種一致性協議來提供一致性。現在有很多開源專案使用Zookeeper來維護配置,比如,在開源的訊息佇列Kafka中,也使用Zookeeper來維護broker的資訊;在Alibaba開源的SOA框架Dubbo中也廣泛的使用Zookeeper管理一些配置來實現服務治理;在HBase中,客戶端就是連線一個Zookeeper,獲得必要的HBase叢集的配置資訊,然後才可以進一步操作。

  • 命名服務
    命名服務這個就很好理解了。比如為了通過網路訪問一個系統,我們得知道對方的IP地址,但是IP地址對人非常不友好,這個時候我們就需要使用域名來訪問。但是計算機是不能識別域名的。怎麼辦呢?如果我們每臺機器裡都有一份域名到IP地址的對映,這個倒是能解決一部分問題,但是如果域名對應的IP發生變化了又該怎麼辦呢?

    於是我們有了DNS這個東西。我們只需要訪問一個大家熟知的(known)的點,它就會告訴你這個域名對應的IP是什麼。在我們的應用中也會存在很多這類問題,特別是在我們的服務特別多的時候,如果我們在本地儲存服務的地址將非常不方便,但是如果我們只需要訪問一個大家都熟知的訪問點,這裡提供統一的入口,那麼維護起來將方便多了。

    比如,用過Duboo+Zookeeper整合的系統的人也能知道,正是因為這個命名服務,才方便了分散式中各個專案之間的聯絡.在SOA(Service-Oriented Architecture)框架裡面,RMI(Remote Method Invoke)框架就是通過某個伺服器上的URL來獲取遠端伺服器上的物件來呼叫服務,但到叢集和分散式環境下,如何做到子專案之間呼叫的關係不會很複雜,不會到時候出現問題都不知道哪個服務呼叫的哪個,所以這裡就需要一個伺服器專門替我們管理這些服務,這樣我們就可以集中精力在業務處理上。Zookeeper的命名功能就是這麼一個伺服器。在叢集中,相同的一個服務有很多個提供者,這些提供者啟動時,提供者的相關資訊(服務介面,地址,埠等)註冊到Zookeeper中,當消費者要消費某服務的時候,從Zookeeper中獲取該服務的所有提供者的資訊目錄,再根據Dubbo的負載均衡機制選擇一個提供者。

    其實從配置管理、命名服務的作用中可以看出,Zookeeper相當於一個檔案系統(類似與linux檔案系統),換句話說,zookeeper是分散式中的大腦。

  • 分散式鎖
    分散式鎖,又叫分散式的同步。比如,上文中提到了Zookeeper是一個分散式協調服務,這樣我們就可以利用Zookeeper來協調多個分散式程式之間的活動。

    比如在一個分散式環境裡,為了提高可靠性,我們的叢集中每臺伺服器上都部署著同樣的服務。但是,一件事情如果叢集中的每個伺服器都進行的話,那相互之間就要協調,程式設計起來將非常複雜。而如果我們只讓一個服務進行操作,那又存在單點。通常還有一種做法就是使用分散式鎖,在某個時刻只讓一個服務去幹活,當這臺服務出問題的時候鎖釋放,立即fail over到另外的服務。

    很多分散式系統都是這麼做的,這種設計有一個更好聽的名字叫Leader Election(leader選舉)。比如HBase的Master就是採用這種機制。

    但要注意的是分散式鎖跟同一個程式的鎖還是有區別的,所以使用的時候要比同一個程式裡的鎖更謹慎的使用。

  • 叢集管理
    在分散式的叢集中,經常會由於各種原因,比如硬體故障,軟體故障,網路問題,有些節點會進進出出。有新的節點加入進來,也有老的節點退出叢集。這個時候,叢集中其他機器需要感知到這種變化,然後根據這種變化做出對應的決策。

    比如在一個分散式儲存系統中,有一箇中央控制節點負責儲存的分配,當有新的儲存進來的時候我們要根據現在叢集目前的狀態來分配儲存節點。這個時候我們就需要動態感知到叢集目前的狀態。

    還有,比如一個分散式的SOA架構中,服務是一個叢集提供的,當消費者訪問某個服務時,就需要採用某種機制發現 現在有哪些節點可以提供該服務(這也稱之為服務發現,比如Alibaba開源的SOA框架Dubbo,就採用了Zookeeper作為服務發現的底層機制)。還有開源的Kafka佇列就採用了Zookeeper作為Cosnumer的上下線管理。


推薦閱讀:

漫畫:什麼是ZooKeeper?



參考文件:

ZooKeeper之(一)ZooKeeper是什麼
Zookeeper--Zookeeper是什麼
Zookeeper-Zookeeper可以幹什麼