ZooKeeper概述与原理
1.ZooKeeper簡介
ZooKeeper 是一個開源的分布式協調框架,它的定位是為分布式應用提供一致性服務,是整個大數據體系的管理員,它會封裝好復雜易出錯的關鍵服務,將高效、穩定、易用的服務提供給用戶使用,為分布式應用提供協調服務的Apache項目,通俗點可以認為 ZooKeeper = 文件系統 + 監聽通知機制。
1.1 文件系統
Zookeeper維護一個類似文件系統的樹狀數據結構,這種特性使得 Zookeeper 不能用于存放大量的數據,每個節點的存放數據上限為1M。每個子目錄項如 NameService 都被稱作為 znode(目錄節點)。和文件系統一樣,我們能夠自由的增加、刪除znode,在一個znode下增加、刪除子znode,唯一的不同在于znode是可以存儲數據的。默認有四種類型的znode:
1.2 監聽通知機制
Watcher 監聽機制是 Zookeeper 中非常重要的特性,我們基于 Zookeeper 上創建的節點,可以對這些節點綁定監聽事件,比如可以監聽節點數據變更、節點刪除、子節點狀態變更等事件,通過這個事件機制,可以基于 Zookeeper 實現分布式鎖、集群管理等功能。Watcher 特性如下:
當數據發生變化的時候, Zookeeper 會產生一個 Watcher 事件,并且會發送到客戶端。但是客戶端只會收到一 次通知。如果后續這個節點再次發生變化,那么之前設置 Watcher 的客戶端不會再次收到消息。(Watcher 是一次 性的操作)。可以通過循環監聽去達到永久監聽效果。ZooKeeper 的 Watcher 機制,總的來說可以分為三個過程:
在微服務場景中,watcher 機制主要提供了服務通知功能,比如 Instance1 這個實例在 Service1 服務節點下注冊了一個 emphemeral 子節點后,它的某個服務消費者根據依賴配置在 Service1 節點上注冊了一個子節點 watcher,就如圖中的紅鑰匙。子節點類型的 watcher 會觀測 Service1 的子節點,即 InstanceX 節點,但不會觀測孫子節點 config1。那么當 Instance1 節點掛掉之后,watcher 可以做到通知給注冊自身的那個服務消費者,通知完一次后 wacther 也就被銷毀了。
wacther 原理框架如下:
- zookeeper 的 watcher 主要由 client、server 以及 watchManager 之間配合完成,包括 watcher 注冊以及觸發 2 個階段;
- 在 client 端注冊表為 ZkWatchManager,其中包括了 dataWatches、existWatches 以及 childWatches。在 server 端的注冊表在 DataTree 類中,封裝了 2 類 WatchManager,即 dataWatches 和 existWatches。dataWatches 代表當前節點的數據監聽,childWathes 代表子節點監聽,與 client 比少的 existWatches 也很容易理解,節點否存在需要客戶端去判斷;
- 注冊階段客戶端的 getData 和 exists 請求可以注冊 dataWatches,getChilden 可以注冊 childWatches。而觸發階段,setData 請求會觸發當前節點 dataWatches,create 請求會觸發當前節點 dataWatches 以及父節點的 childWatches,delete 請求則會觸發當前節點、父節點、子節點的 dataWatches,以及父節點的 childWatches;
- 請求階段的傳輸數據(包括 watcher 信息)會封裝在 request 和 response 中,比如 getData 請求會封裝 getDataRequest/getDataResponse。而觸發階段的 watcher 通知則通過事件 event 進行通信,server 端會發送一個 watcherEvent,而 client 端則會將其轉換成 watchedEvent 再進行處理;
- 在客戶端每個都會維護 2 個線程,SendThread 負責處理客戶端與服務端的請求通信,比如發送 getDataRequest,而 EventThread 則負責處理服務端的事件通知,即 watcher 的事件。
監聽流程大致如下:
1.3 Zookeeper 特點
- 集群:Zookeeper是一個領導者(Leader),多個跟隨者(Follower)組成的集群;
- 高可用性:集群中只要有半數以上節點存活,Zookeeper集群就能正常服務;
- 全局數據一致:每個Server保存一份相同的數據副本,Client無論連接到哪個Server,數據都是一致的;
- 更新請求順序進行:來自同一個Client的更新請求按其發送順序依次執行;
- 數據更新原子性:一次數據更新要么成功,要么失敗;
- 實時性:在一定時間范圍內,Client能讀到最新數據。
Zookeeper是一個分布式協調系統,滿足CP性,跟SpringCloud中的Eureka滿足AP不一樣。
分布式協調系統:Leader會同步數據到follower,用戶請求可通過follower得到數據,這樣不會出現單點故障,并 且只要同步時間無限短,那這就是個好的 分布式協調系統。CAP原則又稱CAP定理,指的是在一個分布式系統中,一致性(Consistency)、可用性(Availability)、分區 容錯性(Partition tolerance)。CAP 原則指的是,這三個要素最多只能同時實現兩點,不可能三者兼顧。2 Zookeeper 提供的功能
通過對 Zookeeper 中豐富的數據節點進行交叉使用,配合 Watcher 事件通知機制,可以非常方便的構建一系列分布式應用中涉及的核心功能,比如 數據發布/訂閱、負載均衡、命名服務、分布式協調/通知、集群管理、Master 選舉、分布式鎖和分布式隊列 等功能。
2.1 數據發布/訂閱
當某些數據由幾個機器共享,且這些信息經常變化數據量還小的時候,這些數據就適合存儲到ZK中。
- 數據存儲:將數據存儲到 Zookeeper 上的一個數據節點
- 數據獲取:應用在啟動初始化節點從 Zookeeper 數據節點讀取數據,并在該節點上注冊一個數據變更 Watcher
- 數據變更:當變更數據時會更新 Zookeeper 對應節點數據,Zookeeper會將數據變更通知發到各客戶端,客戶端接到通知后重新讀取變更后的數據即可
2.2 分布式鎖
基于ZooKeeper的分布式鎖一般有如下兩種:
- 保持獨占:在zk中有一個唯一的臨時節點,只有拿到節點的才可以操作數據,沒拿到的線程就需要等待。缺點:可能引發羊群效應,第一個用完后瞬間有999個同時并發的線程向zk請求獲得鎖
- 控制時序:避免了羊群效應,臨時節點已經預先存在,所有想要獲得鎖的線程在它下面創建臨時順序編號目錄節點,編號最小的獲得鎖,用完刪除,后面的依次排隊獲取
2.3 負載均衡
多個相同的jar包在不同的服務器上開啟相同的服務,可以通過nginx在服務端進行負載均衡的配置,也可以通過ZooKeeper在客戶端進行負載均衡配置。ZooKeeper負載均衡和Nginx負載均衡區別如下:
- ZooKeeper不存在單點問題,zab機制保證單點故障可重新選舉一個leader只負責服務的注冊與發現,不負責轉發,減少一次數據交換(消費方與服務方直接通信),需要自己實現相應的負載均衡算法
- Nginx存在單點問題,單點負載高數據量大,需要通過 KeepAlived + LVS 備機實現高可用。每次負載,都充當一次中間人轉發角色,增加網絡負載量(消費方與服務方間接通信),自帶負載均衡算法
2.4 命名服務
命名服務是指通過指定的名字來獲取資源或者服務的地址,利用 zk 創建一個全局唯一的路徑,這個路徑就可以作為一個名字,指向集群中的集群,提供的服務的地址,或者一個遠程的對象等等。
2.5 分布式協調/通知
- 對于系統調度來說,用戶更改zk某個節點的value, ZooKeeper會將這些變化發送給注冊了這個節點的 watcher 的所有客戶端,進行通知。
- 對于執行情況匯報來說,每個工作進程都在目錄下創建一個攜帶工作進度的臨時節點,那么匯總的進程可以監控目錄子節點的變化獲得工作進度的實時的全局情況。
2.6 集群管理
大數據體系下的大部分集群服務好像都通過ZooKeeper管理的,其實管理的時候主要關注的就是機器的動態上下線跟Leader選舉。
- 動態上下線
- 比如在zookeeper服務器端有一個znode叫 /Configuration,那么集群中每一個機器啟動的時候都去這個節點下創建一個EPHEMERAL類型的節點,比如server1 創建 /Configuration/Server1,server2創建**/Configuration /Server1**,然后Server1和Server2都watch /Configuration 這個父節點,那么也就是這個父節點下數據或者子節點變化都會通知到該節點進行watch的客戶端
- Leader選舉
- 利用ZooKeeper的強一致性,能夠保證在分布式高并發情況下節點創建的全局唯一性,即:同時有多個客戶端請求創建 /Master 節點,最終一定只有一個客戶端請求能夠創建成功。利用這個特性,就能很輕易的在分布式環境中進行集群選舉了
- 動態Master選舉。這就要用到 EPHEMERAL_SEQUENTIAL類型節點的特性了,這樣每個節點會自動被編號。允許所有請求都能夠創建成功,但是得有個創建順序,每次選取序列號最小的那個機器作為Master
3 Leader選舉
ZooKeeper集群節點個數一定是奇數個,一般3個或者5個就OK。為避免集群群龍無首,一定要選個大哥出來當Leader。
3.1 前提知識
3.1.1 節點四種狀態
- LOOKING:尋 找 Leader 狀態。當服務器處于該狀態時會認為當前集群中沒有 Leader,因此需要進入 Leader 選舉狀態
- FOLLOWING:跟隨者狀態。處理客戶端的非事務請求,轉發事務請求給 Leader 服務器,參與事務請求 Proposal(提議) 的投票,參與 Leader 選舉投票
- LEADING:領導者狀態。事務請求的唯一調度和處理者,保證集群事務處理的順序性,集群內部個服務器的調度者(管理follower,數據同步)
- OBSERVING:觀察者狀態。3.0 版本以后引入的一個服務器角色,在不影響集群事務處理能力的基礎上提升集群的非事務處理能力,處理客戶端的非事務請求,轉發事務請求給 Leader 服務器,不參與任何形式的投票
3.1.2 服務器ID
既Server id,一般在搭建ZK集群時會在myid文件中給每個節點搞個唯一編號,編號越大在Leader選擇算法中的權重越大,比如初始化啟動時就是根據服務器ID進行比較。
3.1.3 ZXID
- ZooKeeper 采用全局遞增的事務 Id 來標識,所有 proposal(提議)在被提出的時候加上了ZooKeeper Transaction Id ,zxid是64位的Long類型,這是保證事務的順序一致性的關鍵。zxid中高32位表示紀元epoch,低32位表示事務標識xid。你可以認為zxid越大說明存儲數據越新。
- 每個leader都會具有不同的epoch值,表示一個紀元/朝代,用來標識 leader 周期。每個新的選舉開啟時都會生成一個新的epoch,新的leader產生的話epoch會自增,會將該值更新到所有的zkServer的zxid和epoch
- xid是一個依次遞增的事務編號。數值越大說明數據越新,所有 proposal(提議)在被提出的時候加上了zxid,然后會依據數據庫的兩階段過程,首先會向其他的 server 發出事務執行請求,如果超過半數的機器都能執行并且能夠成功,那么就會開始執行。
3.2 Leader選舉
Leader的選舉一般分為啟動時選舉跟Leader掛掉后的運行時選舉。
3.2.1 啟動時Leader選舉
我們以上面的5臺機器為例,只有超過半數以上,即最少啟動3臺服務器,集群才能正常工作。
3.2.2 運行時Leader選舉
運行時候如果Master節點崩潰了會走恢復模式,新Leader選出前會暫停對外服務,大致可以分為四個階段:選舉、發現、同步、廣播。
3.3 腦裂
腦裂問題是集群部署必須考慮的一點,比如在Hadoop跟Spark集群中。而ZAB為解決腦裂問題,要求集群內的節點數量為2N+1。當網絡分裂后,始終有一個集群的節點數量過半數,而另一個節點數量小于N+1, 因為選舉Leader需要過半數的節點同意,所以我們可以得出如下結論:有了過半機制,對于一個Zookeeper集群,要么沒有Leader,要沒只有1個Leader,這樣就避免了腦裂問題。
4 一致性協議之 ZAB
4.1 ZAB 協議介紹
ZAB (Zookeeper Atomic Broadcast 原子廣播協議) 協議是為分布式協調服務ZooKeeper專門設計的一種支持崩潰恢復的一致性協議。基于該協議,ZooKeeper 實現了一種主從模式的系統架構來保持集群中各個副本之間的數據一致性。分布式系統中leader負責外部客戶端的寫請求,follower服務器負責讀跟同步。這時需要解決倆問題:
Leader 服務器是如何把數據更新到所有的Follower的。 Leader 服務器突然間失效了,集群咋辦?因此ZAB協議為了解決上面兩個問題而設計了兩種工作模式,整個 Zookeeper 就是在這兩個模式之間切換:
原子廣播模式:把數據更新到所有的follower。 崩潰恢復模式:Leader發生崩潰時,如何恢復。4.2 原子廣播模式
你可以認為消息廣播機制是簡化版的 2PC協議,就是通過如下的機制保證事務的順序一致性的。
FIFO隊列把ACK返回給Leader
4.3 崩潰恢復
消息廣播過程中,Leader 崩潰了還能保證數據一致嗎?當 Leader 崩潰會進入崩潰恢復模式。其實主要是對如下兩種情況的處理:
Leader 在復制數據給所有 Follwer 之后崩潰,怎么辦? Leader 在收到 Ack 并提交了自己,同時發送了部分 commit 出去之后崩潰咋辦?針對此問題,ZAB 定義了 2 個原則:
ZAB 協議確保執行那些已經在 Leader 提交的事務最終會被所有服務器提交。 ZAB 協議確保丟棄那些只在 Leader 提出/復制,但沒有提交的事務至于如何實現確保提交已經被 Leader 提交的事務,同時丟棄已經被跳過的事務呢?關鍵點就是依賴上面說到過的 ZXID了。
4.4 ZAB 特性
- 一致性保證:可靠提交(Reliable delivery) ,如果一個事務 A 被一個server提交(committed)了,那么它最終一定會被所有的server提交
- 全局有序(Total order):假設有A、B兩個事務,有一臺server先執行A再執行B,那么可以保證所有server上A始終都被在B之前執行
- 因果有序(Causal order):如果發送者在事務A提交之后再發送B,那么B必將在A之后執行
- 高可用性:只要大多數(法定數量)節點啟動,系統就行正常運行
- 可恢復性:當節點下線后重啟,它必須保證能恢復到當前正在執行的事務
4.5 ZAB 和 Paxos 對比
- 相同點
- 兩者都存在一個類似于 Leader 進程的角色,由其負責協調多個 Follower 進程的運行
- Leader 進程都會等待超過半數的 Follower 做出正確的反饋后,才會將一個提案進行提交
- ZAB 協議中,每個 Proposal 中都包含一個 epoch 值來代表當前的 Leader周期,Paxos 中名字為 Ballot
- 不同點
- ZAB 用來構建高可用的分布式數據主備系統(Zookeeper),Paxos 是用來構建分布式一致性狀態機系統
5 ZooKeeper 零散知識
5.1 常見指令
Zookeeper 有三種部署模式:
部署完畢后常見指令如下:
| help | 顯示所有操作命令 |
| ls path [watch] | 查看當前節點數據并能看到更新次數等數據 |
| create | 普通創建, -s 含有序列, |
| -e 臨時(重啟或者超時消失) | |
| get path [watch] | 獲得節點的值 |
| set | 設置節點的具體值 |
| stat | 查看節點狀態 |
| delete | 刪除節點 |
| rmr | 遞歸刪除節點 |
5.2 Zookeeper客戶端
5.2.1. Zookeeper原生客戶端
Zookeeper客戶端是異步的哦!需要引入CountDownLatch 來確保連接好了再做下面操作。Zookeeper原生api是不支持迭代式的創建跟刪除路徑的,具有如下弊端:
5.2.2. ZkClient
開源的zk客戶端,在原生API基礎上封裝,是一個更易于使用的zookeeper客戶端,做了如下優化:
5.2.3. Curator
開源的zk客戶端,在原生API基礎上封裝,apache頂級項目。是Netflix公司開源的一套Zookeeper客戶端框架。了解過Zookeeper原生API都會清楚其復雜度。Curator幫助我們在其基礎上進行封裝、實現一些開發細節,包括接連重連、反復注冊Watcher和NodeExistsException等。目前已經作為Apache的頂級項目出現,是最流行的Zookeeper客戶端之一。
5.2.4. Zookeeper圖形化客戶端工具
工具名叫ZooInspector
5.3 ACL 權限控制機制
ACL全稱為Access Control List 即訪問控制列表,用于控制資源的訪問權限。zookeeper利用ACL策略控制節點的訪問權限,如節點數據讀寫、節點創建、節點刪除、讀取子節點列表、設置節點權限等。基于scheme :id :permission的方式進行權限控制,scheme表示授權模式、id模式對應值、permission即具體的增刪改權限位。
- scheme認證模型
| world | 開放模式,world表示全世界都可以訪問(這是默認設置) |
| ip | ip模式,限定客戶端IP防問 |
| auth | 用戶密碼認證模式,只有在會話中添加了認證才可以防問 |
| digest | 與auth類似,區別在于auth用明文密碼,而digest 用sha-1+base64加密后的密碼。在實際使用中digest 更常見。 |
- permission權限位
| c | CREATE | 可以創建子節點 |
| d | DELETE | 可以刪除子節點(僅下一級節點) |
| r | READ | 可以讀取節點數據及顯示子節點列表 |
| w | WRITE | 可以設置節點數據 |
| a | ADMIN | 可以設置節點訪問控制列表權限 |
- acl 相關命令
| getAcl | getAcl | 讀取ACL權限 |
| setAcl | setAcl | 設置ACL權限 |
| addauth | addauth | 添加認證用戶 |
world權限示例語法:setAcl world:anyone:<權限位> ,其中world模式中anyone是唯一的值,表示所有人。查看默認節點權限:
#創建一個節點 create -e /testAcl #查看節點權限 getAcl /testAcl #返回的默認權限表示 ,所有人擁有所有權限。 'world,'anyone: cdrwa修改默認權限為讀寫:
#設置為rw權限 setAcl /testAcl world:anyone:rw # 可以正常讀 get /testAcl # 無法正常創建子節點 create -e /testAcl/t "hi" # 返回沒有權限的異常 Authentication is not valid : /testAcl/t5.4 Zookeeper使用注意事項
- 集群中機器的數量并不是越多越好,一個寫操作需要半數以上的節點ack,所以集群節點數越多,整個集群可以抗掛點的節點數越多(越可靠),但是吞吐量越差。集群的數量必須為奇數
- zk是基于內存進行讀寫操作的,有時候會進行消息廣播,因此不建議在節點存取容量比較大的數據
- dataDir目錄、dataLogDir兩個目錄會隨著時間推移變得龐大,容易造成硬盤滿了。建議自己編寫或使用自帶的腳本保留最新的n個文件
- 默認最大連接數 默認為60,配置maxClientCnxns參數,配置單個客戶端機器創建的最大連接數
參考文章:
https://mp.weixin.qq.com/s/EtYBzNhl2tKS3SjFOja1DA?
https://www.hiyin.com/81643.html
總結
以上是生活随笔為你收集整理的ZooKeeper概述与原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Netty专题-(3)NIO网络编程
- 下一篇: JVM专题(2)-类加载器子系统