zookeeper介紹與核心概念

【推薦】2019 Java 開發者跳槽指南.pdf(吐血整理) >>> hot3.png

1、ZooKeeper介紹與核心概念

1.1 簡介

    ZooKeeper最爲主要的使用場景,是作爲分佈式系統的分佈式協同服務。在學習zookeeper之前,先要對分佈式系統的概念有所瞭解,否則你將完全不知道zookeeper在分佈式系統中起到了什麼作用,解決了什麼問題。

1.2分佈式系統面臨的問題

    我們將分佈式系統定義爲:分佈式系統指的是同時跨越多個物理主機,將一個完整的系統劃分爲多個獨立運行的子系統,這些子系統之間互相協作構成一個完整的系統功能。類比一下,分佈式系統就是將一個完整的任務細分爲多個子任務,一羣人分別完成一個子任務,最終完成整個任務。人多力量大,每個服務器的算力是有限的,但是通過分佈式系統,由n個服務器組成起來的集羣,算力是可以無限擴張的。

說起分佈式就要談談集羣,兩者很相似,都是通過網絡協同多臺主機服務器節點完成整體的功能。

但不同點在於:

集羣中的每個服務器節點都完成的是同一個功能,比如mysql數據庫集羣、redis集羣;

而分佈式系統則是各個服務器節點所負責的是不同的子系統(任務或者說功能),比如電商系統的分佈式系統會分爲訂單系統、支付系統、數據庫系統、緩存系統等等。

所謂分佈式集羣系統,就是將一個完整的系統進行拆分多個子系統,每個子系統都進行集羣部署,各系統集羣之間互相協作,就能構成一個分佈式集羣系統。

    優點顯而易見,人多幹活快,並且互爲備份。但是缺點也很明顯。我們可以想象一下,以一個小研發團隊開發軟件爲例,假設我們有一個5人的項目組,要開始一個系統的開發,項目組將面臨如下問題:

8af2347bfb510fcf5f6c3ba327c8012da61.jpg

你一定在想,以上這些問題很簡單啊,在我的日常工作中天天都在發生,並沒感覺有什麼複雜。是的,這是因爲我們人類的大腦是個超級計算機,能夠靈活應對這些問題,而且現實中信息的交換不依賴網絡,不會因網絡延遲或者中斷,出現信息不對等。而且現實中對以上問題的處理其實並不嚴謹,從而也引發了很多問題。想一想,項目中是不是出現過溝通不暢造成任務分配有歧義?是否由於人員離職造成任務進行不下去,甚至要聯繫離職人員協助?是不是出現過任務分配不合理?類似這樣的各種問題,肯定會發生於你的項目組中。在現實世界,我們可以人爲去協調,即使出錯了,人工去補錯,加加班搞定就好。但在計算機的世界,這樣做是行不通的,一切都要保證嚴謹,以上問題要做到儘可能不要發生。因此,分佈式系統必須採用合理的方式解決掉以上的問題。

實際上要想解決這些問題並沒有那麼複雜,我們僅需要做一件事就可以萬事無憂---讓信息在項目組成員中同步。如果能做到信息同步,那麼每個人在幹什麼,大家都是清楚的,幹到什麼程度也是清晰的,無論誰離職也不會產生問題。分配的工作,能夠及時清晰的同步給每個組員,確保每個組員收到的任務分配沒有衝突。

    分佈式系統的協調工作就是通過某種方式,讓每個節點的信息能夠同步和共享。這依賴於服務進程之間的通信。通信方式有兩種:

1、通過網絡進行信息共享

    這就像現實世界,開發leader在會上把任務傳達下去,組員通過聽leader命令或者看leader的郵件知道自己要幹什麼。當任務分配有變化時,leader會單獨告訴組員,或者再次召開會議。信息通過人與人之間的直接溝通,完成傳遞。

2、通過共享存儲

    這就好比開發leader按照約定的時間和路徑,把任務分配表放到了svn上,組員每天去svn上拉取最新的任務分配表,然後幹活。其中svn就是共享存儲。更好一點的做法是,當svn文件版本更新時,觸發郵件通知,每個組員再去拉取最新的任務分配表。這樣做更好,因爲每次更新,組員都能第一時間得到消息,從而讓自己手中的任務分配表永遠是最新的。此種方式依賴於中央存儲。整個過程如下圖所示:

 4149e86ac30c25b1a8a6ffe8d8d52e4b15e.jpg

