1. Zookeeper是什麼
Zookeeper是一個開源的分佈式應用程序協調服務器,其爲分佈式提供一致性服務。分佈式應用程序可以基於Zookeeper實現諸如數據發佈/訂閱、負載均衡、命名服務、分佈式協調/通知、集羣管理、Master選舉、分佈式鎖和分佈式隊列等功能。
2. Zookeeper的一致性協議
Zookeeper專門定製了一個一致性協議叫做ZAB(Zookeeper Atomic BroadCast) 原子廣播協議,該協議能夠很好的支持崩潰恢復。
2.1 ZAB協議的三個角色
- Leader:集羣中唯一的寫請求處理者,能夠發起投票
- Follower:能夠接受客戶端的請求,如果是讀請求則自己處理,如果是寫請求則轉發給Leader。在Leader的選舉過程中會參與投票,有選舉權和被選舉權。
- Observer:沒有選舉權和被選舉權,其它和Follower一致
2.1.1 角色的四種狀態
- LOOKING
- FOLLOWING
- LEADING
- OBSERVING
2.2 ZAB協議的兩種模式
- 消息廣播:當集羣中有一半的Follower服務器完成了和Leader服務器的狀態同步,那麼整個服務框架就進入了消息廣播模式
- 崩潰恢復:當Leader服務器不能正常工作時,會進入崩潰模式,選舉新的Leader,當新的Leader選舉成功後,如果有過半的Follower服務器完成與Leader服務器的狀態同步則退出崩潰模式。當新的集齊加入集羣的時候,如果已經存在Leader服務器則直接進入崩潰模式,找到Leader進行數據同步
3. Zookeeper的幾個重要概念
要想掌握Zookeeper的使用,還需要了解重要的概念。如Zookeeper的數據模型、會話機制、權限控制ACL、事件監聽Watcher機制等
3.1 數據模型
Zookeeper的數據存儲結構與標準的Unix文件系統相似,都是樹形結構。但是Zookeeper沒有目錄和文件的結構,每個節點是使用znode作爲數據節點。
每個znode有自己的節點類型和節點狀態
3.1.1 znode類型
- 持久節點:一旦創建就永久存在,除非手動刪除
- 持久有序節點:一個父節點可以爲其子節點維護一個創建的先後順序,這個順序體現在節點的名稱上,會自動在節點後自動添加一個10位數字組成的數字串,從0開始。
- 臨時節點:臨時節點的生命週期和客戶端回話綁定,回話結束則節點消失。臨時節點只能作爲子節點,不能創建子節點
- 臨時有序節點:同持久有序節點類似
3.1.2 znode狀態
- czxid:該節點被創建時的事務ID
- mzxid:節點最後修改時的事務ID
- ctime:節點創建時的時間
- mtime:節點最後修改的時間
- version:節點的版本號,更新刪除節點可以填寫,類似於樂觀鎖
- cversion:子節點 的版本號
- aversion:節點ACL版本號
- ephemeralOwner:創建該節點會話的sessionID。如果該節點爲持久節點,則該值爲0
- dataLength:節點數據內容長度
- numChildre:該節點子節點的個數,如果爲臨時節點則爲0
- pzxid:該節點的子節點列表最後一次被修改時的事務ID
3.2 會話
Zookeeper客戶端和服務端之間是通過tcp長連接維持的回話機制
在Zookeeper中,會話也有對應的事件,比如CONNECTION_LOSS連接丟失、SESSION_MOVED會話轉移、SESSION_EXPIRED會話超時等事件
3.3 權限控制ACL
Zookeeper權限控制,共有4種權限控制方案和5種權限
3.3.1 權限控制方案
- world:只有一個用戶anyone,默認代表所有人
- ip:使用ip地址進行認證
- auth:使用已添加認證的用戶認證
- digest:使用用戶名、密碼方式認證
3.3.2 權限
- create:有創建節點權限
- delete:有刪除節點權限
- read:有讀取節點數據及顯示子節點列表權限
- write:有設置節點數據權限
- admin:有設置節點訪問控制列表權限
3.4 事件監聽Watcher
客戶端註冊指定類型的watcher,當事件觸發的時候,執行自定義watcher。值得注意的是一次事件註冊,只會執行一次。
4. Zookeeper的常用命令
當前記錄的爲3.6.0版本的命令,其他版本的命令可能稍有不同。
4.1 節點操作
4.1.1 創建節點
create [-s] [-e] [-c] [-t ttl] path [data] [acl]
參數詳解:
- -s:創建有序節點
- -e:創建臨時節點,默認我持久化節點
- -c:創建容器節點,容器節點的當子節點都刪除後,容器節點本身也刪除
- -t:節點存活時間
- acl:權限控制
4.1.2 刪除節點
delete [-v version] path
deleteall path [-b batch size]
參數詳解:
- -v:節點版本號
4.1.3 修改節點
set [-s] [-v version] path data
參數詳解:
- -s:設置並返回當前節點狀態
- -v:節點版本號
4.1.4 查看節點
get [-s] [-w] path
參數詳解:
- -s:查看並返回節點當前狀態
- -w:設置watcher
4.1.5 查看節點狀態
stat [-w] path
參數詳解:
- -w:設置watcher
4.1.6 查看子節點信息
ls [-s] [-w] [-R] path
參數詳解:
- -s:查看子節點列表並返回當前節點狀態
- -w:設置watcher
- -R:遞歸顯示所有節點
4.2 權限操作
4.2.1 設置權限
setAcl [-s] [-v version] [-R] path acl
參數詳解:
- -s:設置權限並返回當前節點狀態
- -v:節點版本號
- -R:遞歸設置所有子節點權限
4.2.2 查看權限
getAcl [-s] path
參數詳解:
- -s:查看權限並返回當前節點狀態
4.2.3 添加權限用戶
addauth scheme auth
5. Zookeeper幾種典型的應用場景
Zookeeper到底有什麼用,在我們實際的開發過程中,我們可以使用它來幫助我們做什麼?
5.1 選主
選主場景的實現主要使用的是Zookeeper的強一致性,即使在高併發情況下也無法創建重複節點
大致思路:多個客戶端選主的時候,我們可以讓客戶端創建同一個路徑的臨時節點,創建成功的即爲Master節點。當Master無法正常使用時,因爲我們創建的是臨時節點,那麼臨時節點在Master宕機,斷掉與服務器的會話後,臨時節點就會被刪除。這是我們在客戶端上監聽臨時節點的變化,如果臨時節點被刪除,表示Master宕機了,則進行Master的二次選舉。
5.2 分佈式ID
在日常的開發中,我們會遇到分庫分表的情況,這種情況下普通的使用數據庫自增長ID可能無法滿足需求。使用UUID做ID會顯得比較臃腫。這是我們可以使用Zookeeper實現分佈式唯一ID
分佈式ID的實現主要使用Zookeeper可以創建臨時有序節點的特性
大致思路:在一個節點下創建臨時有序的子節點,然後截取子節點的有序號作爲ID
5.3 分佈式鎖
分佈式鎖的實現方式有很多種,比如 Redis
、數據庫 、zookeeper
等。個人認爲 zookeeper
在實現分佈式鎖這方面是非常非常簡單的。
上面我們已經提到過了 zk在高併發的情況下保證節點創建的全局唯一性,這玩意一看就知道能幹啥了。實現互斥鎖唄,又因爲能在分佈式的情況下,所以能實現分佈式鎖唄。
如何實現呢?這玩意其實跟選主基本一樣,我們也可以利用臨時節點的創建來實現。
首先肯定是如何獲取鎖,因爲創建節點的唯一性,我們可以讓多個客戶端同時創建一個臨時節點,創建成功的就說明獲取到了鎖 。然後沒有獲取到鎖的客戶端也像上面選主的非主節點創建一個 watcher
進行節點狀態的監聽,如果這個互斥鎖被釋放了(可能獲取鎖的客戶端宕機了,或者那個客戶端主動釋放了鎖)可以調用回調函數重新獲得鎖。
zk
中不需要向redis
那樣考慮鎖得不到釋放的問題了,因爲當客戶端掛了,節點也掛了,鎖也釋放了。是不是很簡答?
那能不能使用 zookeeper
同時實現 共享鎖和獨佔鎖 呢?答案是可以的,不過稍微有點複雜而已。
還記得 有序的節點 嗎?
這個時候我規定所有創建節點必須有序,當你是讀請求(要獲取共享鎖)的話,如果 沒有比自己更小的節點,或比自己小的節點都是讀請求 ,則可以獲取到讀鎖,然後就可以開始讀了。若比自己小的節點中有寫請求 ,則當前客戶端無法獲取到讀鎖,只能等待前面的寫請求完成。
如果你是寫請求(獲取獨佔鎖),若 沒有比自己更小的節點 ,則表示當前客戶端可以直接獲取到寫鎖,對數據進行修改。若發現 有比自己更小的節點,無論是讀操作還是寫操作,當前客戶端都無法獲取到寫鎖 ,等待所有前面的操作完成。
這就很好地同時實現了共享鎖和獨佔鎖,當然還有優化的地方,比如當一個鎖得到釋放它會通知所有等待的客戶端從而造成 羊羣效應 。此時你可以通過讓等待的節點只監聽他們前面的節點。
具體怎麼做呢?其實也很簡單,你可以讓 讀請求監聽比自己小的最後一個寫請求節點,寫請求只監聽比自己小的最後一個節點 ,感興趣的小夥伴可以自己去研究一下
5.4 註冊中心和集羣管理
註冊中心和集羣管理的實現主要是利用Zookeeper的臨時節點
5.4.1 註冊中心
大致思路:服務提供者在Zookeeper中創建臨時節點,將自身的一些信息寫入節點中,如IP、Port等信息。服務消費者獲取節點下所有的臨時子節點,既所有的服務提供者,並將信息緩存至本地。當消費者調用服務時,不會再去請求註冊中心,而是直接通過負載均衡算法從地址列表中取一個服務提供者的服務器調用服務。
當服務提供者信息改變或者下線的時候,因爲是臨時節點,所以服務提供者的信息會自動刪除
5.4.2 集羣管理
大致思路:我們可以爲每條機器創建臨時節點,並監控其父節點,如果子節點列表有變動(我們可能創建刪除了臨時節點),那麼我們可以使用在其父節點綁定的 watcher
進行狀態監控和回調。