一文入门 Zookeeper
文章目錄
- 1. zookeeper 簡介
- 1.1 什么是zookeeper
- 1.2 zookeeper 發(fā)展歷史
- 1.3 zookeeper 典型應(yīng)用場景
- 1.4 zookeeper 提供的服務(wù)
- 1.5 zookeeper的數(shù)據(jù)模型
- 1.6 znode的分類
- 2. Zookeeper架構(gòu)
- 2.1 整體架構(gòu)
- 2.2 session
- 2.3 Quorum 模式
- 2.4 數(shù)據(jù)一致性
- 3. zookeeper的案例演示
- 3.1 zookeeper 安裝
- 3.2 zookeeper啟動
- 3.3 zookeeper Client 交互式命令
- 3.4 zookeeper 實現(xiàn)簡單的分布式鎖
- 3.5 zookeeper實現(xiàn)master-worker架構(gòu)
- 3.6 zookeeper quorum集群模式
- 4. 總結(jié)
通過本篇文章,你能夠了解zookeeper的基本知識和應(yīng)用場景,并且能夠通過命令行完整體會到zookeeper的功能和基本應(yīng)用場景。當然,因為作者水平有限,對分布式系統(tǒng)的理解多少有一些不足,希望大家不吝賜教。
本文通過如下幾個方面介紹zookeeper基礎(chǔ):
- zookeeper 的 發(fā)展歷史 ,提供什么服務(wù) 以及 基本功能和應(yīng)用場景
- zookeeper 的整體架構(gòu)
- zookeeper 實現(xiàn)簡單分布式鎖 和 master-work架構(gòu),并演示zookeeper的集群模式
1. zookeeper 簡介
希望通過zookeeper的發(fā)展歷史,基本功能介紹以及應(yīng)用場景來讓大家對zookeeper有一個宏觀的認識。
1.1 什么是zookeeper
一句話:zookeeper 是一個開源的分布式協(xié)同服務(wù)系統(tǒng)。說到系統(tǒng)而不是像raft那樣的庫,我們就能夠理解 zookeeper擁有自己的client和server服務(wù)進程。
zookeeper的設(shè)計目標: 是將那一些復(fù)雜且容易出錯的分布式系統(tǒng)服務(wù)封裝起來,抽象出一個高效易用的原語集,并向用戶提供簡單的接口。
而比較有趣的是zookeeper 這個名字,認為zookeeper的工作性質(zhì)就像是一個動物園管理員,用來協(xié)調(diào)整個動物園內(nèi)的工作,讓每一個動物都井井有條得處在自己的籠子里,游客(IO請求)能夠安全得在動物園里游覽并達到自己想去的位置而不受其他動物的影響。
1.2 zookeeper 發(fā)展歷史
Zookeeper最早起源于雅虎研究院的一個研究小組。當時研究人員發(fā)現(xiàn),在雅虎內(nèi)部很多大型系統(tǒng)都依賴一個類似的系統(tǒng)來進行分布式協(xié)同,但是這一些系統(tǒng)都存在分布式單點問題
所以雅虎人員開發(fā)了一個通用的無單點問題的分布式協(xié)調(diào)框架,這就是ZK。在 zookeeper 被大量使用之后出現(xiàn)了如下著名的開源項目:
- Hadoop: 使用zookeeper 做namespace的高可用
- HBase: 保證集群只有一個Master;保存hbase:meta 表的位置,保存集群中的RegionServer列表
- Kafka: 集群成員管理,controller 節(jié)點選舉
可見zookeeper還是能夠提供足夠的應(yīng)用場景,且能夠得到較多的開源項目的認可。
接下來可以看看zookeeper的典型場景:
1.3 zookeeper 典型應(yīng)用場景
- 配置管理(configuration management)
- DNS 服務(wù)
- 組成成員管理(group membership) – 經(jīng)典的分布式數(shù)據(jù)庫HBASE
- 各種分布式鎖(上一家公司參與的超融合存儲項目 中即使用zookeeper來做一些集群鎖的作用)
關(guān)于組成成員管理 和 分布式鎖 后續(xù)的介紹中會為大家演示zookeeper原生接口如何實現(xiàn)簡單的分布式鎖的功能。當然,更深層次實現(xiàn)介紹還需要再摸索一番。
ps: zookeeper 適用于存儲和協(xié)同相關(guān)的關(guān)鍵數(shù)據(jù),不適合用于大量數(shù)據(jù) 存儲。
1.4 zookeeper 提供的服務(wù)
這里簡單說一下 zookeeper 提供什么樣子的服務(wù)給到客戶端。
- Application 直接使用Zookeeper客戶端庫即可,目前主要是Java接口
- Zookeeper的客戶端庫負責和Zookeeper集群中的server進行交互
1.5 zookeeper的數(shù)據(jù)模型
這里描述的并不是zookeeper的架構(gòu),僅僅是其數(shù)據(jù)存儲相關(guān)的介紹。
我們知道在整個分布式系統(tǒng)的領(lǐng)域里,主流的兩種數(shù)據(jù)存儲方式是:樹型 和 key-value型。
zookeeper這里選擇樹型模型(**data-tree)**的考慮是:
- 便于表達數(shù)據(jù)之間的層次關(guān)系
- 便于為不同的應(yīng)用分配獨立的命名空間
如上圖中 每一個節(jié)點叫做znode,都可以用來保存數(shù)據(jù)。
同時每一個節(jié)點都有一個版本,這個版本是從0開始計數(shù)。
關(guān)于Zookeeper 提供的data-tree 數(shù)據(jù)模型有以下幾點需要注意:
- 使用Unix 風(fēng)格路徑定位znode,例如 /A/A_1 表示A的子節(jié)點A_1
- 支持全量寫入和讀取,沒有像普通文件系統(tǒng)那樣支持部分寫入和讀取
- Data tree的所有API 都是wait-free的;正在調(diào)用的API不影響其他API的完成 即為wait-free
- Data tree 直接提供鎖這樣的分布式系統(tǒng)機制,但可以通過API來實現(xiàn)分布式協(xié)同機制
1.6 znode的分類
- 持久性Znode(Persistent):創(chuàng)建之后即使發(fā)生節(jié)點異常或集群宕機,znode 的信息也不會丟失
- 臨時性Znode(Ephemeral): 節(jié)點宕機 或 在指定時間timeout 沒有給zookeeper集群發(fā)消息, 這樣的znode就會消失
- 持久順序性Znode(Persistent_Sequential) znode 關(guān)聯(lián)一個唯一的單調(diào)遞增整數(shù),這個整數(shù)是znode名字后綴,具備持久性。
- 臨時順序性Znode(Ephemeral_Sequential) 擁有順序性的臨時Znode 節(jié)點。
2. Zookeeper架構(gòu)
以下的zookeeper架構(gòu)僅僅是簡要的介紹,并沒有深入到各個機制的細節(jié),后續(xù)文章會嘗試進行探討。
2.1 整體架構(gòu)
應(yīng)用使用zookeeper客戶端(shell) 或者 zookeeper客戶端庫(java api) 使用zookeeper的基本服務(wù)。
zookeeper 客戶端用來zookeeper集群進行交互。
實際的zookeeper集群可以有兩種模式:standalone 模式 和 quorum 模式
- standalone模式 即集群中只有一個節(jié)點作為server,一般的分布式系統(tǒng)不需要這樣的模式,存在單點問題。適用于開發(fā)場景。
- quorum 模式 即 集群有3個及以上的節(jié)點,通過quorum協(xié)議 為集群維護一個法定人數(shù),只要集群存活的節(jié)點大于2/n+1(n表示節(jié)點數(shù))個,則集群能夠一直對外提供服務(wù)。
2.2 session
zookeeper 客戶端和zookeeper集群建立鏈接時,會和集群中的某一個節(jié)點創(chuàng)建session。
關(guān)閉session有兩種情況(客戶端和集群斷開鏈接):
- 客戶端可以主動關(guān)閉session
- zookeeper集群節(jié)點沒有在指定的timeout時間內(nèi)收到客戶端的消息的話,zookeeper節(jié)點也會關(guān)閉session。
如果zookeeper客戶端庫發(fā)現(xiàn)鏈接的zookeeper集群出錯,會自動的和其他的zookeeper節(jié)點建立鏈接。如下圖:
2.3 Quorum 模式
處于Quorum模式的集群包含多個節(jié)點,下圖中的三個節(jié)點組成了一個zookeeper集群,其中綠色節(jié)點1的是leader節(jié)點,節(jié)點2和節(jié)點3是follower節(jié)點。leader節(jié)點可以處理讀寫請求,follower節(jié)點只能處理讀請求,當寫請求到達follower節(jié)點時會將寫請求轉(zhuǎn)發(fā)到leader節(jié)點。
寫請求寫入到leader之后,再由leader負責將數(shù)據(jù)同步到其他的follower節(jié)點。
2.4 數(shù)據(jù)一致性
我們知道分布式系統(tǒng)中的數(shù)據(jù)一致性主要是由于寫 的順序問題帶來的一系列讀/寫相關(guān)的一致性問題。
zookeeper的設(shè)計目標是提供協(xié)調(diào)服務(wù),并不會提供大量的數(shù)據(jù)存儲,所以zookeeper 的主從架構(gòu)則能夠保證寫請求的順序性。
全局可線性化(Linearizable)寫入: 大體的意思就是先到達leader的請求會被優(yōu)先處理,leader決定寫請求的執(zhí)行順序。
客戶端FIFO順序:來自給定客戶端的請求按照發(fā)送順序執(zhí)行即可。
后續(xù)同樣會通過 實際集群演示來 展示整個quorum 的zookeeper集群工作形態(tài)。
3. zookeeper的案例演示
以下將通過zookeeper的客戶端 命令來演示zookeeper 的一些應(yīng)用場景 以及 集群模式下的quorum機制。
3.1 zookeeper 安裝
- zookeeper的環(huán)境依賴是JDK7+
- zookeeper源碼包下載,我這里使用的是git:
git clone https://github.com/apache/zookeeper.git
git checkout branch-3.5.8目前最新版本是3.6 - 創(chuàng)建zoo.cfg
可以直接拷貝cp zookeeper/conf/zoo_example.cfg zookeeper/conf/zoo.cfg
主要配置如下兩個配置,# the directory where the snapshot is stored. # do not use /tmp for storage, /tmp here is just # example sakes. dataDir=/tmp/zookeeper # the port at which the clients will connect clientPort=2181 ... - 導(dǎo)入
zookeeper/bin目錄到環(huán)境變量,方便使用相關(guān)的cli腳本,我放到了自己的~/.zshrc下,其他人可以放在自己的~/.bashrc
這樣能夠在整個客戶端的不同位置運行zookeeper的客戶端腳本命令。export ZOOKEEPER_HOME="$HOME/IdeaProjects/zookeeper" export PATH="$ZOOKEEPER_HOME/bin/":$PATH
到此 整個zookeeper 已經(jīng)安裝完畢,當然這里還需要描述以下不同操作系統(tǒng)下的方式,以上步驟適用于所有類unix系統(tǒng),包括mac os;Windows下只有最后一步不同,記得windows的shell rc文件和unix不同。
3.2 zookeeper啟動
以下可以直接運行的命令是我之前已經(jīng)將zookeeper/bin 目錄放入到了path變量中,所以能夠直接運行,具體步驟在上一節(jié)。
-
啟動server
zkServer.sh start/usr/bin/java ZooKeeper JMX enabled by default Using config: /Users/zhanghuigui/IdeaProjects/zookeeper/bin/../conf/zoo.cfg Starting zookeeper ... STARTED可以在你自己的zookeeper的安裝目錄下有一個
logs文件目錄,用來保存服務(wù)進行運行的相關(guān)日志
~/IdeaProjects/zookeeper/logs,其中IdeaProjects是我自己的目錄,可以打開你自己的目錄即可。
可以看到日志中并沒有異常報錯。我們再去zookeeper的data目錄看有哪一些數(shù)據(jù):
╰─$ tree . ├── version-2 │ └── snapshot.0 # zookeeper的快照文件 └── zookeeper_server.pid #zookeeper server的進程id1 directory, 2 files -
啟動 client
zkCli.sh,能夠看到已經(jīng)運行的client,因為我們沒有變更zoo.cfg端口號,所以client也會默認訪問zoo.cfg的端口號建立連接。... 2020-11-21 14:56:40,635 [myid:localhost:2181] - INFO [main-SendThread(localhost:2181):ClientCnxn$SendThread@1394] - Session establishment complete on server localhost/[0:0:0:0:0:0:0:1]:2181, sessionid = 0x100016e76ff0000, negotiated timeout = 30000WATCHER::WatchedEvent state:SyncConnected type:None path:null [zk: localhost:2181(CONNECTED) 0]這個時候能夠進行交互命令,實現(xiàn)zookeeper的client和server的通信。
3.3 zookeeper Client 交互式命令
- 輸入
h可以看到很多交互式命令[zk: localhost:2181(CONNECTED) 0] h ZooKeeper -server host:port cmd argsaddauth scheme authcloseconfig [-c] [-w] [-s]connect host:portcreate [-s] [-e] [-c] [-t ttl] path [data] [acl]delete [-v version] pathdeleteall pathdelquota [-n|-b] pathget [-s] [-w] pathgetAcl [-s] path... - 輸入
ls -R /所有訪問znode的命令都需要加絕對路徑,可以看到已經(jīng)有一些根節(jié)點下的子節(jié)點存在了[zk: localhost:2181(CONNECTED) 2] ls -R / / /zookeeper /zookeeper/config /zookeeper/quota create /app創(chuàng)建第幾個永久 znode
再次查看對應(yīng)的znode data tree信息[zk: localhost:2181(CONNECTED) 26] create /app1 Created /app1 [zk: localhost:2181(CONNECTED) 27] create /app2 Created /app2 [zk: localhost:2181(CONNECTED) 28] create /app1/app1_1 Created /app1/app1_1 [zk: localhost:2181(CONNECTED) 29] create /app1/app1_2 Created /app1/app1_2[zk: localhost:2181(CONNECTED) 30] ls -R / / /app1 /app2 /zookeeper /app1/app1_1 /app1/app1_2 /zookeeper/config /zookeeper/quotastat /查看 根目錄 znode元信息[zk: localhost:2181(CONNECTED) 31] stat / cZxid = 0x0 ctime = Thu Jan 01 08:00:00 CST 1970 # 創(chuàng)建時間 mZxid = 0x0 mtime = Thu Jan 01 08:00:00 CST 1970 #修改時間 pZxid = 0x13 cversion = 17 dataVersion = 0 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 0 numChildren = 3 #子節(jié)點數(shù)量
3.4 zookeeper 實現(xiàn)簡單的分布式鎖
分布式鎖的基本要求如下:
- 鎖被持有時其他 client無法訪問 臨界區(qū)的資源
- 持有鎖的進程/節(jié)點 宕機 ,鎖需要被釋放(防止死鎖)
這是分布式鎖的基本功能,通過zookeeper 顯示如上基本功能:
使用兩個Client來模擬一個集群中的兩個節(jié)點,訪問鎖的情況。
基本步驟如下:
- 節(jié)點一 持有鎖 – 成功
- 節(jié)點二 嘗試持有鎖 – 失敗
- 節(jié)點二 監(jiān)控鎖的使用情況
- 節(jié)點一 異常退出,并釋放鎖
- 節(jié)點二 感知到鎖被釋放,從而持有鎖 – 成功
如下演示:
這里主要通過zookeeper的臨時節(jié)點(Ephemeral) 來實現(xiàn)的,因為臨時znode節(jié)點異常退出或者client和server無心跳,則znode數(shù)據(jù)會被清除掉,能夠作為分布式鎖的節(jié)點異常退出鎖釋放的條件。
不過這樣的實現(xiàn) 后續(xù)需要深入探索以下zookeeper的一致性 和 這種場景下的性能問題,如果成百上千的客戶端在等待這把鎖,能否足夠快得釋放 以及 如果保證每次只有一個客戶端拿到鎖(如何保證多個客戶端同時創(chuàng)建一個inode,而只能有一個客戶端創(chuàng)建成功。。。,需要深入探討一下zookeeper的一致性模型)。
3.5 zookeeper實現(xiàn)master-worker架構(gòu)
master-worker 架構(gòu)是一種應(yīng)用比較廣泛的分布式系統(tǒng)架構(gòu)。
其中有一個master負責監(jiān)控worker的狀態(tài),并負責為worker分配對應(yīng)的任務(wù)。
有如下幾個約束規(guī)則:
- 在任何時刻,系統(tǒng)中只有一個master,不能同時出現(xiàn)兩個mater或者多個master,這樣整個系統(tǒng)的調(diào)度就亂掉了。
- 系統(tǒng)中除了處于active狀態(tài)的master,還有一個backup狀態(tài)的mater,如果active master異常,則backup master能夠立即接管,進行任務(wù)分配
- master 實施監(jiān)控worker狀態(tài),并能及時收到worker成員變化的通知。master收到worker成員變化的時候通常會重新進行任務(wù)分配。
比如HBase:
HMBase 是系統(tǒng)中的Master,HRegionServer 是系統(tǒng)中的worker。
HMBase 實時監(jiān)控HBase Cluster中worker的變化,把Region分配給各個HRegionServer。系統(tǒng)中有一個HMBase 服務(wù)處于active,其他處于backup狀態(tài)。
比如CEPH中的Cephfs:
同樣多個mds提供cephfs元數(shù)據(jù)管理功能,每一時刻只有一個mds-active ,所有的元數(shù)據(jù)調(diào)度都由這個active的mds負責,其他備mds則提供active mds的接管服務(wù)。
那么如何在Zookeeper中實現(xiàn)master-worker架構(gòu)呢?
-
使用一個臨時節(jié)點 /master表示master。master想要行使職能之前先要創(chuàng)建這個znode,創(chuàng)建成功,則進入active狀態(tài),否則進入
backup狀態(tài),并使用watch機制監(jiān)控/master,來及時得跟蹤active master的狀態(tài)。加入系統(tǒng)中現(xiàn)在有一個active master, 一個 backup master。如果active master異常,則它創(chuàng)建的/master就會被刪除,并且被處于監(jiān)聽狀態(tài)的backup master watch到,此時backup master通過創(chuàng)建自己的/master 臨時節(jié)點來成為active master行使職能。
-
worker 通過在/workers 目錄下創(chuàng)建子節(jié)點來加入集群
-
處于active 狀態(tài)的master 會通過watch 機制監(jiān)聽/works 下的znode列表,來感知集群worker的變化。
使用zookeeper實現(xiàn) 的基本功能master-worker架構(gòu) 以及 data tree中對應(yīng)的znode分布如上圖,master通過一個znode節(jié)點表示,work則分布在一個大的workers父節(jié)點之下。
其中zookeeper master 和 backup master選舉的演示功能如下:
主要是通過stat -w /master監(jiān)控狀態(tài)變化
zookeeper master監(jiān)控 works成員列表變化的演示過程如下:
主要通過ls -w /works 監(jiān)控節(jié)點成員變化
3.6 zookeeper quorum集群模式
之前的簡單分布式鎖以及master-work架構(gòu)都是在同一臺機器上通過不同的客戶端實現(xiàn)的,接下來的集群模式如果大家想要了解且有多臺服務(wù)器,玩一玩也不錯。沒有多臺服務(wù)器也是可以的,按照如下配置即啟動多個server進程組成quorum。
-
配置文件
之前單個server 只需要一個zoo.cfg,那我們配置集群模式肯定需要多個不同的zoo.cfg
其中的dataDir和clientPort需要配置成不同的值,三個zoo.cfg中的server.n需要配置成一樣的。server.1=127.0.0.1:7777:7778 server.2=127.0.0.1:6666:6667 server.3=127.0.0.1:5555:5556其中server.n中的ip是當前節(jié)點的ip,如果大家有各自的服務(wù)器,直接設(shè)置成服務(wù)器可通信的ip地址即可。
ip之后的第一個端口號是:quorum 協(xié)議的通信端口,第二個端口號是leader選舉的端口號
如下配置
除此之外,啟動server之前,還需要先在每個server各自的dataDir目錄下創(chuàng)建一個名稱為myid的文件,內(nèi)存分別是1,2,3,即在server.1的dataDir目錄下創(chuàng)建一個myid文件,內(nèi)容為1。其他的server依次都需要創(chuàng)建。 -
啟動過程需要通過前臺啟動server進程,并指定對應(yīng)的zoo.cfg
zkServer.sh start-foreground ./conf/zoo-node1.cfg,這樣日志能夠打印到客戶端啟動第一個server的時候,會有一些報錯,因為只有一個節(jié)點,quorum無法滿足法定人數(shù)。
依次啟動第二個server和第三個server,即能夠發(fā)現(xiàn)對應(yīng)的選舉過程,啟動第二個server的時候兩個server中會有一個成為leader,此時啟動client,即能夠正常提供zookeepe服務(wù)。通過命令
zkCli.sh -server 127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183,指定對應(yīng)的三個server ip和port即能夠建立client和zookeeper集群的鏈接。
演示過程如下:
4. 總結(jié)
通過對zookeeper的簡介,zookeeper的基本架構(gòu),zookeeper的一些簡單應(yīng)用的介紹,我們對zookeeper有了全面卻很初步的理解,更多的細節(jié):zookeeper 如何實現(xiàn)服務(wù)發(fā)現(xiàn),zookeeper如何做到事件驅(qū)動,zookeeper的類似quorum模式zab協(xié)議的實現(xiàn),以及如何在生產(chǎn)環(huán)境中通過java代碼實現(xiàn)分布式鎖,分布式隊列,選舉 這一系列更加底層更加易用的實現(xiàn)將在后續(xù)的學(xué)習(xí)中不斷總結(jié)。
這是一個非常有代表性的分布式系統(tǒng),涉及 分布式通信,分布式協(xié)議,分布式存儲 ,復(fù)雜度相比于專門的分布式存儲 系統(tǒng)低很多,更棒的是開源。每一個精妙的設(shè)計我們都能夠看到其底層實現(xiàn),細細品味。
總結(jié)
以上是生活随笔為你收集整理的一文入门 Zookeeper的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 诺字成语开头的成语有哪些?
- 下一篇: Go 分布式学习利器(11)-- Go