1. 程式人生 > >Zookeeper在大型分散式系統中的應用

Zookeeper在大型分散式系統中的應用

一、前言

  上一篇博文講解了Zookeeper的典型應用場景,在大資料時代,各種分散式系統層出不窮,其中,有很多系統都直接或間接使用了Zookeeper,用來解決諸如配置管理、分散式通知/協調、叢集管理和Master選舉等一系列分散式問題。

二、 Hadoop

  Hadoop的核心是HDFS(Hadoop Distributed File System)和MapReduce,分別提供了對海量資料的儲存和計算能力,後來,Hadoop又引入了全新MapReduce框架YARN(Yet Another Resource Negotiator)。在Hadoop中Zookeeper主要用於實現HA

(High Availability),這部分邏輯主要集中在Hadoop Common的HA模組中,HDFS的NameNode與YARN的ResourceManager都是基於此HA模組來實現自己的HA功能,YARN又使用了Zookeeper來儲存應用的執行狀態。

  YARN

  YARN是一種新的 Hadoop 資源管理器,它是一個通用資源管理系統,可為上層應用提供統一的資源管理和排程,它的引入為叢集在利用率、資源統一管理和資料共享等方面帶來了巨大好處。其可以支援MapReduce模型,同時也支援Tez、Spark、Storm、Impala、Open MPI等。

  YARN主要由ResourceManager(RM)、NodeManager(NM)、ApplicationManager(AM)、Container

四部分構成。其中,ResourceManager為全域性資源管理器,負責整個系統的資源管理和分配。由YARN體系架構可以看到ResourceManager的單點問題,ResourceManager的工作狀況直接決定了整個YARN架構是否可以正常運轉。

  ResourceManager HA

  為了解決ResourceManager的單點問題,YARN設計了一套Active/Standby模式的ResourceManager HA架構。

  由上圖可知,在執行期間,會有多個ResourceManager並存,並且其中只有一個ResourceManager處於Active狀態,另外一些(允許一個或者多個)則處於Standby狀態,當Active節點無法正常工作時,其餘處於Standby狀態的節點則會通過競爭選舉產生新的Active節點。

  主備切換

  ResourceManager使用基於Zookeeper實現的ActiveStandbyElector元件來確定ResourceManager的狀態。具體步驟如下

  1. 建立鎖節點。在Zookeeper上會有一個類似於/yarn-leader-election/pseudo-yarn-rm-cluster的鎖節點,所有的ResourceManager在啟動時,都會去競爭寫一個Lock子節點(/yarn-leader-election/pseudo-yarn-rm-cluster/ActiveStandbyElectorLock),子節點型別為臨時節點,利用Zookeeper的特性,建立成功的那個ResourceManager切換為Active狀態,其餘的為Standby狀態。

  2. 註冊Watcher監聽。所有Standby狀態的ResourceManager都會向/yarn-leader-election/pseudo-yarn-rm-cluster/ActiveStandbyElectorLock節點註冊一個節點變更監聽,利用臨時節點的特性,能夠快速感知到Active狀態的ResourceManager的執行情況。

  3. 主備切換。當Active的ResourceManager無法正常工作時,其建立的Lock節點也會被刪除,此時,其餘各個Standby的ResourceManager都會收到通知,然後重複步驟1。

  隔離(Fencing)

  在分散式環境中,經常會出現諸如單機假死(機器由於網路閃斷或是其自身由於負載過高,常見的有GC佔用時間過長或CPU負載過高,而無法正常地對外進行及時響應)情況。假設RM叢集由RM1和RM2兩臺機器構成,某一時刻,RM1發生了假死,此時,Zookeeper認為RM1掛了,然後進行主備切換,RM2會成為Active狀態,但是在隨後,RM1恢復了正常,其依然認為自己還處於Active狀態,這就是分散式腦裂現象,即存在多個處於Active狀態的RM工作,可以使用隔離來解決此類問題。

  YARN引入了Fencing機制,藉助Zookeeper的資料節點的ACL許可權控制機制來實現不同RM之間的隔離。在上述主備切換時,多個RM之間通過競爭建立鎖節點來實現主備狀態的確定,此時,只需要在建立節點時攜帶Zookeeper的ACL資訊,目的是為了獨佔該節點,以防止其他RM對該節點進行更新。

  還是上述案例,若RM1出現假死,Zookeeper會移除其建立的節點,此時RM2會建立相應的鎖節點並切換至Active狀態,RM1恢復之後,會試圖去更新Zookeeper相關資料,但是此時其沒有許可權更新Zookeeper的相關節點資料,因為節點不是由其建立的,於是就自動切換至Standby狀態,這樣就避免了腦裂現象的出現。

  ResourceManager狀態儲存

  在ResourceManager中,RMStateStore可以儲存一些RM的內部狀態資訊,包括Application以及Attempts資訊、Delegation Token及Version Information等,值得注意的是,RMStateStore的絕大多數狀態資訊都是不需要持久化儲存的(如資源使用情況),因為其很容易從上下文資訊中重構,,在儲存方案設計中,提供了三種可能的實現。

  1. 基於記憶體實現,一般用於日常開發測試。

  2. 基於檔案系統實現,如HDFS。

  3. 基於Zookeeper實現。

  由於儲存的資訊不是特別大,Hadoop官方建議基於Zookeeper來實現狀態資訊的儲存,在Zookeeper中,ResourceManager的狀態資訊都被儲存在/rmstore這個根節點下,其資料結構如下。

  在RMAppRoot節點下儲存的是與各個Application相關的資訊,RMDTSecretManagerRoot儲存的是與安全相關的Token資訊。每個Active狀態的ResourceManager在初始化節點都會從Zookeeper上讀取到這些資訊,並根據這些狀態資訊繼續後續的處理。