1.3 ZooKeeper如何解決分佈式系統面臨的問題

ZooKeeper對分佈式系統的協調,使用的是第二種方式,共享存儲。其實共享存儲,分佈式應用也需要和存儲進行網絡通信。網絡通信是分佈式系統併發設計的基礎。

    實際上,通過ZooKeeper實現分佈式協同的原理,和項目組通過SVN同步工作任務的例子是一樣的。ZooKeeper就像是svn,存儲了任務的分配、完成情況等共享信息。每個分佈式應用的節點就是組員,訂閱這些共享信息。當主節點(組leader),對某個從節點的分工信息作出改變時,相關訂閱的從節點得到zookeeper的通知,取得自己最新的任務分配。完成工作後,把完成情況存儲到zookeeper。主節點訂閱了該任務的完成情況信息,所以將得到zookeeper的完工的通知。參考下圖,和前面項目組通過svn分配工作的例子一模一樣,僅僅是把svn和郵件系統合二爲一,以ZooKeeper代替。

9bcb014f6417aa433f9ba2304fc3e6285fc.jpg

注:Slave節點要想獲取ZooKeeper的更新通知,需事先在關心的數據節點上設置觀察點。

大多數分佈式系統中出現的問題,都源於信息的共享出了問題。如果各個節點間信息不能及時共享和同步,那麼就會在協作過程中產生各種問題。ZooKeeper解決協同問題的關鍵,在於保證分佈式系統信息的一致性。

通過以上章節的講解,我們應該已經理解分佈式系統以及其面臨的問題。瞭解了ZooKeeper通過什麼樣的機制去解決這些問題。從宏觀上對ZooKeeper已經有了認知,接下來我們先切入到zookeeper自身,講解zookeeper的概念,這些概念很重要,所有zookeeper的應用都會圍繞這些概念來實現。

 
1.4、zookeeper概念介紹

ZooKeeper並不直接暴露分佈式服務所需要的原語及原語的調用方法。什麼是原語?舉個例子,比如說分佈式鎖機制是一個原語,它會暴露出創建、獲取、釋放三個調用方法。ZooKeeper以類似文件系統的方式存儲數據,暴漏出調用這些數據的API。讓應用通過ZooKeeper的機制和API,自己來實現分佈式相關原語。

我們若想讓應用能夠通過ZooKeeper實現分佈式協同,那麼第一件事就是了解ZooKeeper的特性及相關概念,另外熟悉它給我們提供了哪些API。

1.4.1 znode

第一章講過Zookeeper會保存任務的分配、完成情況,等共享信息,那麼ZooKeeper是如何保存的呢?在ZooKeeper中,這些信息被保存在一個個數據節點上,這些節點被稱爲znode。它採用了類似文件系統的層級樹狀結構進行管理。見下圖示例:

a20706d3a53861744fcd712bd3edb45d61e.jpg

 

根節點/包含4個子節點,其中三個擁有下一級節點。有的葉子節點存儲了信息。

節點上沒有存儲數據,也有着重要的含義。比如在主從模式中,當/master節點沒有數據時,代表分佈式應用的主節點還沒有選舉出來。

znode節點存儲的數據爲字節數組。存儲數據的格式zookeeper不做限制,也不提供解析,需要應用自己實現。

實際上圖就是主從模式存儲數據的示例,這裏先簡單講解:

  1. /master,存儲了當前主節點的信息
  2. /workers,下面的每個子znode代表一個從節點,子znode上存儲的數據,如“foo.com:2181”,代表從節點的信息。
  3. /tasks,下面的每個子znode代表一個任務,子znode上存儲的信息如“run cmd”,代表該內務內容
  4. /assign,下面每個子znode代表一個從節點的任務集合。如/assign/worker-1,代表worker-1這個從節點的任務集合。/assign/worker-1下的每個子znode代表分配給worker-1的一個任務。

持久節點(persistent)和臨時節點(ephemeral)

