Zookeeper 到底能做什麼?

語言: CN / TW / HK

theme: channing-cyan

我正在參加「掘金·啟航計劃」

Zookeeper 是一個高可用的分佈式數據管理與協調框架。隨着近年來互聯網規模的不斷擴大,大數據時代的到來,越來越多的分佈式系統將 Zookeeper 作為核心組件使用。本文章將重點介紹 Zookeeper 的應用場景。

數據發佈/訂閲

數據發佈/訂閲(Publish/Subscribe)系統,即所謂的配置中心。顧名思義就是發佈者將數據發佈到 ZooKeeper 的一個或一系列節點上,供訂閲者進行數據訂閲,進而達到動態獲取數據的目的,實現配置信息的集中式管理和數據的動態更新。

發佈/訂閲系統一般有兩種設計模式,分別是推(Push)模式和拉(Pull)模式。在推模式中,服務端主動將數據更新發送給所有訂閲的客户端;而拉模式則是由客户端主動發起請求來獲取最新數據,通常客户端都採用定時進行輪詢拉取的方式。ZooKeeper 採用的是推拉相結合的方式:客户端向服務端註冊自己需要關注的節點,一旦該節點的數據發生變更,那麼服務端就會向相應的客户端發送 Watcher 事件通知,客户端接收到這個消息通知之後,需要主動到服務端獲取最新的數據。

其中一個最典型的基於 Zookeeper 實現的配置中心就是 disconfdisconf 是一個分佈式配置管理平台(Distributed Configuration Management Platform),專注於各種分佈式系統配置管理的通用組件/通用平台, 提供統一的配置管理服務,是一套完整的基於 Zookeeper 的分佈式配置統一解決方案。

下面就是 Zookeeper 的目錄存儲結構 |----disconf |----app1_version1_env1 |----file |----confA.properties |----item |----keyA |----app2_version2_env2 |----file |----conf2.properties |----item |----key2 disconf 通過 disconf-web 管理配置信息,然後將配置的 keyZookeeper 上建立節點,disconf-client 啟動後拉取自身需要的配置信息並監聽 Zookeeper 的節點。在 web 上更新配置信息會觸發 Zookeeper 節點狀態的變動,client 可以實時感知到變化,然後從 web 上拉取最新配置信息。

disconf.png

命名服務

命名服務是指通過指定的名字來獲取資源或者服務的地址、提供者等信息。利用 Zookeeper 很容易創建一個全局的路徑,而這個路徑就可以作為一個名字,它可以指向集羣中的集羣,提供的服務的地址、遠程對象等。例如 Dubbo 使用 Zookeeper 來作為其命名服務,所有 Dubbo 相關的數據都組織在 /dubbo 的根節點下;二級目錄是服務名,如 com.foo.BarService;三級目錄有兩個子節點,分別是 providers 和 consumers,表示該服務的提供者和消費者。四級目錄記錄了與該服務相關的每一個應用實例的 URL 信息,在 providers 下的表示該服務的所有提供者,而在 consumers 下的表示該服務的所有消費者。舉例説明,com.foo.BarService 的服務提供者在啟動時將自己的 URL 信息註冊到 /dubbo/com.foo.BarService/providers 下;同樣的,服務消費者將自己的信息註冊到相應的 consumers 下,同時,服務消費者會訂閲其所對應的 providers 節點,以便能夠感知到服務提供方地址列表的變化。

zookeeper.jpg

負載均衡

Dubbo 中把 Zookeeper 作為一個服務的註冊中心時,基本流程:

loadbalance.png 1. 服務提供者 server 啟動時在ZK進行服務註冊(創建臨時文件); 2. 服務消費者 client 啟動時,請求 Zookeeper 獲取最新的服務存活列表並註冊 watcher,然後將獲得服務列表保存到本地緩存中; 3. client 請求 server 時,根據自己的負載均衡算法,從服務器列表選取一個進行通信。 4. 若在運行過程中,服務提供者出現異常或人工關閉不能提供服務,臨時節點失效,Zookeeper 探測到變化更新本地服務列表並異步通知到服務消費者,服務消費者監聽到服務列表的變化,更新本地緩存。 5. 後續 client 請求 server 時,根據負載均衡算法從新的服務地址中選出一台服務請求。

