不會這些概念,勸你簡歷不要寫 “熟悉” zookeeper

點擊“ 程序員內點事 ”關注,選擇“ 設置星標 ”

堅持學習,好文每日送達!

嘮嘮叨叨

本文主要分享一下zookeeper的一些基本概念,在正式進入正題前,和大家聊一聊剛入行時我的面試經驗,可以說是耿直的有些可愛。

面試官:用過zookeeper 嗎?

:用過啊,給dubbo提供服務的註冊與發現嘛

面試官:知道 zookeeper 是什麼嗎?

:知道啊,註冊中心嘛

面試官:那你們項目中都是怎麼用 zookeeper 的?

:就在 springboot 的 application.properties 配置文件裏添加一個 zookeeper 服務地址就行了。。。


上邊的對話好像也沒什麼毛病,但似乎又感覺哪裏有點不太對,結果就是每次我如此回答面試都被pass。

爲什麼會被問zookeeper?因爲我的簡歷項目上寫着熟練使用zookeeper,可面試官理解的 “熟練” 使用可不是會配置,工程啓動不報錯那麼簡單。所以還是有必要全面瞭解一下zookeeper的相關知識。


一、zookeeper初識?

Zookeeper 它作爲Hadoop項目中的一個開源子項目,是一個經典的分佈式數據一致性解決方案,致力於爲分佈式應用提供一個高性能、高可用,且具有嚴格順序訪問控制能力的分佈式協調服務。

1、zookeeper數據模型

zookeeper 維護了一個類似文件系統的數據結構,每個子目錄(/微信、/微信/公衆號)都被稱作爲 znode 即節點。和文件系統一樣,我們可以很輕鬆的對 znode 節點進行增加、刪除等操作,而且還可以在一個znode下增加、刪除子znode,區別在於文件系統的是,znode可以存儲數據(嚴格說是必須存放數據,默認是個空字符)。

由於zookeeper是目錄節點結構,在獲取和創建節點時,必須要以“/” 開頭,否則在獲取節點時會報錯 Path must start with / character

1[zk: localhost:2181(CONNECTED) 13] get test
2Command failed: java.lang.IllegalArgumentException: Path must start with / character

根節點名必須爲“/XXX”,創建子節點時必須要帶上根節點目錄“/XXX/CCC”“/XXX/AAA”

例如:想要獲取下圖 程序員內點事 節點必須拼接完整的路徑 get /微信/公衆號/程序員內點事

1get /微信/公衆號/程序員內點事
znode被用來存儲 byte級 或 kb級 的數據,可存儲的最大數據量是1MB請注意:一個節點的數據量不僅包含它自身存儲數據,它的所有子節點的名字也要折算成Byte數計入,因此znode的子節點數也不是無限的)雖然可以手動的修改節點存儲量大小,但一般情況下並不推薦這樣做。

2、znode節點屬性

一個znode節點不僅可以存儲數據,還有一些其他特別的屬性。接下來我們創建一個/test節點分析一下它各個屬性的含義。

 1[zk: localhost:2181(CONNECTED) 6] get /test
 2456
 3cZxid = 0x59ac //
 4ctime = Mon Mar 30 15:20:08 CST 2020
 5mZxid = 0x59ad
 6mtime = Mon Mar 30 15:22:25 CST 2020
 7pZxid = 0x59ac
 8cversion = 0
 9dataVersion = 2
10aclVersion = 0
11ephemeralOwner = 0x0
12dataLength = 3
13numChildren = 0  

我們看到一個znode節點的屬性比較多,但比較主要的屬性還是zxidversionacl 這三個。


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,版本號遞增了。

 1[zk: localhost:2181(CONNECTED) 10] set /test 8888
 2cZxid = 0x59ac
 3ctime = Mon Mar 30 15:20:08 CST 2020
 4mZxid = 0x59b6
 5mtime = Mon Mar 30 16:58:08 CST 2020
 6pZxid = 0x59ac
 7cversion = 0
 8dataVersion = 3
 9aclVersion = 0
10ephemeralOwner = 0x0
11dataLength = 4
12numChildren = 0
3、znode的類型

zookeeper 有四種類型的znode,在用客戶端 client 創建節點的時候需要指定類型。