三、HBase

  HBase(Hadoop Database)是一個基於Hadoop的檔案系統設計的面向海量資料的高可靠、高效能、面向列、可伸縮的分散式儲存系統,其針對資料寫入具有強一致性,索引列也實現了強一致性,其採用了Zookeeper服務來完成對整個系統的分散式協調工作,其架構如下

  HBase架構中,Zookeeper是串聯起HBase叢集與Client的關鍵所在,Zookeeper在HBase中的系統容錯、RootRegion管理、Region狀態管理、分散式SplitLog任務管理、Replication管理都扮演重要角色

  系統容錯

  在HBase啟動時,每個RegionServer伺服器都會到Zookeeper的/hbase/rs節點下建立一個資訊節點,例如/hbase/rs/[Hostname],同時,HMaster會對這個節點進行監聽,當某個RegionServer掛掉時,Zookeeper會因為在一段時間內無法接收其心跳資訊(Session失效),而刪除掉該RegionServer伺服器對應的節點,與此同時,HMaster則會接收到Zookeeper的NodeDelete通知,從而感知到某個節點斷開,並立即開始容錯工作,HMaster會將該RegionServer所處理的資料分片(Region)重新路由到其他節點上,並記錄到Meta資訊中供客戶端查詢。

  RootRegion管理

  資料儲存的位置資訊是記錄在元資料分片上的,即在RootRegion上,每次客戶端發起新的請求,就會查詢RootRegion來確定資料的位置,而RootRegion自身的位置則記錄在Zookeeper上(預設情況下在/hbase/root-region-server節點中)。當RootRegion發生變化時,如Region手工移動、Balance或者是RootRegion所在伺服器發生了故障,就能通過Zookeeper來感知到這一變化並做出一系列相應的容災措施,從而保障客戶端總能夠拿到正確的RootRegion資訊。

  Region狀態管理

  Region是HBase中資料的物理切片,每個Region記錄了全域性資料的一小部分,並且不同的Region之間的資料是相互不重複的。但對於一個分散式系統來說,Region是會發生變更的,原因可能是系統故障、負載均衡、配置修改、Region分裂合併等,一旦Region發生變動,它必然經歷離線和重新線上的過程。在離線期間,資料是不能被訪問的,並且Region的狀態變化必須讓全域性知曉,否則可能會出現某些事務性的異常,而對於HBase叢集而言,Region的數量可能會多達10萬級別,因此這樣規模的Region狀態管理只有依靠Zookeeper才能做到。

  分散式SplitLog任務管理

  當某臺RegionServer伺服器掛掉後,由於總有一部分新寫入的資料還沒有持久化到HFile中(在記憶體中),因此在遷移該RegionServer的服務時,應該從HLog中恢復這部分還在記憶體中的資料,此時HMaster需要遍歷該RegionServer伺服器的HLog(SplitLog),並按照Region切分成小塊移動到新的地址,並進行資料的Replay。由於單個RegionServer的日誌量相對龐大(可能存在上千個Region,上GB的日誌),而使用者往往希望系統能夠快速完成日誌的恢復工作,因此,需要將處理HLog的任務分配給多臺RegionServer伺服器共同處理,而這又需要一個持久化元件來輔助HMaster完成任務的分配,當前做法如下,HMaster會在Zookeeper上建立一個splitlog節點(預設為/hbase/splitlog節點),將"哪個RegionServer處理哪個Region"的資訊以列表的形式存放在該節點上,然後由各個RegionServer伺服器自行到該節點上去領取任務並在任務執行成功或失敗後再更新該節點的資訊以通知HMaster繼續後續步驟,Zookeeper起到了相互通知和資訊持久化的角色。

  Replication管理

  Replication是實現HBase中主備叢集間的實時同步的重要模組,與傳統關係型資料庫不同的是,HBase的Replication是多對多的,且每個節點隨時都有可能掛掉,因此其會複雜得多。HBase也藉助了Zookeeper來完成Replication功能,做法是在Zookeeper上記錄一個replication節點(預設是/hbase/replication節點),然後把不同的RegionServer伺服器對應的HLog檔名稱記錄到相應的節點上,HMaster叢集會將新增的資料推送給Slave叢集,並同時將推送資訊記錄到Zookeeper上(稱為斷點記錄),然後重複上述步驟,當伺服器掛掉時,由於Zookeeper上已經儲存了斷點資訊,因此只要有HMaster能夠根據這些資訊來協同用來推送HLog資料的主節點伺服器就可以進行繼續複製操作。