注意:服務發現可能存在延遲,因為服務提供者掛掉到緩存更新大約需要3-5s的時間(根據網絡環境不同還需仔細測試)。為了保證服務的實時可用,client請求server發生異常時,需要根據服務消費報錯信息,進行重負載均衡重試等。

集羣管理

集羣管理通常指監控集羣中各個主機的運行時狀態、存活狀況等信息。通過 Zookeeper 可以實現對集羣的隨時監控。其基本原理是使用zk的臨時節點來進行監控。如下圖所示,主機向zk註冊臨時節點,監控系統註冊監聽集羣下的臨時節點,從而獲取集羣中服務的狀態等信息。

master 選舉

Master 選舉是一個在分佈式系統中非常常見的應用場景。分佈式最核心的特性就是能夠將具有獨立計算能力的系統單元部署在不同的機器上,構成一個完整的分佈式系統。而與此同時,實際場景中往往也需要在這些分佈在不同機器上的獨立系統單元中選出一個所謂的“老大”,在計算機科學中,我們稱之為 Master

Kafka 中的 Controller 選舉就使用到了 Zookeeper。在於分佈式系統中,總會有一個地方需要對全局 meta 做一個統一的維護,KafkaController 就是充當這個角色的。Controller 是運行在 Broker 上的,任何一台 Broker 都可以作為 Controller,但是一個集羣同時只能存在一個 ControllerController 選舉的基本流程是: 1. 每個 broker 啟動的時候會去嘗試讀取 /controller 節點的 brokerid 的值並監聽這個節點,如果讀取到的 brokerid 的值不為-1,表示已經有其他broker節點成功競選為控制器,所以當前broker就會放棄競選;如果Zookeeper中不存在/controller 節點,或者這個節點的數據異常,那麼就會嘗試去創建 /controller 節點。當前broker去創建節點的時候,也有可能有其他 broker 同時去嘗試創建這個節點,只有創建成功的那個 broker 才會成為控制器。每個 broker 都會在內存中保存當前控制器的 brokerid 值。 2. 如果當前的 Controller 宕機,一段時間後 /controller 這個臨時節點將會被刪除,所有 broker 監聽到刪除事件會發起新一輪選舉,選舉方式跟步驟1相同。

分佈式鎖

分佈式鎖是控制分佈式系統之間同步訪問共享資源的一種方式。如果不同的系統或是同一個系統的不同主機之間共享了一個或一組資源,那麼訪問這些資源的時候,往往需要通過一些互斥手段來防止彼此之間的干擾,以保證一致性,在這種情況下,就需要使用分佈式鎖了。更多關於分佈式鎖的知識可以看一下這篇文章《一文帶你精通分佈式鎖》 加鎖的流程為: 1. 創建臨時節點 /my_lcok。 2. 創建成功則表明加鎖成功;創建失敗表示加鎖失敗,並監聽 /my_lcok 節點。

釋放鎖的流程: 1. 獲取鎖的客户端主動刪除 /my_lcok 節點或客户端宕機 /my_lcok 自動刪除。 2. 監聽/my_lcok 節點的所有客户端收到通知,然後執行加鎖流程。

注意:這個方法實現簡單,在客户端數量不多的情況下是比較好的加鎖方式,如果客户端的數量比較多,在釋放鎖通知客户端時會出現驚羣效應

zklock1.png 針對上述問題,我們使用臨時順序節點優化了加鎖流程。 1. 創建順序臨時節點 /my_lcokZookeeper 會自動在後面添加遞增的序號。 2. 獲取所有的臨時節點,判斷是否是序號最小的節點。 3. 如果為是,則表示加鎖成功。 4. 如果為否,則表示加鎖失敗,同時監聽上一個序號比自己小的節點。

釋放鎖的流程: 1. 獲取鎖的客户端主動刪除 /my_lcok-*** 節點或客户端宕機 /my_lcok-*** 自動刪除。 2. 監聽/my_lcok-*** 節點的客户端即序號第一個比這個節點大的客户端收到通知,然後執行加鎖流程。 zklock2.png

總結

本文章將重點介紹 Zookeeper 在數據發佈/訂閲、命名服務、負載均衡、集羣管理、master 選舉、分佈式鎖等場景的使用方法。對各個場景下的使用方法只是淺嘗輒止,當然 Zookeeper 使用場景還有很多,有興趣的同學可以自己深入瞭解一下。創作不易點個讚唄。 9NKbG.gif

參考文章

《從Paxos到Zookeeper》

disconf

dubbo

kafka