分佈式橋樑ZooKeeper開發體驗

從傳統Java Web轉入分佈式系統應用,再到接觸分佈式協調框架ZooKeeper,通過痛苦的思維邏輯和理念轉變,歷經一個月時間,小夥伴們終於把ZooKeeper嵌入到了BoCloud博雲的BeyondContainer中,並在其上進行相應功能的開發:服務註冊與發現、集羣管理、模塊的高可用及分佈式鎖等。

在選定ZooKeeper之前,我們對其他的分佈式框架也進行了調研和對比,分別 有etcd和consul。對於etcd而言,在原生接口和提供服務方式方面,etcd更適合作爲集羣配置服務器,用來存儲集羣中的大量數據,方便的REST接口也可以讓集羣中的任意一個節點在使用key |value服務時獲取方便,然而etcd在監控服務的狀態和通知方面比較麻煩;consul方面,是使用Go語言開發的分佈式協調,對業務發現的管理提供很好的支持,他的HTTP API也能很好的和不同的語言綁定,但在業務檢測方面有一定的延時,不太適合實時響應的情景;並且etcd和consul需要其他組件的配合才能達到ZooKeeper的服務能力,故ZooKeeper則更加的適合於提供分佈式協調服務,實現分佈式鎖模型方面也較爲簡單方便,並且功能全,社區活躍,用戶羣體很大,對所有典型的用例都有很好的封裝,支持不同語言的綁定,故最終選定ZooKeeper。

在產品開發中使用ZooKeeper是一件有趣的事情,下面就讓我們來詳細扒一扒我們的開發體驗吧。

一、簡單介紹

先來簡單說明下ZooKeeper原理,再來談談在產品中的具體使用。ZooKeeper是一個開放源碼的分佈式應用程序協調服務,由知名互聯網公司雅虎創建,是Google Chubby的開源實現。設計目標是爲分佈式提供一致性服務,其沒有直接採用Paxos算法,而是採用了被稱爲ZAB的一致性協議。ZooKeeper具有以下幾個特徵:

1、數據模型

ZooKeeper使用一個共享的、樹形結構的名字空間-數據模型,其由一系列被稱爲ZNode的的數據節點組成,其層級關係,如文件系統的目錄結構一樣,結構如下圖所示:

每個Znode上都會保存自己的數據內容以及屬性信息,節點分爲持久節點和臨時節點,持久節點除非進行刪除操作,否則將會一直保存在ZooKeeper上;而臨時節點,他的生命週期和客戶端會話綁定,一旦會話失效,這個客戶端所創建的所有臨時節點都會被刪除。另外,可以給節點加上一個屬性:sequential,節點創建的時候,會自動在節點後面追加一個由父節點維護的自增數字。

2、構建集羣

一個ZooKeeper集羣通常由一組機器組成,一般3-5臺機器就可以組成一個可用的集羣,其結構和工作原理如下圖所示:

只要集羣中存在超過一半的機器能夠正常工作,集羣就能夠正常對外服務。

ZooKeeper具有以下三種角色:

Leader: 爲客戶端提供讀寫服務

Follower:爲客戶端提供讀服務,參與Leader選舉過程

Observer:爲客戶端提供讀服務,不參與Leader選舉過程

3、順序訪問

對於客戶端的每個更新請求,ZooKeeper都會分配一個全局唯一的遞增編號,反映了所有事務操作的先後順序,通過這個特點來實現更高層次的同步原語。

4、高性能

由於ZooKeeper將全局數據存儲在內存中,並直接服務於客戶端的所有非事物請求,因此他特別適用於以讀寫操作爲主的應用場景。

二、核心處理

在學習、調研和實際產品使用的過程中,總結了兩種ZooKeeper的處理方式,以應對各種使用場景的實現。分別爲:阻塞式和自殺式。

阻塞式:

所有實例會向ZooKeeper的指定路徑下把實例ip:port註冊上去,注意,這裏註冊的節點類型是一個臨時順序節點。完成註冊後,每個實例都可以獲取到所有的節點列表,再通過對比判斷自己是否是所有節點中序號最小的,如果是最小的則作爲Leader,來執行定時任務或者數據庫同步。當Leader機器發生故障,會再次按“小序號優先”策略選出Leader繼續執行任務。具體的做法是,所有實例在指定路徑下注冊臨時順序子節點,並得到子節點列表,通過排序判斷自己是否是最小子節點,如果是即爲Leader,否則對前一個比自己序號小的節點,註冊一個“節點存在”的watcher監聽,來監控節點存在情況,一旦客戶端斷開連接或者會話失效,對應節點會被自動刪除,對它進行監控的實例會受到事件通知,以此類推,從而選出最小節點,即Leader。其流程圖如下:

自殺式:

