1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        一文徹底搞懂 zookeeper 核心知識點(修訂版)

        共 7618字,需瀏覽 16分鐘

         ·

        2020-12-25 11:27


        點擊“?程序員內(nèi)點事?”關注,選擇“?設置星標?”

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




        初識 zookeeper

        Zookeeper 它作為Hadoop項目中的一個開源子項目,是一個經(jīng)典的分布式數(shù)據(jù)一致性解決方案,致力于為分布式應用提供一個高性能、高可用,且具有嚴格順序訪問控制能力的分布式協(xié)調(diào)服務。

        1、zookeeper數(shù)據(jù)模型

        zookeeper 維護了一個類似文件系統(tǒng)的數(shù)據(jù)結(jié)構(gòu),每個子目錄(/微信、/微信/公眾號)都被稱作為 znode 即節(jié)點。和文件系統(tǒng)一樣,我們可以很輕松的對 znode 節(jié)點進行增加、刪除等操作,而且還可以在一個znode下增加、刪除子znode,區(qū)別在于文件系統(tǒng)的是,znode可以存儲數(shù)據(jù)(嚴格說是必須存放數(shù)據(jù),默認是個空字符)。

        由于zookeeper是目錄節(jié)點結(jié)構(gòu),在獲取和創(chuàng)建節(jié)點時,必須要以“/” 開頭,否則在獲取節(jié)點時會報錯 Path must start with / character。

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

        根節(jié)點名必須為“/XXX”,創(chuàng)建子節(jié)點時必須要帶上根節(jié)點目錄“/XXX/CCC”、“/XXX/AAA”。

        例如:想要獲取下圖 程序員內(nèi)點事 節(jié)點必須拼接完整的路徑 get /微信/公眾號/程序員內(nèi)點事

        get?/微信/公眾號/程序員內(nèi)點事
        在這里插入圖片描述

        znode被用來存儲 byte級kb級 的數(shù)據(jù),可存儲的最大數(shù)據(jù)量是1MB請注意:一個節(jié)點的數(shù)據(jù)量不僅包含它自身存儲數(shù)據(jù),它的所有子節(jié)點的名字也要折算成Byte數(shù)計入,因此znode的子節(jié)點數(shù)也不是無限的)雖然可以手動的修改節(jié)點存儲量大小,但一般情況下并不推薦這樣做。


        2、znode節(jié)點屬性

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

        [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??
        節(jié)點屬性注解
        cZxid該數(shù)據(jù)節(jié)點被創(chuàng)建時的事務Id
        mZxid該數(shù)據(jù)節(jié)點被修改時最新的事物Id
        pZxid當前節(jié)點的父級節(jié)點事務Id
        ctime該數(shù)據(jù)節(jié)點創(chuàng)建時間
        mtime該數(shù)據(jù)節(jié)點最后修改時間
        dataVersion當前節(jié)點版本號(每修改一次值+1遞增)
        cversion子節(jié)點版本號(子節(jié)點修改次數(shù),每修改一次值+1遞增)
        aclVersion當前節(jié)點acl版本號(節(jié)點被修改acl權(quán)限,每修改一次值+1遞增)
        ephemeralOwner臨時節(jié)點標示,當前節(jié)點如果是臨時節(jié)點,則存儲的創(chuàng)建者的會話id(sessionId),如果不是,那么值=0
        dataLength當前節(jié)點所存儲的數(shù)據(jù)長度
        numChildren當前節(jié)點下子節(jié)點的個數(shù)

        我們看到一個znode節(jié)點的屬性比較多,但比較主要的屬性還是zxidversion、acl 這三個。


        Zxid:

        znode節(jié)點狀態(tài)改變會導致該節(jié)點收到一個zxid格式的時間戳,這個時間戳是全局有序的,znode節(jié)點的建立或者更新都會產(chǎn)生一個新的。如果zxid1的值 < zxid2的值,那么說明zxid2發(fā)生的改變在zxid1之后。每個znode節(jié)點都有3個zxid屬性,cZxid(節(jié)點創(chuàng)建時間)、mZxid(該節(jié)點修改時間,與子節(jié)點無關)、pZxid(該節(jié)點或者該節(jié)點的子節(jié)點的最后一次創(chuàng)建或者修改時間,孫子節(jié)點無關)。

        zxid屬性主要應用于zookeeper的集群,這個后邊介紹集群時詳細說。

        Version:

        znode屬性中一共有三個版本號dataversion(數(shù)據(jù)版本號)、cversion(子節(jié)點版本號)、aclversion(節(jié)點所擁有的ACL權(quán)限版本號)。

        znode中的數(shù)據(jù)可以有多個版本,如果某一個節(jié)點下存有多個數(shù)據(jù)版本,那么查詢這個節(jié)點數(shù)據(jù)就需要帶上版本號。每當我們對znode節(jié)點數(shù)據(jù)修改后,該節(jié)點的dataversion版本號會遞增。當客戶端請求該znode節(jié)點時,會同時返回節(jié)點數(shù)據(jù)和版本號。另外當dataversion-1的時候可以忽略版本進行操作。對一個節(jié)點設置權(quán)限時aclVersion版本號會遞增,下邊會詳細說ACL權(quán)限控制。

        驗證一下,我們修改/test節(jié)點的數(shù)據(jù)看看dataVersion有什么變化,發(fā)現(xiàn)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


        3、znode的類型

        zookeeper 有四種類型的znode,在用客戶端 client 創(chuàng)建節(jié)點的時候需要指定類型。

        zookeeper.create("/公眾號/程序員內(nèi)點事",?"".getBytes(),?Ids.OPEN_ACL_UNSAFE,?CreateMode.EPHEMERAL_SEQUENTIAL);
        • PERSISTENT-持久化目錄節(jié)點 :client創(chuàng)建節(jié)點后,與zookeeper斷開連接該節(jié)點將被持久化,當client再次連接后節(jié)點依舊存在。

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

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

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



        節(jié)點的ACL權(quán)限控制

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

        zookeeper中設置ACL權(quán)限的格式由::三段組成。

        schema :表示授權(quán)的方式

        • world:表示任何人都可以訪問
        • auth:只有認證的用戶可以訪問
        • digest:使用username ?:password用戶密碼生成MD5哈希值作為認證ID
        • host/ip:使用客戶端主機IP地址來進行認證

        id:權(quán)限的作用域,用來標識身份,依賴于schema選擇哪種方式。

        acl:給一個節(jié)點賦予哪些權(quán)限,節(jié)點的權(quán)限有create,、delete、write、read、admin 統(tǒng)稱 cdwra。

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

        我們用 getAcl 命令來看一下,沒有設置過權(quán)限的znode節(jié)點,默認情況下的權(quán)限情況。

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

        看到?jīng)]有設置ACL屬性的節(jié)點,默認schema 使用的是world,作用域是anyone,節(jié)點權(quán)限是cdwra,也就是說任何人都可以訪問。

        那我們?nèi)绻o一個schema 為非world的節(jié)點設置world權(quán)限咋搞?

        setAcl?/test?world:anyone:crdwa

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

        schema 用auth授權(quán)表示只有認證后的用戶才可以訪問,那么首先就需要添加認證用戶,添加完以后需要對認證的用戶設置ACL權(quán)限。

        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

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

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

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

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

        setAcl??digest:::

        setAcl?/test?digest:user:jalRr+knv/6L2uXdenC93dEDNuE=:crdwa

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

        通過shell命令加密

        echo?-n?:<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=

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

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

        [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




        zookeeper 靈魂 watcher

        我們在開頭就說過:zookeeper可以為dubbo提供服務的注冊與發(fā)現(xiàn),作為注冊中心,但你有想過zookeeper為啥能夠?qū)崿F(xiàn)服務的注冊與發(fā)現(xiàn)嗎?這就不得不說一下zookeeper的靈魂 Watcher(監(jiān)聽者)。

        1、watcher是個啥?

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

        簡單的介紹了一下watcher ,那么我們來分析一下,zookeeper是如何實現(xiàn)服務的注冊與發(fā)現(xiàn)。zookeeper的服務注冊與發(fā)現(xiàn),主要應用的是zookeeperznode節(jié)點數(shù)據(jù)模型和watcher機制,大致的流程如下:

        在這里插入圖片描述
        • 服務注冊: 服務提供者(Provider)啟動時,會向zookeeper服務端注冊服務信息,也就是創(chuàng)建一個節(jié)點,例如:用戶注冊服務com.xxx.user.register,并在節(jié)點上存儲服務的相關數(shù)據(jù)(如服務提供者的ip地址、端口等)。

        • 服務發(fā)現(xiàn): 服務消費者(Consumer)啟動時,根據(jù)自身配置的依賴服務信息,向zookeeper服務端獲取注冊的服務信息并設置watch監(jiān)聽,獲取到注冊的服務信息之后,將服務提供者的信息緩存在本地,并進行服務的調(diào)用。

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

        上邊的過程就是zookeeper可以實現(xiàn)服務注冊與發(fā)現(xiàn)的大致原理。

        2、watcher類型

        znode節(jié)點可以設置兩類watch,一種是DataWatches,基于znode節(jié)點的數(shù)據(jù)變更從而觸發(fā) watch 事件,觸發(fā)條件getData()、exists()setData()、 create()。

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

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

        3、watcher特性

        watch對節(jié)點的監(jiān)聽事件是一次性的!客戶端在指定的節(jié)點設置了監(jiān)聽watch,一旦該節(jié)點數(shù)據(jù)發(fā)生變更通知一次客戶端后,客戶端對該節(jié)點的監(jiān)聽事件就失效了。

        如果還要繼續(xù)監(jiān)聽這個節(jié)點,就需要我們在客戶端的監(jiān)聽回調(diào)中,再次對節(jié)點的監(jiān)聽watch事件設置為True。否則客戶端只能接收到一次該節(jié)點的變更通知。



        zookeeper 實現(xiàn)的功能

        服務的注冊與發(fā)現(xiàn)功能只是zookeeper的冰山一角,它還能實現(xiàn)諸如分布式鎖、隊列、配置中心等一系列功能,接下來我們只分析一下原理,具體的實現(xiàn)大家上網(wǎng)查一下資料還是比較全的。

        1、分布式鎖

        zookeeper基于watcher機制和znode的有序節(jié)點,天生就是一個分布式鎖的坯子。首先創(chuàng)建一個/test/lock父節(jié)點作為一把鎖,盡量是持久節(jié)點(PERSISTENT類型),每個嘗試獲取這把鎖的客戶端,在/test/lock父節(jié)點下創(chuàng)建臨時順序子節(jié)點。

        由于序號的遞增性,我們規(guī)定序號最小的節(jié)點即獲得鎖。例如:客戶端來獲取鎖,在/test/lock節(jié)點下創(chuàng)建節(jié)點為/test/lock/seq-00000001,它是最小的所以它優(yōu)先拿到了鎖,其它節(jié)點等待通知再次獲取鎖。/test/lock/seq-00000001執(zhí)行完自己的邏輯后刪除節(jié)點釋放鎖。

        那么節(jié)點/test/lock/seq-00000002想要獲取鎖等誰的通知呢?

        這里我們讓/test/lock/seq-00000002節(jié)點監(jiān)聽/test/lock/seq-00000001節(jié)點,一旦/test/lock/seq-00000001節(jié)點刪除,則通知/test/lock/seq-00000002節(jié)點,讓它再次判斷自己是不是最小的節(jié)點,是則拿到鎖,不是繼續(xù)等通知。

        以此類推/test/lock/seq-00000003節(jié)點監(jiān)聽/test/lock/seq-00000002節(jié)點,總是讓后一個節(jié)點監(jiān)聽前一個節(jié)點,不用讓所有節(jié)點都監(jiān)聽最小的節(jié)點,避免設置不必要的監(jiān)聽,以免造成大量無效的通知,形成“羊群效應”。

        zookeeper分布式鎖和redis分布式鎖相比,因為大量的創(chuàng)建、刪除節(jié)點性能上比較差,并不是很推薦。

        2、分布式隊列

        zookeeper實現(xiàn)分布式隊列也很簡單,應用znode的有序節(jié)點天然的“先進先出”,后創(chuàng)建的節(jié)點總是最大的,出隊總是拿序號最小的節(jié)點即可。

        3、配置管理

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

        4、命名服務

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


        一文搞懂什么是事務


        是時候捋一捋Java的深淺拷貝了


        Synchronized 使用場景實例


        GitHub 開源項目文章爬蟲,懶人必備!


        瀏覽 43
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            中文字幕无码久久精品青草 | 成人黄色小说免费观看 | 一级插骚逼 | 无码免费观看 | 亚洲婷婷无码 | 国产喷水吹潮在线播放91 | 成人自拍视频在线观看 | 日日干天天干视频 | 四虎影库久免费视频 | 美女撒尿私密视频 |