1zookeeper.create("/公衆號/程序員內點事", "".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
  • PERSISTENT-持久化目錄節點 :client創建節點後,與zookeeper斷開連接該節點將被持久化,當client再次連接後節點依舊存在。

  • PERSISTENT_SEQUENTIAL-持久化順序節點 :client創建節點後,與zookeeper斷開連接該節點將被持久化,再次連接節點還存在,zookeeper會給該節點名稱進行順序編號,例如:/lock/0000000001、/lock/0000000002、/lock/0000000003。

  • EPHEMERAL-臨時目錄節點 :client與zookeeper斷開連接後,該節點即會被刪除

  • EPHEMERAL_SEQUENTIAL-臨時順序節點 :client與zookeeper斷開連接後,該節點被刪除,會給該節點名稱進行順序編號,例如:/lock/0000000001、/lock/0000000002、/lock/0000000003。

二、節點的ACL權限控制

ACL:即 Access Control List (節點的權限控制),通過ACL機制來解決znode節點的訪問權限問題,要注意的是zookeeper對權限的控制是基於znode級別的,也就說節點之間的權限不具有繼承性,即子節點不繼承父節點的權限。

zookeeper中設置ACL權限的格式由<schema>:<id>:<acl>三段組成。

schema :表示授權的方式

  • world:表示任何人都可以訪問

  • auth:只有認證的用戶可以訪問

  • digest:使用username :password用戶密碼生成MD5哈希值作爲認證ID

  • host/ip:使用客戶端主機IP地址來進行認證

id:權限的作用域,用來標識身份,依賴於schema選擇哪種方式。

acl:給一個節點賦予哪些權限,節點的權限有create,、delete、write、read、admin 統稱 cdwra

1、world:表示任何人都可以訪問

我們用 getAcl 命令來看一下,沒有設置過權限的znode節點,默認情況下的權限情況。

1[zk: localhost:2181(CONNECTED) 12] getAcl /test
2'world,'anyone
3: cdrwa

看到沒有設置ACL屬性的節點,默認schema 使用的是world,作用域是anyone,節點權限是cdwra,也就是說任何人都可以訪問。

那我們如果要給一個schema 爲非world的節點設置world權限咋搞?

1setAcl /test world:anyone:crdwa
2、auth:只有認證的用戶可以訪問

schema 用auth授權表示只有認證後的用戶纔可以訪問,那麼首先就需要添加認證用戶,添加完以後需要對認證的用戶設置ACL權限。

1addauth digest test:password(明文)

需要注意的是設置認證用戶時的密碼是明文的。

1[zk: localhost:2181(CONNECTED) 2] addauth digest user:user //用戶名:密碼
2[zk: localhost:2181(CONNECTED) 5] setAcl /test auth:user:crdwa
3[zk: localhost:2181(CONNECTED) 6] getAcl /test
4'digest,'user:ben+k/3JomjGj4mfd4fYsfM6p0A=
5: cdrwa

實際上我們這樣設置以後,就是將這個節點開放給所有認證的用戶,setAcl /test auth:user:crdwa 相當於setAcl /test auth::crdwa

3、digest:用戶名:密碼的驗證方式

用戶名:密碼方式授權是針對單個特定用戶,這種方式是不需要先添加認證用戶的。

如果在代碼中使用zookeeper客戶端設置ACL,那麼密碼是明文的,但若是zk.cli等客戶端操作就需要將密碼進行sha1base64處理。

1setAcl <path> digest:<user>:<password(密文)>:<acl>
2
3setAcl /test digest:user:jalRr+knv/6L2uXdenC93dEDNuE=:crdwa

那麼密碼如何加密嘞?有以下幾種方式:

通過shell命令加密

1echo -n <user>:<password> | openssl dgst -binary -sha1 | openssl base64
2

使用zookeeper自帶的類庫org.apache.zookeeper.server.auth.DigestAuthenticationProvider生成

1java -cp /zookeeper-3.4.13/zookeeper-3.4.13.jar:/zookeeper-3.4.13/lib/slf4j-api-1.7.25.jar \
2  org.apache.zookeeper.server.auth.DigestAuthenticationProvider \
3  root:root
4root:root->root:qiTlqPLK7XM2ht3HMn02qRpkKIE=
4、host/ip:使用客戶端主機IP地址來進行認證

這種方式就比較好理解了,通過對特定的IP地址,也可以是一個IP段進行授權。

 1[zk: localhost:2181(CONNECTED) 3] setAcl /test0000000014 ip:127.0.0.1:crdwa
 2cZxid = 0x59ac
 3ctime = Mon Mar 30 15:20:08 CST 2020
 4mZxid = 0x59b6
 5mtime = Mon Mar 30 16:58:08 CST 2020
 6pZxid = 0x59ac
 7cversion = 0
 8dataVersion = 3
 9aclVersion = 3 // 這個版本一直在增加
10ephemeralOwner = 0x0
11dataLength = 4
12numChildren = 0

三、zookeeper的靈魂 watcher

我們在開頭就說過:zookeeper可以爲dubbo提供服務的註冊與發現,作爲註冊中心,但你有想過zookeeper爲啥能夠實現服務的註冊與發現嗎?這就不得不說一下zookeeper的靈魂 Watcher(監聽者)。

1、watcher是個啥?

watcher 是zooKeeper中一個非常核心功能 ,客戶端watcher 可以監控節點的數據變化以及它子節點的變化,一旦這些狀態發生變化,zooKeeper服務端就會通知所有在這個節點上設置過watcher的客戶端 ,從而每個客戶端都很快感知,它所監聽的節點狀態發生變化,而做出對應的邏輯處理。

簡單的介紹了一下watcher ,那麼我們來分析一下,zookeeper是如何實現服務的註冊與發現。
zookeeper的服務註冊與發現,主要應用的是zookeeperznode節點數據模型和watcher機制,大致的流程如下:


  • 服務註冊: 服務提供者(Provider)啓動時,會向zookeeper服務端註冊服務信息,也就是創建一個節點,例如:用戶註冊服務com.xxx.user.register,並在節點上存儲服務的相關數據(如服務提供者的ip地址、端口等)。

  • 服務發現: 服務消費者(Consumer)啓動時,根據自身配置的依賴服務信息,向zookeeper服務端獲取註冊的服務信息並設置watch監聽,獲取到註冊的服務信息之後,將服務提供者的信息緩存在本地,並進行服務的調用。

  • 服務通知: 一旦服務提供者因某種原因宕機不再提供服務之後,客戶端與zookeeper服務端斷開連接,zookeeper服務端上服務提供者對應服務節點會被刪除(例如:用戶註冊服務com.xxx.user.register),隨後zookeeper服務端會異步向所有消費用戶註冊服務com.xxx.user.register,且設置了watch監聽的服務消費者發出節點被刪除的通知,消費者根據收到的通知拉取最新服務列表,更新本地緩存的服務列表。

上邊的過程就是zookeeper可以實現服務註冊與發現的大致原理。

2、watcher類型

znode節點可以設置兩類watch,一種是DataWatches,基於znode節點的數據變更從而觸發 watch 事件,觸發條件getData()exists()setData()、 create()

另一種是Child Watches,基於znode的孩子節點發生變更觸發的watch事件,觸發條件 getChildren()、 create()

而在調用 delete() 方法刪除znode時,則會同時觸發Data WatchesChild Watches,如果被刪除的節點還有父節點,則父節點會觸發一個Child Watches

3、watcher特性

watch對節點的監聽事件是一次性的!客戶端在指定的節點設置了監聽watch,一旦該節點數據發生變更通知一次客戶端後,客戶端對該節點的監聽事件就失效了。

如果還要繼續監聽這個節點,就需要我們在客戶端的監聽回調中,再次對節點的監聽watch事件設置爲True。否則客戶端只能接收到一次該節點的變更通知。

四、zookeeper能實現哪些功能

服務的註冊與發現功能只是zookeeper的冰山一角,它還能實現諸如分佈式鎖、隊列、配置中心等一系列功能,接下來我們只分析一下原理,具體的實現大家上網查一下資料還是比較全的。

1、分佈式鎖

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分佈式鎖相比,因爲大量的創建、刪除節點性能上比較差,並不是很推薦。

在這裏插入圖片描述
2、分佈式隊列

zookeeper實現分佈式隊列也很簡單,應用znode的有序節點天然的“先進先出”,後創建的節點總是最大的,出隊總是拿序號最小的節點即可。

3、配置管理

現在有很多開源項目都在使用Zookeeper來維護配置,像消息隊列Kafka中,就使用Zookeeper來維護broker的信息;dubbo中管理服務的配置信息。原理也是基於watcher機制,例如:創建一個/config節點存放一些配置,客戶端監聽這個節點,一點修改/config節點的配置信息,通知各個客戶端數據變更重新拉取配置信息。

4、命名服務

zookeeper的命名服務:也就是我們常說的服務註冊與發現,主要是根據指定名字來獲取資源或服務的地址,服務提供者等信息,利用其znode節點的特點和watcher機制,將其作爲動態註冊和獲取服務信息的配置中心,統一管理服務名稱和其對應的服務器列表信息,我們能夠近乎實時地感知到後端服務器的狀態(上線、下線、宕機)。

總結

本文旨在給大家介紹一下zookeeper的基礎知識,像面試中被問頻率較高的zookeeper集羣選主等概念,並沒有放在這期來寫,因爲集羣的內容也是比較多的,我怕篇幅太長大家沒有耐心看完(其實就是有點犯懶了,哈哈哈!)

感興趣的小夥伴可以關注一波,zookeeper集羣我們下期見嘍。


今天就說這麼多,如果本文對您有一點幫助,希望能得到您一個點贊????哦

您的認可纔是我寫作的動力!

小福利:

獲取到一些極客付費課程 ,噓~,免費 送給小夥伴們。關注公衆號回覆【極客】自行領取

往期精彩回顧

爲了不復制粘貼,我被逼着學會了JAVA爬蟲

一口氣說出 9種 分佈式ID生成方式,面試官有點懵了
面試總被問分庫分表怎麼辦?這些知識點你要懂

基於 Java 實現的人臉識別功能(附源碼)

面試被問分佈式ID怎麼辦?滴滴(Tinyid)甩給他

9種分佈式ID生成之美團(Leaf)實戰

技術/面試/吐槽

程序員內點事這都有

長按掃碼可關注

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章