四、Kafka

  kafka是一個吞吐量極高的分散式訊息系統,其整體設計是典型的釋出與訂閱系統模式,在Kafka叢集中,沒有中心主節點概念,所有伺服器都是對等的,因此,可以在不做任何配置更改的情況下實現伺服器的新增與刪除,同樣,訊息的生產者和消費者也能夠隨意重啟和機器的上下線。

  生產者(Producer):訊息產生的源頭,負責生成訊息併發送到Kafka伺服器。

  消費者(Consumer):訊息的使用方,負責消費Kafka伺服器上的訊息。

  主題(Topic):由使用者定義並配置在Kafka服務端,用於建立生產者和消費者之間的訂閱關係,生產者傳送訊息到指定Topic下,消費者從這個Topic中消費訊息。

  訊息分割槽(Partition):一個Topic下會分為多個分割槽,如"kafka-test"這個Topic可以分為10個分割槽,分別由兩臺伺服器提供,那麼通常可以配置讓每臺伺服器提供5個分割槽,假設伺服器ID為0和1,那麼分割槽為0-0、0-1、0-2、0-3、0-4和1-0、 1-1、1-2、1-3、1-4。訊息分割槽機制和分割槽的數量與消費者的負載均衡機制有很大的關係。

  伺服器(Broker):用於儲存資訊,在訊息中介軟體中通常被稱為Broker。

  消費者分組(Group):歸組同類消費者,多個消費者可以共同消費一個Topic下的訊息,每個消費者消費其中的部分訊息,這些消費者組成了消費者分組,擁有同一個分組名稱,通常也被稱為消費者叢集。

  偏移量(Offset):訊息儲存在Kafka的Broker上,消費者拉取訊息資料的過程中需要知道訊息在檔案中的偏移量。

  Broker註冊

  Broker是分散式部署並且相互之間相互獨立,但是需要有一個註冊系統能夠將整個叢集中的Broker管理起來,此時就使用到了Zookeeper。在Zookeeper上會有一個專門用來進行Broker伺服器列表記錄的節點/brokers/ids。每個Broker在啟動時,都會到Zookeeper上進行註冊,即到/brokers/ids下建立屬於自己的節點,如/brokers/ids/[0...N]。Kafka使用了全域性唯一的數字來指代每個Broker伺服器,不同的Broker必須使用不同的Broker ID進行註冊,建立完節點後,每個Broker就會將自己的IP地址和埠資訊記錄到該節點中去。其中,Broker建立的節點型別是臨時節點,一旦Broker宕機,則對應的臨時節點也會被自動刪除。

  Topic註冊

  在Kafka中,同一個Topic的訊息會被分成多個分割槽並將其分佈在多個Broker上,這些分割槽資訊及與Broker的對應關係也都是由Zookeeper在維護,由專門的節點來記錄,如/borkers/topics。Kafka中每個Topic都會以/brokers/topics/[topic]的形式被記錄,如/brokers/topics/login和/brokers/topics/search等。Broker伺服器啟動後,會到對應Topic節點(/brokers/topics)上註冊自己的Broker ID並寫入針對該Topic的分割槽總數,如/brokers/topics/login/3->2,這個節點表示Broker ID為3的一個Broker伺服器,對於"login"這個Topic的訊息,提供了2個分割槽進行訊息儲存,同樣,這個分割槽節點也是臨時節點。

  生產者負載均衡

  由於同一個Topic訊息會被分割槽並將其分佈在多個Broker上,因此,生產者需要將訊息合理地傳送到這些分散式的Broker上,那麼如何實現生產者的負載均衡,Kafka支援傳統的四層負載均衡,也支援Zookeeper方式實現負載均衡。

  ① 四層負載均衡,根據生產者的IP地址和埠來為其確定一個相關聯的Broker。通常,一個生產者只會對應單個Broker,然後該生產者產生的訊息都發往該Broker。這種方式邏輯簡單,每個生產者不需要同其他系統建立額外的TCP連線,只需要和Broker維護單個TCP連線即可。但是,其無法做到真正的負載均衡,因為實際系統中的每個生產者產生的訊息量及每個Broker的訊息儲存量都是不一樣的,如果有些生產者產生的訊息遠多於其他生產者的話,那麼會導致不同的Broker接收到的訊息總數差異巨大,同時,生產者也無法實時感知到Broker的新增和刪除。

  ② 使用Zookeeper進行負載均衡,由於每個Broker啟動時,都會完成Broker註冊過程,生產者會通過該節點的變化來動態地感知到Broker伺服器列表的變更,這樣就可以實現動態的負載均衡機制。

  消費者負載均衡

  與生產者類似,Kafka中的消費者同樣需要進行負載均衡來實現多個消費者合理地從對應的Broker伺服器上接收訊息,每個消費者分組包含若干消費者,每條訊息都只會傳送給分組中的一個消費者,不同的消費者分組消費自己特定的Topic下面的訊息,互不干擾。

  消費分割槽與消費者的關係

  對於每個消費者分組,Kafka都會為其分配一個全域性唯一的Group ID,同一個消費者分組內部的所有消費者共享該ID。同時,Kafka為每個消費者分配一個Consumer ID,通常採用"Hostname:UUID"形式表示。在Kafka中,規定了每個訊息分割槽有且只能同時有一個消費者進行消費,因此,需要在Zookeeper上記錄訊息分割槽與消費者之間的關係,每個消費者一旦確定了對一個訊息分割槽的消費權力,需要將其Consumer ID 寫入到對應訊息分割槽的臨時節點上,例如/consumers/[group_id]/owners/[topic]/[broker_id-partition_id],其中,[broker_id-partition_id]就是一個訊息分割槽的標識,節點內容就是該消費分割槽上訊息消費者的Consumer ID。

  訊息消費進度Offset記錄

  在消費者對指定訊息分割槽進行訊息消費的過程中,需要定時地將分割槽訊息的消費進度Offset記錄到Zookeeper上,以便在該消費者進行重啟或者其他消費者重新接管該訊息分割槽的訊息消費後,能夠從之前的進度開始繼續進行訊息消費。Offset在Zookeeper中由一個專門節點進行記錄,其節點路徑為/consumers/[group_id]/offsets/[topic]/[broker_id-partition_id],節點內容就是Offset的值。

  消費者註冊

  消費者伺服器在初始化啟動時加入消費者分組的步驟如下

  ① 註冊到消費者分組。每個消費者伺服器啟動時,都會到Zookeeper的指定節點下建立一個屬於自己的消費者節點,例如/consumers/[group_id]/ids/[consumer_id],完成節點建立後,消費者就會將自己訂閱的Topic資訊寫入該臨時節點。

  ② 對消費者分組中的消費者的變化註冊監聽。每個消費者都需要關注所屬消費者分組中其他消費者伺服器的變化情況,即對/consumers/[group_id]/ids節點註冊子節點變化的Watcher監聽,一旦發現消費者新增或減少,就觸發消費者的負載均衡。

  ③ 對Broker伺服器變化註冊監聽。消費者需要對/broker/ids/[0-N]中的節點進行監聽,如果發現Broker伺服器列表發生變化,那麼就根據具體情況來決定是否需要進行消費者負載均衡。

  ④ 進行消費者負載均衡。為了讓同一個Topic下不同分割槽的訊息儘量均衡地被多個消費者消費而進行消費者與訊息分割槽分配的過程,通常,對於一個消費者分組,如果組內的消費者伺服器發生變更或Broker伺服器發生變更,會發出消費者負載均衡。

  負載均衡

  Kafka藉助Zookeeper上記錄的Broker和消費者資訊,採用消費者均衡演算法進行負載均衡,其具體步驟如下。假設一個訊息分組的每個消費者記為C1,C2,Ci,...,Cn。那麼對於消費者Ci,其對應的訊息分割槽分配策略如下:

  1. 設定Pr為指定Topic所有的訊息分割槽。

  2. 設定Cg為統一消費者分組中的所有消費者。

  3. 對Pr進行排序,使分佈在同一個Broker伺服器上的分割槽儘量靠在一起。

  4. 對Cg進行排序。

  5. 設定i為Ci在Cg中的位置索引,同時設定N = size (Pr) / size (Cg)。

  6. 將編號為i * N ~ (i + 1) * N - 1的訊息分割槽分配給Ci。

  7. 重新更新Zookeeper上訊息分割槽與消費者Ci的關係。

轉載:http://www.cnblogs.com/leesf456/p/6063694.html