天天看點

Zookeeper架構

zookeeper是什麼

ZooKeeper是一個分布式的,開放源碼的分布式應用程式協調服務,是Google的Chubby一個開源的實作,是Hadoop的重要元件,CDH版本中更是使用它進行Namenode的協調控制。它是一個為分布式應用提供一緻性服務的軟體,提供的功能包括:配置維護、名字服務、分布式同步、組服務等。ZooKeeper的目标就是封裝好複雜易出錯的關鍵服務,将簡單易用的接口和性能高效、功能穩定的系統提供給使用者。

Zookeeper的基本概念

(1) 角色

Zookeeper中的角色主要有以下三類,如下表所示:

Zookeeper架構

系統模型如圖所示:

Zookeeper架構

(2)重要概念

ZNode

前文已介紹了ZNode, ZNode根據其本身的特性,可以分為下面兩類:

    Regular ZNode: 正常型ZNode, 使用者需要顯式的建立、删除

    Ephemeral ZNode: 臨時型ZNode, 使用者建立它之後,可以顯式的删除,也可以在建立它的Session結束後,由ZooKeeper Server自動删除

ZNode還有一個Sequential的特性,如果建立的時候指定的話,該ZNode的名字後面會自動Append一個不斷增加的SequenceNo。

Session

Client與ZooKeeper之間的通信,需要建立一個Session,這個Session會有一個逾時時間。因為ZooKeeper叢集會把Client的Session資訊持久化,是以在Session沒逾時之前,Client與ZooKeeper Server的連接配接可以在各個ZooKeeper Server之間透明地移動。

在實際的應用中,如果Client與Server之間的通信足夠頻繁,Session的維護就不需要其它額外的消息了。否則,ZooKeeper Client會每t/3 ms發一次心跳給Server,如果Client 2t/3 ms沒收到來自Server的心跳回應,就會換到一個新的ZooKeeper Server上。這裡t是使用者配置的Session的逾時時間。

Watcher

ZooKeeper支援一種Watch操作,Client可以在某個ZNode上設定一個Watcher,來Watch該ZNode上的變化。如果該ZNode上有相應的變化,就會觸發這個Watcher,把相應的事件通知給設定Watcher的Client。需要注意的是,ZooKeeper中的Watcher是一次性的,即觸發一次就會被取消,如果想繼續Watch的話,需要用戶端重新設定Watcher。這個跟epoll裡的oneshot模式有點類似。

(3)特性

順序性,client的updates請求都會根據它發出的順序被順序的處理;

原子性,  一個update操作要麼成功要麼失敗,沒有其他可能的結果;

一緻的鏡像,client不論連接配接到哪個server,展示給它都是同一個視圖;

可靠性,一旦一個update被應用就被持久化了,除非另一個update請求更新了目前值

實時性,對于每個client它的系統視圖都是最新的

ZooKeeper Client API

ZooKeeper Client Library提供了豐富直覺的API供使用者程式使用,下面是一些常用的API:

    create(path, data, flags): 建立一個ZNode, path是其路徑,data是要存儲在該ZNode上的資料,flags常用的有: PERSISTEN, PERSISTENT_SEQUENTAIL, EPHEMERAL, EPHEMERAL_SEQUENTAIL

    delete(path, version): 删除一個ZNode,可以通過version删除指定的版本, 如果version是-1的話,表示删除所有的版本

    exists(path, watch): 判斷指定ZNode是否存在,并設定是否Watch這個ZNode。這裡如果要設定Watcher的話,Watcher是在建立ZooKeeper執行個體時指定的,如果要設定特定的Watcher的話,可以調用另一個重載版本的exists(path, watcher)。以下幾個帶watch參數的API也都類似

    getData(path, watch): 讀取指定ZNode上的資料,并設定是否watch這個ZNode

    setData(path, watch): 更新指定ZNode的資料,并設定是否Watch這個ZNode

    getChildren(path, watch): 擷取指定ZNode的所有子ZNode的名字,并設定是否Watch這個ZNode

    sync(path): 把所有在sync之前的更新操作都進行同步,達到每個請求都在半數以上的ZooKeeper Server上生效。path參數目前沒有用

    setAcl(path, acl): 設定指定ZNode的Acl資訊

    getAcl(path): 擷取指定ZNode的Acl資訊

 特點

