本文收錄在 GitHub 地址 https://github.com/chengxy-nds/Springboot-Notebookjavascript
嘮嘮叨叨
本文主要分享一下zookeeper
的一些基本概念,在正式進入正題前,和你們聊一聊剛入行時個人面試經驗,能夠說是耿直的有些可愛。java
面試官:用過zookeeper
嗎?node
我:用過啊,給dubbo
提供服務的註冊與發現嘛git
面試官:知道 zookeeper
是什麼嗎?程序員
我:知道啊,註冊中心嘛github
面試官:那大家項目中都是怎麼用 zookeeper
的?面試
我:就在 springboot
的 application.properties
配置文件裏添加一個 zookeeper
服務地址就好了。。。 . 上邊的對話好像也沒什麼毛病,但彷佛又感受哪裏有點不太對,結果就是每次我如此回答面試都被pass。redis
爲何會被問zookeeper?由於個人簡歷項目上寫着熟練使用zookeeper,可面試官理解的 「熟練」 使用可不是會配置,工程啓動不報錯那麼簡單。因此仍是有必要全面瞭解一下zookeeper的相關知識。spring
1、zookeeper初識?
Zookeeper
它做爲Hadoop
項目中的一個開源子項目,是一個經典的分佈式數據一致性解決方案,致力於爲分佈式應用提供一個高性能、高可用,且具備嚴格順序訪問控制能力的分佈式協調服務。shell
一、zookeeper數據模型
zookeeper
維護了一個相似文件系統的數據結構,每一個子目錄(/微信、/微信/公衆號)都被稱做爲 znode
即節點。和文件系統同樣,咱們能夠很輕鬆的對 znode
節點進行增長、刪除等操做,並且還能夠在一個znode
下增長、刪除子znode
,區別在於文件系統的是,znode
能夠存儲數據(嚴格說是必須存放數據,默認是個空字符)。
因爲zookeeper
是目錄節點結構,在獲取和建立節點時,必需要以「/」
開頭,不然在獲取節點時會報錯 Path must start with / character
。
[zk: localhost:2181(CONNECTED) 13] get test Command failed: java.lang.IllegalArgumentException: Path must start with / character
根節點名必須爲「/XXX」
,建立子節點時必需要帶上根節點目錄「/XXX/CCC」
、「/XXX/AAA」
。
例如:想要獲取下圖 程序員內點事
節點必須拼接完整的路徑 get /微信/公衆號/程序員內點事
get /微信/公衆號/程序員內點事
znode
被用來存儲 byte級
或 kb級
的數據,可存儲的最大數據量是1MB
(請注意:一個節點的數據量不只包含它自身存儲數據,它的全部子節點的名字也要折算成Byte數計入,所以znode
的子節點數也不是無限的)雖然能夠手動的修改節點存儲量大小,但通常狀況下並不推薦這樣作。
二、znode節點屬性
一個znode
節點不只能夠存儲數據,還有一些其餘特別的屬性。接下來咱們建立一個/test
節點分析一下它各個屬性的含義。
[zk: localhost:2181(CONNECTED) 6] get /test 456 cZxid = 0x59ac // ctime = Mon Mar 30 15:20:08 CST 2020 mZxid = 0x59ad mtime = Mon Mar 30 15:22:25 CST 2020 pZxid = 0x59ac cversion = 0 dataVersion = 2 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 3 numChildren = 0
節點屬性 | 註解 |
---|---|
cZxid | 該數據節點被建立時的事務Id |
mZxid | 該數據節點被修改時最新的事物Id |
pZxid | 當前節點的父級節點事務Id |
ctime | 該數據節點建立時間 |
mtime | 該數據節點最後修改時間 |
dataVersion | 當前節點版本號(每修改一次值+1遞增) |
cversion | 子節點版本號(子節點修改次數,每修改一次值+1遞增) |
aclVersion | 當前節點acl版本號(節點被修改acl權限,每修改一次值+1遞增) |
ephemeralOwner | 臨時節點標示,當前節點若是是臨時節點,則存儲的建立者的會話id(sessionId),若是不是,那麼值=0 |
dataLength | 當前節點所存儲的數據長度 |
numChildren | 當前節點下子節點的個數 |
咱們看到一個znode
節點的屬性比較多,但比較主要的屬性仍是zxid
、version
、acl
這三個。
Zxid:
znode
節點狀態改變會致使該節點收到一個zxid
格式的時間戳,這個時間戳是全局有序的,znode節點的創建或者更新都會產生一個新的。若是zxid1
的值 < zxid2
的值,那麼說明zxid2
發生的改變在zxid1
以後。每一個znode節點都有3個zxid
屬性,cZxid
(節點建立時間)、mZxid
(該節點修改時間,與子節點無關)、pZxid
(該節點或者該節點的子節點的最後一次建立或者修改時間,孫子節點無關)。
zxid
屬性主要應用於zookeeper
的集羣,這個後邊介紹集羣時詳細說。
Version:
znode
屬性中一共有三個版本號dataversion
(數據版本號)、cversion
(子節點版本號)、aclversion
(節點所擁有的ACL權限版本號)。
znode
中的數據能夠有多個版本,若是某一個節點下存有多個數據版本,那麼查詢這個節點數據就須要帶上版本號。每當咱們對znode
節點數據修改後,該節點的dataversion
版本號會遞增。當客戶端請求該znode
節點時,會同時返回節點數據和版本號。另外當dataversion
爲 -1
的時候能夠忽略版本進行操做。對一個節點設置權限時aclVersion
版本號會遞增,下邊會詳細說ACL權限控制。
驗證一下,咱們修改/test
節點的數據看看dataVersion
有什麼變化,發現dataVersion
屬性變成了 3,版本號遞增了。
[zk: localhost:2181(CONNECTED) 10] set /test 8888 cZxid = 0x59ac ctime = Mon Mar 30 15:20:08 CST 2020 mZxid = 0x59b6 mtime = Mon Mar 30 16:58:08 CST 2020 pZxid = 0x59ac cversion = 0 dataVersion = 3 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 4 numChildren = 0
三、znode的類型
zookeeper
有四種類型的znode
,在用客戶端 client
建立節點的時候須要指定類型。
zookeeper.create("/公衆號/程序員內點事", "".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
-
PERSISTENT
-持久化目錄節點 :client建立節點後,與zookeeper斷開鏈接該節點將被持久化,當client再次鏈接後節點依舊存在。 -
PERSISTENT_SEQUENTIAL
-持久化順序節點 :client建立節點後,與zookeeper斷開鏈接該節點將被持久化,再次鏈接節點還存在,zookeeper會給該節點名稱進行順序編號,例如:/lock/000000000一、/lock/000000000二、/lock/0000000003。 -
EPHEMERAL
-臨時目錄節點 : client與zookeeper斷開鏈接後,該節點即會被刪除 -
EPHEMERAL_SEQUENTIAL
-臨時順序節點 : client與zookeeper斷開鏈接後,該節點被刪除,會給該節點名稱進行順序編號,例如:/lock/000000000一、/lock/000000000二、/lock/0000000003。
2、節點的ACL權限控制
ACL
:即 Access Control List
(節點的權限控制),經過ACL
機制來解決znode
節點的訪問權限問題,要注意的是zookeeper
對權限的控制是基於znode
級別的,也就說節點之間的權限不具備繼承性,即子節點不繼承父節點的權限。
zookeeper
中設置ACL權限的格式由<schema>:<id>:<acl>
三段組成。
schema :表示受權的方式
world
:表示任何人均可以訪問auth
:只有認證的用戶能夠訪問digest
:使用username :password用戶密碼生成MD5哈希值做爲認證IDhost/ip
:使用客戶端主機IP地址來進行認證
id: 權限的做用域,用來標識身份,依賴於schema選擇哪一種方式。
acl:給一個節點賦予哪些權限,節點的權限有create,、delete、write、read、admin 統稱 cdwra
。
一、world
:表示任何人均可以訪問
咱們用 getAcl
命令來看一下,沒有設置過權限的znode
節點,默認狀況下的權限狀況。
[zk: localhost:2181(CONNECTED) 12] getAcl /test 'world,'anyone : cdrwa
看到沒有設置ACL屬性的節點,默認schema 使用的是world
,做用域是anyone
,節點權限是cdwra
,也就是說任何人均可以訪問。
那咱們若是要給一個schema 爲非world
的節點設置world
權限咋搞?
setAcl /test world:anyone:crdwa
二、auth
:只有認證的用戶能夠訪問
schema 用auth
受權表示只有認證後的用戶才能夠訪問,那麼首先就須要添加認證用戶,添加完之後須要對認證的用戶設置ACL權限。
addauth digest test:password(明文)
須要注意的是設置認證用戶時的密碼是明文的。
[zk: localhost:2181(CONNECTED) 2] addauth digest user:user //用戶名:密碼 [zk: localhost:2181(CONNECTED) 5] setAcl /test auth:user:crdwa [zk: localhost:2181(CONNECTED) 6] getAcl /test 'digest,'user:ben+k/3JomjGj4mfd4fYsfM6p0A= : cdrwa
實際上咱們這樣設置之後,就是將這個節點開放給全部認證的用戶,setAcl /test auth:user:crdwa
至關於setAcl /test auth::crdwa
。
三、digest
:用戶名:密碼的驗證方式
用戶名:密碼方式受權是針對單個特定用戶,這種方式是不須要先添加認證用戶的。
若是在代碼中使用zookeeper客戶端設置ACL,那麼密碼是明文的,但如果zk.cli等客戶端操做就須要將密碼進行sha1
及base64
處理。
setAcl <path> digest:<user>:<password(密文)>:<acl> setAcl /test digest:user:jalRr+knv/6L2uXdenC93dEDNuE=:crdwa
那麼密碼如何加密嘞?有如下幾種方式:
經過shell
命令加密
echo -n <user>:<password> | openssl dgst -binary -sha1 | openssl base64
使用zookeeper
自帶的類庫org.apache.zookeeper.server.auth.DigestAuthenticationProvider
生成
java -cp /zookeeper-3.4.13/zookeeper-3.4.13.jar:/zookeeper-3.4.13/lib/slf4j-api-1.7.25.jar \ org.apache.zookeeper.server.auth.DigestAuthenticationProvider \ root:root root:root->root:qiTlqPLK7XM2ht3HMn02qRpkKIE=
四、host/ip
:使用客戶端主機IP地址來進行認證
這種方式就比較好理解了,經過對特定的IP地址,也能夠是一個IP段進行受權。
[zk: localhost:2181(CONNECTED) 3] setAcl /test0000000014 ip:127.0.0.1:crdwa cZxid = 0x59ac ctime = Mon Mar 30 15:20:08 CST 2020 mZxid = 0x59b6 mtime = Mon Mar 30 16:58:08 CST 2020 pZxid = 0x59ac cversion = 0 dataVersion = 3 aclVersion = 3 // 這個版本一直在增長 ephemeralOwner = 0x0 dataLength = 4 numChildren = 0
3、zookeeper的靈魂 watcher
咱們在開頭就說過:zookeeper
能夠爲dubbo
提供服務的註冊與發現,做爲註冊中心,但你有想過zookeeper
爲啥可以實現服務的註冊與發現嗎?這就不得不說一下zookeeper
的靈魂 Watcher
(監聽者)。
一、watcher是個啥?
watcher
是zooKeeper
中一個很是核心功能 ,客戶端watcher
能夠監控節點的數據變化以及它子節點的變化,一旦這些狀態發生變化,zooKeeper服務端就會通知全部在這個節點上設置過watcher
的客戶端 ,從而每一個客戶端都很快感知,它所監聽的節點狀態發生變化,而作出對應的邏輯處理。
簡單的介紹了一下watcher
,那麼咱們來分析一下,zookeeper
是如何實現服務的註冊與發現。 zookeeper
的服務註冊與發現,主要應用的是zookeeper
的znode
節點數據模型和watcher
機制,大體的流程以下:
-
服務註冊: 服務提供者(
Provider
)啓動時,會向zookeeper服務端
註冊服務信息,也就是建立一個節點,例如:用戶註冊服務com.xxx.user.register
,並在節點上存儲服務的相關數據(如服務提供者的ip地址、端口等)。 -
服務發現: 服務消費者(
Consumer
)啓動時,根據自身配置的依賴服務信息,向zookeeper服務端
獲取註冊的服務信息並設置watch監聽
,獲取到註冊的服務信息以後,將服務提供者的信息緩存在本地,並進行服務的調用。 -
服務通知: 一旦服務提供者因某種緣由宕機再也不提供服務以後,客戶端與
zookeeper
服務端斷開鏈接,zookeeper
服務端上服務提供者對應服務節點會被刪除(例如:用戶註冊服務com.xxx.user.register
),隨後zookeeper
服務端會異步向全部消費用戶註冊服務com.xxx.user.register
,且設置了watch監聽
的服務消費者發出節點被刪除的通知,消費者根據收到的通知拉取最新服務列表,更新本地緩存的服務列表。
上邊的過程就是zookeeper
能夠實現服務註冊與發現的大體原理。
二、watcher類型
znode
節點能夠設置兩類watch
,一種是DataWatches
,基於znode節點的數據變動從而觸發 watch
事件,觸發條件getData()
、exists()
、setData()
、 create()
。
另外一種是Child Watches
,基於znode的孩子節點發生變動觸發的watch事件,觸發條件 getChildren()
、 create()
。
而在調用 delete()
方法刪除znode時,則會同時觸發Data Watches
和Child Watches
,若是被刪除的節點還有父節點,則父節點會觸發一個Child Watches
。
三、watcher特性
watch
對節點的監聽事件是一次性的!客戶端在指定的節點設置了監聽watch
,一旦該節點數據發生變動通知一次客戶端後,客戶端對該節點的監聽事件就失效了。
若是還要繼續監聽這個節點,就須要咱們在客戶端的監聽回調中,再次對節點的監聽watch
事件設置爲True
。不然客戶端只能接收到一次該節點的變動通知。
4、zookeeper能實現哪些功能
服務的註冊與發現功能只是zookeeper的冰山一角,它還能實現諸如分佈式鎖、隊列、配置中心等一系列功能,接下來咱們只分析一下原理,具體的實現你們上網查一下資料仍是比較全的。
一、分佈式鎖
zookeeper
基於watcher
機制和znode
的有序節點,天生就是一個分佈式鎖的坯子。首先建立一個/test/lock
父節點做爲一把鎖,儘可能是持久節點(PERSISTENT類型),每一個嘗試獲取這把鎖的客戶端,在/test/lock
父節點下建立臨時順序子節點。
因爲序號的遞增性,咱們規定序號最小的節點即得到鎖。例如:客戶端來獲取鎖,在/test/lock
節點下建立節點爲/test/lock/seq-00000001
,它是最小的因此它優先拿到了鎖,其它節點等待通知再次獲取鎖。/test/lock/seq-00000001
執行完本身的邏輯後刪除節點釋放鎖。
那麼節點/test/lock/seq-00000002
想要獲取鎖等誰的通知呢?
這裏咱們讓/test/lock/seq-00000002
節點監聽/test/lock/seq-00000001
節點,一旦/test/lock/seq-00000001
節點刪除,則通知/test/lock/seq-00000002
節點,讓它再次判斷本身是否是最小的節點,是則拿到鎖,不是繼續等通知。
以此類推/test/lock/seq-00000003
節點監聽/test/lock/seq-00000002
節點,老是讓後一個節點監聽前一個節點,不用讓全部節點都監聽最小的節點,避免設置沒必要要的監聽,以避免形成大量無效的通知,造成「羊羣效應」。
zookeeper
分佈式鎖和redis
分佈式鎖相比,由於大量的建立、刪除節點性能上比較差,並非很推薦。
二、分佈式隊列
zookeeper實現分佈式隊列也很簡單,應用znode的有序節點自然的「先進先出」,後建立的節點老是最大的,出隊老是拿序號最小的節點便可。
三、配置管理
如今有不少開源項目都在使用Zookeeper來維護配置,像消息隊列Kafka中,就使用Zookeeper來維護broker的信息;dubbo中管理服務的配置信息。原理也是基於watcher
機制,例如:建立一個/config
節點存放一些配置,客戶端監聽這個節點,一點修改/config
節點的配置信息,通知各個客戶端數據變動從新拉取配置信息。
四、命名服務
zookeeper
的命名服務:也就是咱們常說的服務註冊與發現,主要是根據指定名字來獲取資源或服務的地址,服務提供者等信息,利用其znode
節點的特色和watcher
機制,將其做爲動態註冊和獲取服務信息的配置中心,統一管理服務名稱和其對應的服務器列表信息,咱們可以近乎實時地感知到後端服務器的狀態(上線、下線、宕機)。
總結
本文旨在給你們介紹一下zookeeper的基礎知識,像面試中被問頻率較高的zookeeper集羣選主等概念,並無放在這期來寫,由於集羣的內容也是比較多的,我怕篇幅太長你們沒有耐心看完(其實就是有點犯懶了,哈哈哈!)感興趣的小夥伴能夠關注一波,zookeeper集羣咱們下期見嘍。
整理了幾百本各種技術電子書,送給小夥伴們。關注公號回覆【666】自行領取。和一些小夥伴們建了一個技術交流羣,一塊兒探討技術、分享技術資料,旨在共同窗習進步,若是感興趣就加入咱們吧!
不管你是剛入行、仍是已經有幾年經驗的程序員,相信這份面試提綱都會給你很多助力,長按二維碼關注 『 程序員內點事 』 ,回覆 『 offer 』 自行領取,祝你們 offer 拿到手軟