ZooKeeper是一個分散式的,開放原始碼的分散式應用程式協調服務,是Google
的Chubby
一個開源的實現。
Apache ZooKeeper is an effort to develop and maintain an open-source server which enables highly reliable distributed coordination.
原理介紹和說明
- 一致性演算法
ZooKeeper以Fast Paxos
(帕克索斯)演算法為基礎,讓叢集中的每個zk例項資料保持一致。一般部署叢集,機器數設定為奇數個,更容易滿足>N/2的投票條件。
- 儲存模型
/
├── app1
│ ├── p_1
│ ├── p_2
│ └── p_3
└── app2
複製程式碼
類似作業系統資料夾的樹型模型,與資料夾的區別在於,每個節點上可儲存資料(不超過
1M
),每個節點上的資料可帶版本號。
zNode
節點型別
2
個劃分維度:按節點是否可持久化儲存,分為持久節點與臨時節點;按節點序號是否可順序遞增(類似mysql
的auto_increment
),分為順序節點及非順序節點,
注:3.6.0版本以後,還新增了Container Nodes
(容器節點),該節點的特點是,如果其下的所有子節點都被刪除,該節點也會在將來某個時間被刪除。
ZooKeeper has the notion of container nodes. Container nodes are special purpose nodes useful for recipes such as leader, lock, etc. When the last child of a container is deleted, the container becomes a candidate to be deleted by the server at some point in the future.
持久節點
(client
建立持久節點後,就算與zk
斷開,節點仍然儲存在zk
中)
持久順序節點
(eg: /order/quartz/wm000001 , /order/quartz/wm000002, /order/quartz/wm000003...)
臨時節點
(client
與zk
斷開連線後,節點自動刪除)
臨時順序節點
- 事件監聽
Created event
(節點建立事件)Deleted event
(節點刪除事件)Changed event
(節點的資料變化事件) -zkClient.subscribeChildChanges();
//訂閱子節點的變化zkClient.subscribeDataChanges();
//訂閱某節點的資料變化(包括資料被刪除)事件zkClient.subscribeStateChanges();
//訂單狀態變化(狀態包括:連線,斷開,認證失敗等等)
以上為常用事件,其它事件請參考官方文件。實際使用中,很少用原生寫法來監聽事件,而是藉助一些第三方的開源zk客戶端,比如zkClient來監聽事件。
-
ACL(Access Control List)
許可權控制- 每個節點有5種操作許可權:
Create、Read、Write、Delete、Admin
簡稱crwda
。其中:Delete
是指對子節點是否具有刪除許可權,其它4種許可權指對自身節點的操作許可權。 - 身份認證方式:
world:
預設方式,無限制,全世界均能訪問。auth:
在上下文中新增授權使用者。digest:
使用者名稱/密碼認證ip: ip
地址認證
- 每個節點有5種操作許可權:
應用場景
- 應用場景1:分散式配置
要點:配置資訊儲存在
db
與zk
中(保險起見,資料安全性更高),弄一個後臺管理介面,對配置修改後,先儲存到db
,然後同步寫入zk
的節點中。應用啟動時,先連到zk
上讀取節點中的配置,同時監聽節點的資料變化,當配置變化時,得到實時通知。
- 應用場景2:消除單點故障
(Single Point of Failure,SPOF)
└── ./OrderNoService
├── A0000001
10.0.0.1:8001
└── A0000002
10.0.0.2:8001
複製程式碼
多個服務例項,啟動時在
zk
上臨時順序節點,服務的呼叫方約定取最小節點為Master
,當master
掛掉後,節點自動刪除,呼叫方得到事件通知,取新的最小節點來呼叫(
相當於slave
提升為master)
- 應用場景3:去中心化
上圖中,左邊為傳統中心化的架構,缺點是每次有新的服務例項加入或下線,都要調整
nginx
中心節點的配置(
不管是人工,還是藉助工具自動)
,不利於雲時代的動態彈性調整,而且整體的可用性強依賴於中心節點,一旦中心節點(
中心叢集)
全掛掉,系統就不可用了。
- 應用場景4:分散式鎖
Order
└── 3456890
├── 000001
├── 000002
└── 000003
複製程式碼
原理:以多個程式執行例項同時在處理訂單
3456890
為例,每個程式執行例項處理前,建立一個臨時順序節點,然後檢查自己建立的節點是否為最小,如果不是,表明沒搶到鎖,如果是,表示搶到了鎖,搶到鎖的程式處理完以後,刪除該節點表示釋放鎖。同時,其它處於等候狀態的程式,為了實時得到鎖的釋放通知,均監聽父節點Order/3456890
的子節點變化,發現子節點變化時,重複剛才的檢測過程,直到自己建立的節點變成最小為止。 與redis SETNX
之類的分散式鎖相比,zk
的分散式鎖,還能實現解決Top N
之類的有限資源競爭問題(類似併發中的訊號量)。比如:一堆程式要列印,但是隻有2臺印表機(或者列印佇列的長度只有2,最多同時只能允許2個程式提交列印任務), 類似剛才的思路 ,可以檢測最小的前2個節點,只有建立最小前2個節點的程式,才認為是拿到了訊號,允許提交列印任務。
- 應用場景5:分散式佇列
Queue
└── Queue1
├── 000001
├── 000002
└── 000003
複製程式碼
如上圖,建立
Queue/Queue1
做為一個佇列(或Topic
),然後每建立一個順序節點,視為一條訊息(節點儲存的資料即為訊息內容),生產者每次建立一個新節點,做為訊息傳送,消費者監聽Queue1
的子節點變化(或定時輪詢),每次取最小節點當做消費訊息,處理完後,刪除該節點。相當於實現了一個FIFO
(先進先出)的佇列。 注:zk
框架強制的是CP
(一致性),而非專為高併發、高效能場景設計的,如果在高併發,qps
很高的情況下,分散式佇列需酌情考慮。
- 應用場景7:生成分散式唯一
id
Order
└── OrderId
├── 000001
├── 000002
└── 000003
複製程式碼
思路:每次要生成一個新
Id
時,建立一個持久順序節點,建立操作返回的節點序號,即為新Id
,然後把比自己節點小的刪除即可。
終篇
目前很多開源專案,幾乎都是或多或少依賴
zookeeper
,比如:dubbo,disconf,kafka,...
分散式環境中,
zk
能用於什麼場景,基本上取決於開發人員的想象力!