讀、寫(更新)模式

在ZooKeeper叢集中,讀可以從任意一個ZooKeeper Server讀,這一點是保證ZooKeeper比較好的讀性能的關鍵;寫的請求會先Forwarder到Leader,然後由Leader來通過ZooKeeper中的原子廣播協定,将請求廣播給所有的Follower,Leader收到一半以上的寫成功的Ack後,就認為該寫成功了,就會将該寫進行持久化,并告訴用戶端寫成功了。

WAL和Snapshot

和大多數分布式系統一樣,ZooKeeper也有WAL(Write-Ahead-Log),對于每一個更新操作,ZooKeeper都會先寫WAL, 然後再對記憶體中的資料做更新,然後向Client通知更新結果。另外,ZooKeeper還會定期将記憶體中的目錄樹進行Snapshot,落地到磁盤上,這個跟HDFS中的FSImage是比較類似的。這麼做的主要目的,一當然是資料的持久化,二是加快重新開機之後的恢複速度,如果全部通過Replay WAL的形式恢複的話,會比較慢。

FIFO

對于每一個ZooKeeper用戶端而言,所有的操作都是遵循FIFO順序的,這一特性是由下面兩個基本特性來保證的:一是ZooKeeper Client與Server之間的網絡通信是基于TCP,TCP保證了Client/Server之間傳輸包的順序;二是ZooKeeper Server執行用戶端請求也是嚴格按照FIFO順序的。

Linearizability

在ZooKeeper中,所有的更新操作都有嚴格的偏序關系,更新操作都是串行執行的,這一點是保證ZooKeeper功能正确性的關鍵。

zookeeper的存儲結構

Zookeeper架構

zookeeper中的資料是按照“樹”結構進行存儲的。而且znode節點還分為4中不同的類型。

(1)、znode

根據本小結第一部分的描述,很顯然zookeeper叢集自身維護了一套資料結構。這個存儲結構是一個樹形結構,其上的每一個節點,我們稱之為“znode”。

  • 每一個znode預設能夠存儲1MB的資料(對于記錄狀态性質的資料來說,夠了)
  • 可以使用zkCli指令,登入到zookeeper上,并通過ls、create、delete、sync等指令操作這些znode節點
  • znode除了名稱、資料以外,還有一套屬性:zxid。這套zid與時間戳對應,記錄zid不同的狀态(後續我們将用到)

那麼每個znode結構又是什麼樣的呢?如下圖所示:

Zookeeper架構

此外,znode還有操作權限。如果我們把以上幾類屬性細化,又可以得到以下屬性的細節:

  • czxid:建立節點的事務的zxid
  • mzxid:對znode最近修改的zxid
  • ctime:以距離時間原點(epoch)的毫秒數表示的znode建立時間
  • mtime:以距離時間原點(epoch)的毫秒數表示的znode最近修改時間
  • version:znode資料的修改次數
  • cversion:znode子節點修改次數
  • aversion:znode的ACL修改次數
  • ephemeralOwner:如果znode是臨時節點,則訓示節點所有者的會話ID;如果不是臨時節點,則為零。
  • dataLength:znode資料長度。
  • numChildren:znode子節點個數。

(2)、znode中的存在類型

我們知道了zookeeper内部維護了一套資料結構:由znode構成的集合,znode的集合又是一個樹形結構。每一個znode又有很多屬性進行描述。并且znode的存在性還分為四類,如下如所示:

Zookeeper架構

znode是由用戶端建立的,它和建立它的用戶端的内在聯系,決定了它的存在性:

  • PERSISTENT-持久化節點:建立這個節點的用戶端在與zookeeper服務的連接配接斷開後,這個節點也不會被删除(除非您使用API強制删除)。
  • PERSISTENT_SEQUENTIAL-持久化順序編号節點:當用戶端請求建立這個節點A後,zookeeper會根據parent-znode的zxid狀态,為這個A節點編寫一個全目錄唯一的編号(這個編号隻會一直增長)。當用戶端與zookeeper服務的連接配接斷開後,這個節點也不會被删除。
  • EPHEMERAL-臨時目錄節點:建立這個節點的用戶端在與zookeeper服務的連接配接斷開後,這個節點(還有涉及到的子節點)就會被删除。
  • EPHEMERAL_SEQUENTIAL-臨時順序編号目錄節點:當用戶端請求建立這個節點A後,zookeeper會根據parent-znode的zxid狀态,為這個A節點編寫一個全目錄唯一的編号(這個編号隻會一直增長)。當建立這個節點的用戶端與zookeeper服務的連接配接斷開後,這個節點被删除。
  • 另外,無論是EPHEMERAL還是EPHEMERAL_SEQUENTIAL節點類型,在zookeeper的client異常終止後,節點也會被删除。