與等待式最大的區別在於,當實例發現自己不是最小節點時,會自主刪除。具體的做法是,註冊“最小節點存在”的watcher監聽,來監控最小節點存在情況,一旦Leader機器與ZooKeeper斷開連接,會話失效,對應的節點會被自動刪除,則其他實例會接收到變更通知,再向指定路徑下注冊節點,並判斷自己是否是最小節點,如果是最小節點則爲Leader,不是,則自殺刪除自己註冊的節點。其流程如下:

這裏需要特別聲明下,所有的watcher都是一次性的,如果需要再次監控變化,需要再次註冊watcher監聽。

三、應用場景

接下來,我們談談ZooKeeper在產品中的具體實現和開發,以保證產品的分佈式運行。

1、服務註冊中心

隨着分佈式的發展和微服務的增多,需要一個管理這些服務的控制中心,即服務註冊中心,其主要提供所有服務註冊信息的中心存儲,同時負責將服務註冊信息的更新實時通知給服務消費者,主要通過ZooKeeper的Watcher機制實現。

服務啓動,註冊臨時節點,格式:/{project}/services/{module}/{ip:port},例如在192.168.1.200服務器上啓動某服務模塊,服務端口爲1234,則其註冊節點爲:/zk_project/services/zk_module/192.168.1.200:1234,並開始初始化服務緩存,把服務模塊所有節點信息保存在緩存中,且通過watcher監控路徑爲/{project}/services/{module}/下的節點變化,若發生變化,獲取節點信息更新緩存,保證緩存和ZooKeeper的信息保持一致,其他模塊亦是如此。當需要訪問其他模塊時,通過算法,得到相應的模塊ip:port信息,再通過Http訪問其他模塊,當然安全認證的過程是不可少的。架構設計如下如所示:

2、集羣管理

隨着分佈式系統規模的日益擴大,集羣中的機器規模也隨之變大,因此,如何更好地進行集羣管理也顯得越來越重要了,否則則是一盤散沙。

例如,在日常開發和運維的過程中,平臺需要知道當前有多少在線的服務器,以及對機器進行上下線的操作。爲了實現自動化的線上運維,我們對機器的上下線情況有一個全局的監控。首先將指定的agent部署到這些機器上並啓動,向ZooKeeper指定路徑下注冊臨時節點,完成後,對當前路徑watcher的監控就會接受到節點變更事件,即上線通知,同樣機器下線時,監控同樣會接收到下線通知,這樣便實現了對服務器上下線的檢測,最後同步數據庫,保證數據庫準確性以及和真實場景的一致性。其結構流程如下:

註明:這裏的同步數據庫操作,是由服務實例集羣的Leader執行,接下來的Leader選舉會詳細說明

3、Leader選舉

Leader選舉,它是用來保證集羣運行的過程中任務執行的唯一性和準確性,如果每個節點服務器都能執行Leader才能執行任務,會導致服務混亂以及數據的不準確性。

分佈式都是多實例多點運行,一個服務對應一個多實例服務集羣,這些實例運行着相同的程序,對外提供相同的服務。但某些任務只能由Leader執行,當Leader服務器down機或者發生故障時,需要從其他的實例中選舉一個Leader來繼續執行任務。Leader選舉就是通過上述自殺式處理方式實現的,因爲需要選出網絡性能最好的機器來當Leader。結構圖如下:

4、分佈式鎖

分佈式鎖是控制分佈式系統之間同步訪問共享資源的一種方式。如果不同的系統或者同一個系統的不同主機之間共享一個或一組資源,那麼訪問這些資源的時候,會需要通過一些手段來防止彼此之間的干擾,以保證一致性,這個情況下,就需要使用分佈式鎖了。

產品中分佈式鎖的實現是通過上述阻塞式,如果是最小節點則獲取鎖,進行事務處理,不是則大序號臨時節點會watcher監聽前一個小序號節點存在情況,如果小序號節點消失,大序號節點的實例會受到事件通知,再通過判斷,如果是最小,則獲取鎖,否則繼續監聽前一個小序號節點,以此類推循環,直到拿到鎖爲止。結構圖如下:

四、實踐總結

ZooKeeper在分佈式系統開發過程中,佔據着很重要的地位。在產品選型調研之後,選ZooKeeper作爲分佈式協調、服務註冊與發現等的解決方案,因爲它具有如下優點:

1、開源軟件,背景強悍,很多互聯網公司都在使用;

2、部署簡單、容易入門,提供簡單API;

3、支持多語言的客戶端;

4、存儲的可靠性強,表現爲配置管理、命名服務以及多備份;

5、可通過Watcher機制實現Push模型,節點信息的變更能夠及時發出通知;

6、客戶端與服務器通信的連接狀態自動維護,並能自動刪除節點並通知。

但是ZooKeeper也有自己缺點:

1、是一個CP型系統,所以當網絡分區問題發生時,系統就不能註冊或查找服務;

2、需要自己去實現服務註冊與發現邏輯;

3、使用ZooKeeper會帶來新的複雜性和運維問題。

不過,ZooKeeper在成熟、健壯及豐富的特性上具有着很大的優勢,所以最終決定採用ZooKeeper作爲我們的解決方案。

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