持久節點只能通過delete刪除。臨時節點在創建該節點的客戶端崩潰或關閉時,自動被刪除。

前面例子中的/master應該使用臨時節點,這樣當主節點失效或者退出時,該znode被刪除,其他節點知道主節點崩潰了,開始進行選舉的邏輯。另外/works/worker-1也應該是臨時節點,在此從節點失效的時候,該臨時節點自動刪除。

在目前的版本,由於臨時znode會因爲創建者會話過期被刪除,所以不允許臨時節點擁有子節點。

有序節點

znode可以被設置爲有序(sequential)節點。有序znode節點被分配唯一一個單調遞增的證書。如果創建了個一有序節點爲/workers/worker-,zookeeper會自動分配一個序號1,追加在名字後面,znode名稱爲/workers/worker-1。通過這種方式,可以創建唯一名稱znode,並且可以直觀的看到創建的順序。

znode支持的操作及暴露的API

create /path data

    創建一個名爲/path的znode,數據爲data。

delete /path

    刪除名爲/path的znode。

exists /path

    檢查是否存在名爲/path的znode

setData /path data

    設置名爲/path的znode的數據爲data

getData /path

    返回名爲/path的znode的數據

getChildren /path

    返回所有/path節點的所有子節點列表

1.4.2 觀察與通知

分佈式應用需要及時知道zookeeper中znode的變化,從而瞭解到分佈式應用整體的狀況,如果採用輪詢方式,代價太大,絕大多數查詢都是無效的。因此,zookeeper採用了通知的機制。客戶端向zookeeper請求,在特定的znode設置觀察點(watch)。當該znode發生變化時,會觸發zookeeper的通知,客戶端收到通知後進行業務處理。觀察點觸發後立即失效。所以一旦觀察點觸發,需要再次設置新的觀察點。

我們使用Zookeeper不能期望能夠監控到節點每次的變化。思考如下場景:

1、客戶端C1設置觀察點在/tasks

2、觀察點觸發,C1處理自己的邏輯

3、C1設置新的觀察點前,C2更新了/tasks

4、C1處理完邏輯,再次設置了觀察點。

此時C1不會得到第三步的通知,因此錯過了C2更新/tasks這次操作。要想不錯過這次更新,C1需要在設置監視點前讀取/tasks的數據,進行對比,發現更新。

再如下面的場景:

1、客戶端C1設置觀察點在/tasks

2、/tasks上發生了連續兩次更新

3、C1在得到第一次更新的通知後就讀取了/tasks的數據

4、此時第二次更新也已經發生,C1用第一次的通知,讀取到兩次更新後的數據

此時C1雖然錯過了第二次通知,但是C1最終還是讀取到了最新的數據。

因此Zookeeper只能保證最終的一致性,而無法保證強一致性。

zookeeper可以定義不同的觀察類型。例如觀察znode數據變化,觀察znode子節點變化,觀察znode創建或者刪除。

1.4.3 版本

每個znode都有版本號,隨着每次數據變化自增。setData和delete,以版本號作爲參數,當傳入的版本號和服務器上不一致時,調用失敗。當多個zookeeper客戶端同時對一個znode操作時,版本將會起到作用,假設c1,c2同時往一個znode寫數據,c1先寫完後版本從1升爲2,但是c2寫的時候攜帶版本號1,c2會寫入失敗。

1.4.4 法定人數

zookeeper服務器運行於兩種模式:獨立模式和仲裁模式(集羣)。仲裁模式下,會複製所有服務器的數據樹。但如果讓客戶端等待所有複製完成,延遲太高。這裏引入法定人數概念,指爲了使zookeeper集羣正常工作,必須有效運行的服務器數量。同時也是服務器通知客戶端保存成功前,必須保存數據的服務器最小數。例如我們有一個5臺服務器的zookeeper集羣,法定人數爲3,只要任何3個服務器保存了數據,客戶端就會收到確認。只要有3臺服務器存活,整個zookeeper集羣就是可用的。

下圖展示了客戶端提交請求到收到回覆的過程:

e2a60dd13fb346efa5de25af20a5f108009.jpg

