一文入门 Zookeeper
文章目錄
- 1. zookeeper 簡介
- 1.1 什么是zookeeper
- 1.2 zookeeper 發展歷史
- 1.3 zookeeper 典型應用場景
- 1.4 zookeeper 提供的服務
- 1.5 zookeeper的數據模型
- 1.6 znode的分類
- 2. Zookeeper架構
- 2.1 整體架構
- 2.2 session
- 2.3 Quorum 模式
- 2.4 數據一致性
- 3. zookeeper的案例演示
- 3.1 zookeeper 安裝
- 3.2 zookeeper啟動
- 3.3 zookeeper Client 交互式命令
- 3.4 zookeeper 實現簡單的分布式鎖
- 3.5 zookeeper實現master-worker架構
- 3.6 zookeeper quorum集群模式
- 4. 總結
通過本篇文章,你能夠了解zookeeper的基本知識和應用場景,并且能夠通過命令行完整體會到zookeeper的功能和基本應用場景。當然,因為作者水平有限,對分布式系統的理解多少有一些不足,希望大家不吝賜教。
本文通過如下幾個方面介紹zookeeper基礎:
- zookeeper 的 發展歷史 ,提供什么服務 以及 基本功能和應用場景
- zookeeper 的整體架構
- zookeeper 實現簡單分布式鎖 和 master-work架構,并演示zookeeper的集群模式
1. zookeeper 簡介
希望通過zookeeper的發展歷史,基本功能介紹以及應用場景來讓大家對zookeeper有一個宏觀的認識。
1.1 什么是zookeeper
一句話:zookeeper 是一個開源的分布式協同服務系統。說到系統而不是像raft那樣的庫,我們就能夠理解 zookeeper擁有自己的client和server服務進程。
zookeeper的設計目標: 是將那一些復雜且容易出錯的分布式系統服務封裝起來,抽象出一個高效易用的原語集,并向用戶提供簡單的接口。
而比較有趣的是zookeeper 這個名字,認為zookeeper的工作性質就像是一個動物園管理員,用來協調整個動物園內的工作,讓每一個動物都井井有條得處在自己的籠子里,游客(IO請求)能夠安全得在動物園里游覽并達到自己想去的位置而不受其他動物的影響。
1.2 zookeeper 發展歷史
Zookeeper最早起源于雅虎研究院的一個研究小組。當時研究人員發現,在雅虎內部很多大型系統都依賴一個類似的系統來進行分布式協同,但是這一些系統都存在分布式單點問題
所以雅虎人員開發了一個通用的無單點問題的分布式協調框架,這就是ZK。在 zookeeper 被大量使用之后出現了如下著名的開源項目:
- Hadoop: 使用zookeeper 做namespace的高可用
- HBase: 保證集群只有一個Master;保存hbase:meta 表的位置,保存集群中的RegionServer列表
- Kafka: 集群成員管理,controller 節點選舉
可見zookeeper還是能夠提供足夠的應用場景,且能夠得到較多的開源項目的認可。
接下來可以看看zookeeper的典型場景:
1.3 zookeeper 典型應用場景
- 配置管理(configuration management)
- DNS 服務
- 組成成員管理(group membership) – 經典的分布式數據庫HBASE
- 各種分布式鎖(上一家公司參與的超融合存儲項目 中即使用zookeeper來做一些集群鎖的作用)
關于組成成員管理 和 分布式鎖 后續的介紹中會為大家演示zookeeper原生接口如何實現簡單的分布式鎖的功能。當然,更深層次實現介紹還需要再摸索一番。
ps: zookeeper 適用于存儲和協同相關的關鍵數據,不適合用于大量數據 存儲。
1.4 zookeeper 提供的服務
這里簡單說一下 zookeeper 提供什么樣子的服務給到客戶端。
- Application 直接使用Zookeeper客戶端庫即可,目前主要是Java接口
- Zookeeper的客戶端庫負責和Zookeeper集群中的server進行交互
1.5 zookeeper的數據模型
這里描述的并不是zookeeper的架構,僅僅是其數據存儲相關的介紹。
我們知道在整個分布式系統的領域里,主流的兩種數據存儲方式是:樹型 和 key-value型。
zookeeper這里選擇樹型模型(**data-tree)**的考慮是:
- 便于表達數據之間的層次關系
- 便于為不同的應用分配獨立的命名空間
如上圖中 每一個節點叫做znode,都可以用來保存數據。
同時每一個節點都有一個版本,這個版本是從0開始計數。
關于Zookeeper 提供的data-tree 數據模型有以下幾點需要注意:
- 使用Unix 風格路徑定位znode,例如 /A/A_1 表示A的子節點A_1
- 支持全量寫入和讀取,沒有像普通文件系統那樣支持部分寫入和讀取
- Data tree的所有API 都是wait-free的;正在調用的API不影響其他API的完成 即為wait-free
- Data tree 直接提供鎖這樣的分布式系統機制,但可以通過API來實現分布式協同機制
1.6 znode的分類
- 持久性Znode(Persistent):創建之后即使發生節點異?;蚣哄礄C,znode 的信息也不會丟失
- 臨時性Znode(Ephemeral): 節點宕機 或 在指定時間timeout 沒有給zookeeper集群發消息, 這樣的znode就會消失
- 持久順序性Znode(Persistent_Sequential) znode 關聯一個唯一的單調遞增整數,這個整數是znode名字后綴,具備持久性。
- 臨時順序性Znode(Ephemeral_Sequential) 擁有順序性的臨時Znode 節點。
2. Zookeeper架構
以下的zookeeper架構僅僅是簡要的介紹,并沒有深入到各個機制的細節,后續文章會嘗試進行探討。
2.1 整體架構
應用使用zookeeper客戶端(shell) 或者 zookeeper客戶端庫(java api) 使用zookeeper的基本服務。
zookeeper 客戶端用來zookeeper集群進行交互。
實際的zookeeper集群可以有兩種模式:standalone 模式 和 quorum 模式
- standalone模式 即集群中只有一個節點作為server,一般的分布式系統不需要這樣的模式,存在單點問題。適用于開發場景。
- quorum 模式 即 集群有3個及以上的節點,通過quorum協議 為集群維護一個法定人數,只要集群存活的節點大于2/n+1(n表示節點數)個,則集群能夠一直對外提供服務。
2.2 session
zookeeper 客戶端和zookeeper集群建立鏈接時,會和集群中的某一個節點創建session。
關閉session有兩種情況(客戶端和集群斷開鏈接):
- 客戶端可以主動關閉session
- zookeeper集群節點沒有在指定的timeout時間內收到客戶端的消息的話,zookeeper節點也會關閉session。
如果zookeeper客戶端庫發現鏈接的zookeeper集群出錯,會自動的和其他的zookeeper節點建立鏈接。如下圖:
2.3 Quorum 模式
處于Quorum模式的集群包含多個節點,下圖中的三個節點組成了一個zookeeper集群,其中綠色節點1的是leader節點,節點2和節點3是follower節點。leader節點可以處理讀寫請求,follower節點只能處理讀請求,當寫請求到達follower節點時會將寫請求轉發到leader節點。
寫請求寫入到leader之后,再由leader負責將數據同步到其他的follower節點。
2.4 數據一致性
我們知道分布式系統中的數據一致性主要是由于寫 的順序問題帶來的一系列讀/寫相關的一致性問題。
zookeeper的設計目標是提供協調服務,并不會提供大量的數據存儲,所以zookeeper 的主從架構則能夠保證寫請求的順序性。
全局可線性化(Linearizable)寫入: 大體的意思就是先到達leader的請求會被優先處理,leader決定寫請求的執行順序。
客戶端FIFO順序:來自給定客戶端的請求按照發送順序執行即可。
后續同樣會通過 實際集群演示來 展示整個quorum 的zookeeper集群工作形態。
3. zookeeper的案例演示
以下將通過zookeeper的客戶端 命令來演示zookeeper 的一些應用場景 以及 集群模式下的quorum機制。
3.1 zookeeper 安裝
- zookeeper的環境依賴是JDK7+
- zookeeper源碼包下載,我這里使用的是git:
git clone https://github.com/apache/zookeeper.git
git checkout branch-3.5.8目前最新版本是3.6 - 創建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 ... - 導入
zookeeper/bin目錄到環境變量,方便使用相關的cli腳本,我放到了自己的~/.zshrc下,其他人可以放在自己的~/.bashrc
這樣能夠在整個客戶端的不同位置運行zookeeper的客戶端腳本命令。export ZOOKEEPER_HOME="$HOME/IdeaProjects/zookeeper" export PATH="$ZOOKEEPER_HOME/bin/":$PATH
到此 整個zookeeper 已經安裝完畢,當然這里還需要描述以下不同操作系統下的方式,以上步驟適用于所有類unix系統,包括mac os;Windows下只有最后一步不同,記得windows的shell rc文件和unix不同。
3.2 zookeeper啟動
以下可以直接運行的命令是我之前已經將zookeeper/bin 目錄放入到了path變量中,所以能夠直接運行,具體步驟在上一節。
-
啟動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文件目錄,用來保存服務進行運行的相關日志
~/IdeaProjects/zookeeper/logs,其中IdeaProjects是我自己的目錄,可以打開你自己的目錄即可。
可以看到日志中并沒有異常報錯。我們再去zookeeper的data目錄看有哪一些數據:
╰─$ tree . ├── version-2 │ └── snapshot.0 # zookeeper的快照文件 └── zookeeper_server.pid #zookeeper server的進程id1 directory, 2 files -
啟動 client
zkCli.sh,能夠看到已經運行的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]這個時候能夠進行交互命令,實現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的命令都需要加絕對路徑,可以看到已經有一些根節點下的子節點存在了[zk: localhost:2181(CONNECTED) 2] ls -R / / /zookeeper /zookeeper/config /zookeeper/quota create /app創建第幾個永久 znode
再次查看對應的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 # 創建時間 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 #子節點數量
3.4 zookeeper 實現簡單的分布式鎖
分布式鎖的基本要求如下:
- 鎖被持有時其他 client無法訪問 臨界區的資源
- 持有鎖的進程/節點 宕機 ,鎖需要被釋放(防止死鎖)
這是分布式鎖的基本功能,通過zookeeper 顯示如上基本功能:
使用兩個Client來模擬一個集群中的兩個節點,訪問鎖的情況。
基本步驟如下:
- 節點一 持有鎖 – 成功
- 節點二 嘗試持有鎖 – 失敗
- 節點二 監控鎖的使用情況
- 節點一 異常退出,并釋放鎖
- 節點二 感知到鎖被釋放,從而持有鎖 – 成功
如下演示:
這里主要通過zookeeper的臨時節點(Ephemeral) 來實現的,因為臨時znode節點異常退出或者client和server無心跳,則znode數據會被清除掉,能夠作為分布式鎖的節點異常退出鎖釋放的條件。
不過這樣的實現 后續需要深入探索以下zookeeper的一致性 和 這種場景下的性能問題,如果成百上千的客戶端在等待這把鎖,能否足夠快得釋放 以及 如果保證每次只有一個客戶端拿到鎖(如何保證多個客戶端同時創建一個inode,而只能有一個客戶端創建成功。。。,需要深入探討一下zookeeper的一致性模型)。
3.5 zookeeper實現master-worker架構
master-worker 架構是一種應用比較廣泛的分布式系統架構。
其中有一個master負責監控worker的狀態,并負責為worker分配對應的任務。
有如下幾個約束規則:
- 在任何時刻,系統中只有一個master,不能同時出現兩個mater或者多個master,這樣整個系統的調度就亂掉了。
- 系統中除了處于active狀態的master,還有一個backup狀態的mater,如果active master異常,則backup master能夠立即接管,進行任務分配
- master 實施監控worker狀態,并能及時收到worker成員變化的通知。master收到worker成員變化的時候通常會重新進行任務分配。
比如HBase:
HMBase 是系統中的Master,HRegionServer 是系統中的worker。
HMBase 實時監控HBase Cluster中worker的變化,把Region分配給各個HRegionServer。系統中有一個HMBase 服務處于active,其他處于backup狀態。
比如CEPH中的Cephfs:
同樣多個mds提供cephfs元數據管理功能,每一時刻只有一個mds-active ,所有的元數據調度都由這個active的mds負責,其他備mds則提供active mds的接管服務。
那么如何在Zookeeper中實現master-worker架構呢?
-
使用一個臨時節點 /master表示master。master想要行使職能之前先要創建這個znode,創建成功,則進入active狀態,否則進入
backup狀態,并使用watch機制監控/master,來及時得跟蹤active master的狀態。加入系統中現在有一個active master, 一個 backup master。如果active master異常,則它創建的/master就會被刪除,并且被處于監聽狀態的backup master watch到,此時backup master通過創建自己的/master 臨時節點來成為active master行使職能。
-
worker 通過在/workers 目錄下創建子節點來加入集群
-
處于active 狀態的master 會通過watch 機制監聽/works 下的znode列表,來感知集群worker的變化。
使用zookeeper實現 的基本功能master-worker架構 以及 data tree中對應的znode分布如上圖,master通過一個znode節點表示,work則分布在一個大的workers父節點之下。
其中zookeeper master 和 backup master選舉的演示功能如下:
主要是通過stat -w /master監控狀態變化
zookeeper master監控 works成員列表變化的演示過程如下:
主要通過ls -w /works 監控節點成員變化
3.6 zookeeper quorum集群模式
之前的簡單分布式鎖以及master-work架構都是在同一臺機器上通過不同的客戶端實現的,接下來的集群模式如果大家想要了解且有多臺服務器,玩一玩也不錯。沒有多臺服務器也是可以的,按照如下配置即啟動多個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是當前節點的ip,如果大家有各自的服務器,直接設置成服務器可通信的ip地址即可。
ip之后的第一個端口號是:quorum 協議的通信端口,第二個端口號是leader選舉的端口號
如下配置
除此之外,啟動server之前,還需要先在每個server各自的dataDir目錄下創建一個名稱為myid的文件,內存分別是1,2,3,即在server.1的dataDir目錄下創建一個myid文件,內容為1。其他的server依次都需要創建。 -
啟動過程需要通過前臺啟動server進程,并指定對應的zoo.cfg
zkServer.sh start-foreground ./conf/zoo-node1.cfg,這樣日志能夠打印到客戶端啟動第一個server的時候,會有一些報錯,因為只有一個節點,quorum無法滿足法定人數。
依次啟動第二個server和第三個server,即能夠發現對應的選舉過程,啟動第二個server的時候兩個server中會有一個成為leader,此時啟動client,即能夠正常提供zookeepe服務。通過命令
zkCli.sh -server 127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183,指定對應的三個server ip和port即能夠建立client和zookeeper集群的鏈接。
演示過程如下:
4. 總結
通過對zookeeper的簡介,zookeeper的基本架構,zookeeper的一些簡單應用的介紹,我們對zookeeper有了全面卻很初步的理解,更多的細節:zookeeper 如何實現服務發現,zookeeper如何做到事件驅動,zookeeper的類似quorum模式zab協議的實現,以及如何在生產環境中通過java代碼實現分布式鎖,分布式隊列,選舉 這一系列更加底層更加易用的實現將在后續的學習中不斷總結。
這是一個非常有代表性的分布式系統,涉及 分布式通信,分布式協議,分布式存儲 ,復雜度相比于專門的分布式存儲 系統低很多,更棒的是開源。每一個精妙的設計我們都能夠看到其底層實現,細細品味。
總結
以上是生活随笔為你收集整理的一文入门 Zookeeper的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 诺字成语开头的成语有哪些?
- 下一篇: Go 分布式学习利器(11)-- Go