ZooKeeper典型應用場景

1. 名字服務(NameService) 

分布式應用中,通常需要一套完備的指令機制,既能産生唯一的辨別,又友善人識别和記憶。 我們知道,每個ZNode都可以由其路徑唯一辨別,路徑本身也比較簡潔直覺,另外ZNode上還可以存儲少量資料,這些都是實作統一的NameService的基礎。下面以在HDFS中實作NameService為例,來說明實作NameService的基本布驟:

目标:通過簡單的名字來通路指定的HDFS機群

定義命名規則:這裡要做到簡潔易記憶。下面是一種可選的方案: [serviceScheme://][zkCluster]-[clusterName],比如hdfs://lgprc-example/表示基于lgprc ZooKeeper叢集的用來做example的HDFS叢集

配置DNS映射: 将zkCluster的辨別lgprc通過DNS解析到對應的ZooKeeper叢集的位址

建立ZNode: 在對應的ZooKeeper上建立/NameService/hdfs/lgprc-example結點,将HDFS的配置檔案存儲于該結點下

使用者程式要通路hdfs://lgprc-example/的HDFS叢集,首先通過DNS找到lgprc的ZooKeeper機群的位址,然後在ZooKeeper的/NameService/hdfs/lgprc-example結點中讀取到HDFS的配置,進而根據得到的配置,得到HDFS的實際通路入口

2. 配置管理(Configuration Management) 

在分布式系統中,常會遇到這樣的場景: 某個Job的很多個執行個體在運作,它們在運作時大多數配置項是相同的,如果想要統一改某個配置,一個個執行個體去改,是比較低效,也是比較容易出錯的方式。通過ZooKeeper可以很好的解決這樣的問題,下面的基本的步驟:

将公共的配置内容放到ZooKeeper中某個ZNode上,比如/service/common-conf

所有的執行個體在啟動時都會傳入ZooKeeper叢集的入口位址,并且在運作過程中Watch /service/common-conf這個ZNode

如果叢集管理者修改了了common-conf,所有的執行個體都會被通知到,根據收到的通知更新自己的配置,并繼續Watch /service/common-conf

3. 組員管理(Group Membership) 

在典型的Master-Slave結構的分布式系統中,Master需要作為“總管”來管理所有的Slave, 當有Slave加入,或者有Slave當機,Master都需要感覺到這個事情,然後作出對應的調整,以便不影響整個叢集對外提供服務。以HBase為例,HMaster管理了所有的RegionServer,當有新的RegionServer加入的時候,HMaster需要配置設定一些Region到該RegionServer上去,讓其提供服務;當有RegionServer當機時,HMaster需要将該RegionServer之前服務的Region都重新配置設定到目前正在提供服務的其它RegionServer上,以便不影響用戶端的正常通路。下面是這種場景下使用ZooKeeper的基本步驟:

Master在ZooKeeper上建立/service/slaves結點,并設定對該結點的Watcher

每個Slave在啟動成功後,建立唯一辨別自己的臨時性(Ephemeral)結點/service/slaves/${slave_id},并将自己位址(ip/port)等相關資訊寫入該結點

Master收到有新子結點加入的通知後,做相應的處理

如果有Slave當機,由于它所對應的結點是臨時性結點,在它的Session逾時後,ZooKeeper會自動删除該結點

Master收到有子結點消失的通知,做相應的處理

4. 簡單互斥鎖(Simple Lock) 

我們知識,在傳統的應用程式中,線程、程序的同步,都可以通過作業系統提供的機制來完成。但是在分布式系統中,多個程序之間的同步,作業系統層面就無能為力了。這時候就需要像ZooKeeper這樣的分布式的協調(Coordination)服務來協助完成同步,下面是用ZooKeeper實作簡單的互斥鎖的步驟,這個可以和線程間同步的mutex做類比來了解:

多個程序嘗試去在指定的目錄下去建立一個臨時性(Ephemeral)結點 /locks/my_lock

ZooKeeper能保證,隻會有一個程序成功建立該結點,建立結點成功的程序就是搶到鎖的程序,假設該程序為A

其它程序都對/locks/my_lock進行Watch

當A程序不再需要鎖,可以顯式删除/locks/my_lock釋放鎖;或者是A程序當機後Session逾時,ZooKeeper系統自動删除/locks/my_lock結點釋放鎖。此時,其它程序就會收到ZooKeeper的通知,并嘗試去建立/locks/my_lock搶鎖,如此循環反複

5. 互斥鎖(Simple Lock without Herd Effect) 

上一節的例子中有一個問題,每次搶鎖都會有大量的程序去競争,會造成羊群效應(Herd Effect),為了解決這個問題,我們可以通過下面的步驟來改進上述過程:

每個程序都在ZooKeeper上建立一個臨時的順序結點(Ephemeral Sequential) /locks/lock_${seq}

${seq}最小的為目前的持鎖者(${seq}是ZooKeeper生成的Sequenctial Number)

其它程序都對隻watch比它次小的程序對應的結點,比如2 watch 1, 3 watch 2, 以此類推

目前持鎖者釋放鎖後,比它次大的程序就會收到ZooKeeper的通知,它成為新的持鎖者,如此循環反複

這裡需要補充一點,通常在分布式系統中用ZooKeeper來做Leader Election(選主)就是通過上面的機制來實作的,這裡的持鎖者就是目前的“主”。

6. 讀寫鎖(Read/Write Lock) 

我們知道,讀寫鎖跟互斥鎖相比不同的地方是,它分成了讀和寫兩種模式,多個讀可以并發執行,但寫和讀、寫都互斥,不能同時執行行。利用ZooKeeper,在上面的基礎上,稍做修改也可以實作傳統的讀寫鎖的語義,下面是基本的步驟:

每個程序都在ZooKeeper上建立一個臨時的順序結點(Ephemeral Sequential) /locks/lock_${seq}

${seq}最小的一個或多個結點為目前的持鎖者,多個是因為多個讀可以并發

需要寫鎖的程序,Watch比它次小的程序對應的結點

需要讀鎖的程序,Watch比它小的最後一個寫程序對應的結點

目前結點釋放鎖後,所有Watch該結點的程序都會被通知到,他們成為新的持鎖者,如此循環反複

7. 屏障(Barrier) 

在分布式系統中,屏障是這樣一種語義: 用戶端需要等待多個程序完成各自的任務,然後才能繼續往前進行下一步。下用是用ZooKeeper來實作屏障的基本步驟:

Client在ZooKeeper上建立屏障結點/barrier/my_barrier,并啟動執行各個任務的程序

Client通過exist()來Watch /barrier/my_barrier結點

每個任務程序在完成任務後,去檢查是否達到指定的條件,如果沒達到就啥也不做,如果達到了就把/barrier/my_barrier結點删除

Client收到/barrier/my_barrier被删除的通知,屏障消失,繼續下一步任務

8. 雙屏障(Double Barrier)

雙屏障是這樣一種語義: 它可以用來同步一個任務的開始和結束,當有足夠多的程序進入屏障後,才開始執行任務;當所有的程序都執行完各自的任務後,屏障才撤銷。下面是用ZooKeeper來實作雙屏障的基本步驟:

進入屏障:

Client Watch /barrier/ready結點, 通過判斷該結點是否存在來決定是否啟動任務

每個任務程序進入屏障時建立一個臨時結點/barrier/process/${process_id},然後檢查進入屏障的結點數是否達到指定的值,如果達到了指定的值,就建立一個/barrier/ready結點,否則繼續等待

Client收到/barrier/ready建立的通知,就啟動任務執行過程

離開屏障:

Client Watch /barrier/process,如果其沒有子結點,就可以認為任務執行結束,可以離開屏障

每個任務程序執行任務結束後,都需要删除自己對應的結點/barrier/process/${process_id}

版權聲明:本文為CSDN部落客「weixin_34167819」的原創文章,遵循CC 4.0 BY-SA版權協定,轉載請附上原文出處連結及本聲明。

原文連結:https://blog.csdn.net/weixin_34167819/article/details/92169211