法定人數需要大於服務器數量的一半。也稱爲多數原則。舉個例子說明,假如集羣有5臺服務器,法定人數爲2,那麼有2臺服務器參與複製即可,若這2臺server剛剛複製完/z這個znode,就掛掉了。此時剩下了3臺server,大於法定人數2,所以zookeeper認爲集羣正常,但這三臺服務器是無法發現/z這個znode的。如果法定人數大於服務器數量一半,那麼法定人數複製完成,就可以確保集羣存活時,至少有一臺服務器有最新的znode,否則集羣認爲自己已經崩潰。

下面兩個例子闡明瞭,爲何要遵循多數原則。

下圖展示了5臺server,法定人數爲3,在確保zookeeper集羣存活的前提下,最壞的情況掛了2臺server(剩餘及器數量3>=法定人數3),zookeeper是如何能確保數據完備,集羣繼續工作的。

095584baf8c71464ddff368c55e18f7513c.jpg

接下來兩張圖展示了5臺server,未遵循多數原則,法定人數設爲2。同樣掛了兩臺server時,爲什麼zookeeper集羣會出問題。

bb721cf4da8e5fd48d12d43f0110f3fe9e3.jpg

首先,客戶端發起請求,2個server複製數據後即返回客戶端接收成功。

就在此刻,很不幸,在繼續同步更新給其他節點前,剛剛兩個複製了數據的節點掛了。此時會怎樣呢?如下圖:

36097ca06ac432adc395127b6d175e3a8df.jpg

可以看到創建/z的操作在zookeeper集羣中丟失了。

相信通過以上講解,你已經能夠理解爲什麼法定人數一定要多於一半服務器的數量。

此外,我們要儘量選用奇數個服務器,這樣集羣能容忍崩潰服務器佔比更大,性價比更高。例如4臺服務器的集羣,法定人數最少爲3,那麼只能允許1臺服務器崩潰,也就是僅允許25%的機器崩潰。而5臺服務器的集羣,法定人數最少也是3,但是此時允許2臺服務器崩潰。換句話講,40%的機器崩潰後還能工作。

仲裁模式下,負載均衡通過客戶端隨機選擇連接串中的某個服務器來實現。

 
1.4.5 會話

客戶端對zookeeper集羣發送任何請求前,需要和zookeeper集羣建立會話。客戶端提交給zookeeper的所有操作均關聯在一個會話上。當一個會話因某種原因終止時,會話期間創建的臨時節點將會消失。而當會話服務器發生問題,無法繼續通信時,會話將被透明的轉移到集羣中另外一臺zookeeper的服務器上。

會話提供了順序保障。同一個會話中的請求以FIFO順序執行。併發會話的FIFO順序無法保證。

 
1.4.6 會話狀態和生命週期

會話狀態有:

connecting、connected、closed、not_connected

創建會話時,需要設置會話超時這個重要的參數。如果經過時間t後服務接受不到這個會話的任何消息,服務就會聲明會話過期。客戶端側,t/3時間未收到任何消息,客戶端向服務器發送心跳消息,2t/3時間後,客戶端開始尋找其他服務器。此時他有t/3的時間去尋找,找不到的話,會話失效。

重連服務器時,只有更新大於客戶端的服務器才能被連接,以免連接到落後的服務器。zookeeper中通過更新建立的順序,分配事務標識符。只有服務器的事物標識符大於客戶端攜帶的標識符時,纔可連接。
 

1.5 zookeeper的特性

  • 順序一致性,從同一個客戶端發起的事務請求,最終將會嚴格地按照其發起順序被應用到Zookeeper中去。

  • 原子性,所有事務請求的處理結果在整個集羣中所有機器上的應用情況是一致的,即整個集羣要麼都成功應用了某個事務,要麼都沒有應用。

  • 單一視圖,無論客戶端連接的是哪個 Zookeeper 服務器,其看到的服務端數據模型都是一致的。

  • 可靠性,一旦服務端成功地應用了一個事務,並完成對客戶端的響應,那麼該事務所引起的服務端狀態變更將會一直被保留,除非有另一個事務對其進行了變更。

  • 實時性,Zookeeper 保證在一定的時間段內,客戶端最終一定能夠從服務端上讀取到最新的數據狀態。


     
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章