Zookeeper 到底能做什麼?
theme: channing-cyan
我正在參加「掘金·啟航計劃」
Zookeeper 是一個高可用的分散式資料管理與協調框架。隨著近年來網際網路規模的不斷擴大,大資料時代的到來,越來越多的分散式系統將 Zookeeper 作為核心元件使用。本文章將重點介紹 Zookeeper 的應用場景。
資料釋出/訂閱
資料釋出/訂閱(Publish/Subscribe
)系統,即所謂的配置中心。顧名思義就是釋出者將資料釋出到 ZooKeeper
的一個或一系列節點上,供訂閱者進行資料訂閱,進而達到動態獲取資料的目的,實現配置資訊的集中式管理和資料的動態更新。
釋出/訂閱系統一般有兩種設計模式,分別是推(Push
)模式和拉(Pull
)模式。在推模式中,服務端主動將資料更新發送給所有訂閱的客戶端;而拉模式則是由客戶端主動發起請求來獲取最新資料,通常客戶端都採用定時進行輪詢拉取的方式。ZooKeeper
採用的是推拉相結合的方式:客戶端向服務端註冊自己需要關注的節點,一旦該節點的資料發生變更,那麼服務端就會向相應的客戶端傳送 Watcher
事件通知,客戶端接收到這個訊息通知之後,需要主動到服務端獲取最新的資料。
其中一個最典型的基於 Zookeeper
實現的配置中心就是 disconf
。disconf
是一個分散式配置管理平臺(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
管理配置資訊,然後將配置的 key
在 Zookeeper
上建立節點,disconf-client
啟動後拉取自身需要的配置資訊並監聽 Zookeeper
的節點。在 web 上更新配置資訊會觸發 Zookeeper
節點狀態的變動,client 可以實時感知到變化,然後從 web 上拉取最新配置資訊。
命名服務
命名服務是指通過指定的名字來獲取資源或者服務的地址、提供者等資訊。利用 Zookeeper
很容易建立一個全域性的路徑,而這個路徑就可以作為一個名字,它可以指向叢集中的叢集,提供的服務的地址、遠端物件等。例如 Dubbo
使用 Zookeeper
來作為其命名服務,所有 Dubbo
相關的資料都組織在 /dubbo
的根節點下;二級目錄是服務名,如 com.foo.BarService
;三級目錄有兩個子節點,分別是 providers
和 consumers
,表示該服務的提供者和消費者。四級目錄記錄了與該服務相關的每一個應用例項的 URL 資訊,在 providers
下的表示該服務的所有提供者,而在 consumers
下的表示該服務的所有消費者。舉例說明,com.foo.BarService
的服務提供者在啟動時將自己的 URL 資訊註冊到 /dubbo/com.foo.BarService/providers
下;同樣的,服務消費者將自己的資訊註冊到相應的 consumers
下,同時,服務消費者會訂閱其所對應的 providers
節點,以便能夠感知到服務提供方地址列表的變化。
負載均衡
在 Dubbo
中把 Zookeeper
作為一個服務的註冊中心時,基本流程:
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 做一個統一的維護,Kafka
的 Controller
就是充當這個角色的。Controller
是執行在 Broker
上的,任何一臺 Broker
都可以作為 Controller
,但是一個叢集同時只能存在一個 Controller
。Controller
選舉的基本流程是:
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
節點的所有客戶端收到通知,然後執行加鎖流程。
注意:這個方法實現簡單,在客戶端數量不多的情況下是比較好的加鎖方式,如果客戶端的數量比較多,在釋放鎖通知客戶端時會出現驚群效應。
針對上述問題,我們使用臨時順序節點優化了加鎖流程。
1. 建立順序臨時節點 /my_lcok
, Zookeeper
會自動在後面新增遞增的序號。
2. 獲取所有的臨時節點,判斷是否是序號最小的節點。
3. 如果為是,則表示加鎖成功。
4. 如果為否,則表示加鎖失敗,同時監聽上一個序號比自己小的節點。
釋放鎖的流程:
1. 獲取鎖的客戶端主動刪除 /my_lcok-***
節點或客戶端宕機 /my_lcok-***
自動刪除。
2. 監聽/my_lcok-***
節點的客戶端即序號第一個比這個節點大的客戶端收到通知,然後執行加鎖流程。
總結
本文章將重點介紹 Zookeeper
在資料釋出/訂閱、命名服務、負載均衡、叢集管理、master 選舉、分散式鎖等場景的使用方法。對各個場景下的使用方法只是淺嘗輒止,當然 Zookeeper
使用場景還有很多,有興趣的同學可以自己深入瞭解一下。創作不易點個讚唄。