Zookeeper概述
zookeeper高容錯數據一致性協議(CP)的分佈式小文件系統,提供相似於文件系統的目錄方式的數據存儲。node
- 全局數據一致性:每一個server保存一份相同的數據副本,client不管鏈接到哪一個server展現的數據都是一致的。
- 可靠性:一旦事務成功提交,就會被保留下來。
- 有序性:客戶端發起的事務請求,在也會順序的應用在Zookeeper中。
- 數據更新原子性:一次數據更新要麼成功要麼失敗,不存在中間狀態。
- 實時性:保證客戶端在一個間隔時間範圍內獲取服務的更新消息或服務器失效信息。
zookeeper單機
數據模型:
每個節點都是znode(兼具文件和目錄兩種特色),每個znode都具備原子操做。znode存儲的數據大小有限制(默認1MB),經過絕對路徑引用。 znode分爲3個部分:數據庫
- stat:狀態信息,描述znode的版本和權限等信息。
- data:與該znode關聯的數據。
- children:該znode下的子節點。
znode節點類型
- 臨時節點:該節點的生命週期依賴於建立它的會話,一旦會話結束臨時節點就會被刪除。臨時節點不容許擁有子節點。
- 永久節點:只能經過客戶端顯示執行刪除操做。
- 臨時序列化節點。
- 永久序列化節點。
znode序列化:znode的名字後面追加一個不斷增長的序列號。每個序列號對父節點來講是惟一的,能夠記錄每個子節點的前後順序。緩存
znode節點屬性
zookeeper的節點屬性包括節點數據,狀態,權限等信息。安全
屬性 |
說明 |
cZxid |
znode建立節點的事務ID,Zookeeper中每一個變化都會產生一個全局惟一的zxid。經過它可肯定更新操做的前後順序 |
ctime |
建立時間 |
mZxid |
修改節點的事務ID |
mtime |
最後修改時間 |
pZxid |
子節點變動的事務ID,添加子節點或刪除子節點就會影響子節點列表,可是修改子節點的數據內容則不影響該ID |
cversion |
子節點版本號,子節點每次修改版本號加1 |
dataversion |
數據版本號,數據每次修改該版本號加1 ,多個客戶端對同一個znode進行更新操做時,由於數據版本號,才能保證更新操做的前後順序性。例:客戶端A正在對znode節點作更新操做,此時若是另外一個客戶端B同時更新了這個znode,則A的版本號已通過期,那麼A調用setData不會成功。 |
aclversion |
權限版本號,權限每次修改該版本號加1 |
dataLength |
該節點的數據長度 |
numChildern |
該節點擁有的子節點的數量 |
znode ACL權限控制
ACL權限控制使用 schema:id:permission來標識。 示例:setAcl /test2 ip:128.0.0.1:crwda服務器
Schema
Schema枚舉值 |
說明 |
world |
使用用戶名設置,id爲一個用戶,但這個id只有一個值:anyone,表明全部人 |
ip |
使用IP地址進行認證,ID對應爲一個IP地址或IP段 |
auth |
使用已添加認證的用戶進行認證,以經過addauth digest user:pwd 來添加當前上下文中的受權用戶 |
digest |
使用「用戶名:密碼」方式認證 |
permission
權限 |
ACL簡寫 |
描述 |
CREATE |
c |
能夠建立子節點 |
DELETE |
d |
能夠刪除子節點(僅下一級節點) |
READ |
r |
能夠讀取節點數據及顯示子節點列表 |
WRITE |
w |
能夠設置節點數據 |
ADMIN |
a |
能夠設置節點訪問控制列表權限 |
權限相關命令
命令 |
使用方式 |
描述 |
getAcl |
getAcl
|
讀取ACL權限 |
setAcl |
setAcl
|
設置ACL權限 |
addauth |
addauth |
添加認證用戶 |
zookeeper:提供了分佈式發佈/訂閱功能,能讓多個訂閱者同時監聽某一個主題對象,經過watche機制來實現。網絡
zookeeper watch機制
一個Watch事件是一個一次性的觸發器,當被設置了Watch的數據發生了改變的時候,則服務器將這個改變發送給設置了Watch的客戶端,以便通知它們。session
- 父節點的建立,修改,刪除都會觸發Watcher事件。
- 子節點的建立,刪除會觸發Watcher事件。
監聽器watch特性
特性 |
說明 |
一次性 |
Watcher是一次性的,一旦被觸發就會移除,再次使用時須要從新註冊。監聽的客戶端不少狀況下,每次變更都要通知到全部的客戶端,給網絡和服務器形成很大壓力。一次性能夠減輕這種壓力 |
客戶端順序回調 |
客戶端 Watcher 回調的過程是一個串行同步的過程。 |
輕量 |
Watcher 通知很是簡單,只會告訴客戶端發生了事件,而不會說明事件的具體內容。 |
監聽器原理
- 首先要有一個main()線程,在main線程中建立Zookeeper客戶端,這時就會建立兩個線程,一個負責網絡鏈接通訊(connet),一個負責監聽(listener)。
- 經過connect線程將註冊的監聽事件發送給Zookeeper服務端。
- 在Zookeeper服務端的註冊監聽器列表中將註冊的監聽事件添加到列表中。
- Zookeeper監聽到有數據或路徑變化,就會將這個消息發送
給listener線程。數據結構
- listener線程內部調用了process()方法來觸發Watcher。
zookeeper會話管理
- 客戶端會不時地向所鏈接的ZkServer發送ping消息,ZkServer接收到ping消息,或者任何其它消息的時候,都會將客戶端的session_id,session_timeout記錄在一個map中。
- Leader ZkServer會週期性地向全部的follower發送心跳消息,follower接收到ping消息後,會將記錄session信息的map做爲返回消息,返回給leader,同時清空follower本地的map。 Leader使用這些信息從新計算客戶端的超時時間。
- 一旦在session timout的時間到,leader即沒有從其它follower上收集到客戶端的session信息,也沒有直接接收到該客戶端的任何請求,那麼該客戶端的session就會被關閉。
zookeeper數據模型
- zk維護的數據主要有:客戶端的會話(session)狀態及數據節(dataNode)信息。
- zk在內存中構造了個DataTree的數據結構,維護着path到dataNode的映射以及dataNode間的樹狀層級關係。爲了提升讀取性能,集羣中每一個服務節點都是將數據全量存儲在內存中。因此,zk最適於讀多寫少且輕量級數據的應用場景。
3.數據僅存儲在內存是很不安全的,zk採用事務日誌文件及快照文件的方案來落盤數據,保障數據在不丟失的狀況下能快速恢復。分佈式
Zookeeper集羣
集羣角色
- Leader:集羣工做的核心,事務請求的惟一調度和處理者,保證事務處理的順序性。對於有寫操做的請求,需統一轉發給Leader處理。Leader需決定編號執行操做。
- Follower:處理客戶端非事務請求,轉發事務請求轉發給Leader,參與Leader選舉。
- Observer觀察者:進行非事務請求的獨立處理,對於事務請求,則轉發給Leader服務器進行處理.不參與投票。
事務
- 事務:ZooKeeper中,能改變ZooKeeper服務器狀態的操做稱爲事務操做。通常包括數據節點建立與刪除、數據內容更新和客戶端會話建立與失效等操做。對應每個事務請求,ZooKeeper 都會爲其分配一個全局惟一的事務ID,用 ZXID 表示,一般是一個64位的數字。每個ZXID對應一次更新操做,從這些ZXID中能夠間接地識別出ZooKeeper處理這些事務操做請求的全局順序。
- 事務日誌:全部事務操做都是須要記錄到日誌文件中的,可經過 dataLogDir配置文件目錄,文件是以寫入的第一條事務zxid爲後綴,方便後續的定位查找。zk會採起「磁盤空間預分配」的策略,來避免磁盤Seek頻率,提高zk服務器對事務請求的影響能力。默認設置下,每次事務日誌寫入操做都會實時刷入磁盤,也能夠設置成非實時(寫到內存文件流,定時批量寫入磁盤),但那樣斷電時會帶來丟失數據的風險。事務日誌記錄的次數達到必定數量後,就會將內存數據庫序列化一次,使其持久化保存到磁盤上,序列化後的文件稱爲"快照文件"。有了事務日誌和快照,就可讓任意節點恢復到任意時間點
性能
- 數據快照:數據快照是zk數據存儲中另外一個很是核心的運行機制。數據快照用來記錄zk服務器上某一時刻的全量內存數據內容,並將其寫入到指定的磁盤文件中,可經過dataDir配置文件目錄。可配置參數snapCount,設置兩次快照之間的事務操做個數,zk節點記錄完事務日誌時,會統計判斷是否須要作數據快照(距離上次快照,事務操做次數等於[snapCount/2~snapCount] 中的某個值時,會觸發快照生成操做,隨機值是爲了不全部節點同時生成快照,致使集羣影響緩慢)。
過半原則
1. 過半:所謂「過半」是指大於集羣機器數量的一半,即大於或等於(n/2+1),此處的「集羣機器數量」不包括observer角色節點。leader廣播一個事務消息後,當收到半數以上的ack信息時,就認爲集羣中全部節點都收到了消息,而後leader就不須要再等待剩餘節點的ack,直接廣播commit消息,提交事務。半數選舉致使zookeeper一般由2n+1臺server組成。
-
zookeeper的兩階段提交:zookeeper中,客戶端會隨機鏈接到 zookeeper 集羣中的一個節點,若是是讀請求,就直接從當前節點中讀取數據。若是是寫請求,那麼請求會被轉發給 leader 提交事務,而後 leader 會廣播事務,只要有超過半數節點寫入成功,那麼寫請求就會被提交。
- Leader將寫請求轉化爲一個Proposal(提議),將其分發給集羣中的全部Follower節點。
- Leader等待全部的Follower節點的反饋,一旦超過半數Follower進行了正確的反饋,那麼Leader就會再次向全部的Follower節點發送Commit消息,要求各個Follower節點對前面的一個Proposal節點進行提交。
- leader節點將最新數據同步給Observer節點。
- 返回給客戶端執行的結果。
ZAB協議
ZooKeeper 可以保證數據一致性主要依賴於 ZAB 協議的消息廣播,崩潰恢復和數據同步三個過程。
消息廣播
- 一個事務請求進來以後,Leader節點會將寫請求包裝成提議(Proposal)事務,並添加一個全局惟一的 64 位遞增事務 ID,Zxid。
- Leader 節點向集羣中其餘節點廣播Proposal事務,Leader 節點和 Follower 節點是解耦的,通訊都會通過一個 FIFO 的消息隊列,Leader 會爲每個 Follower 節點分配一個單獨的 FIFO 隊列,而後把 Proposal 發送到隊列中。
- Follower 節點收到對應的Proposal以後會把它持久到磁盤上,當徹底寫入以後,發一個ACK給Leader。
- 當Leader節點收到超過半數Follower節點的ACK以後會提交本地機器上的事務,同時開始廣播commit,Follower節點收到 commit 以後,完成各自的事務提交。
消息廣播相似一個分佈式事務的兩階段提交模式。在這種模式下,沒法處理因Leader在發起事務請求後節點宕機帶來的數據不一致問題。所以ZAB協議引入了崩潰恢復機制。
崩潰恢復
當整個集羣在啓動時,或者Leader失聯後,ZAB協議就會進入恢復模式,恢復模式的流程以下:
- 集羣經過過民主舉機制產生新的Leader,紀元號加1,開始新紀元
- 其餘節點重新的Leader同步狀態
- 過半節點完成狀態同步,退出恢復模式,進入消息廣播模式
Leader選舉流程
server工做狀態
狀態 |
說明 |
LOOKING |
競選狀態,當服務器處於該狀態時,它會認爲當前集羣中沒有 Leader,所以須要進入 Leader 選舉狀態。 |
FOLLOWING |
跟隨者狀態。代表當前服務器角色是 Follower。它負責從Leader同步狀態,並參與選舉投票。 |
LEADING |
領導者狀態。代表當前服務器角色是 Leader。 |
OBSERVING |
觀察者狀態,代表當前服務器角色是Observer,它負責從同步leader狀態,不參與投票。 |
選舉原則
- 選舉投票必須在同一輪次中進行,若是Follower服務選舉輪次不一樣,不會採納投票。
- 數據最新的節點優先成爲Leader,數據的新舊使用事務ID斷定,事務ID越大認爲節點數據約接近Leader的數據,天然應該成爲Leader。
- 若是每一個個參與競選節點事務ID同樣,再使用server.id作比較。server.id是節點在集羣中惟一的id,myid文件中配置。
選舉階段
集羣間互傳的消息稱爲投票,投票Vote主要包括二個維度的信息:ID、ZXID  ID 候選者的服務器ID ZXID 候選者的事務ID,從機器DataTree內存中獲取,確保事務已經在機器上被commit過。
選主過程當中主要有三個線程在工做
- 選舉線程:主動調用lookForLeader方法的線程,經過阻塞隊sendqueue及recvqueue與其它兩個線程協做。
- WorkerReceiver線程:選票接收器,不斷獲取其它服務器發來的選舉消息,篩選後會保存到recvqueue隊列中。zk服務器啓動時,開始正常工做,不中止
- WorkerSender線程:選票發送器,會不斷地從sendqueue隊列中獲取待發送的選票,並廣播至集羣。
- WorkerReceiver線程一直在工做,即便當前節點處於LEADING或者FOLLOWING狀態,它起到了一個過濾的做用,當前節點爲LOOKING時,纔會將外部投票信息轉交給選舉線程處理;
- 若是當前節點處於非LOOKING狀態,收到了處於LOOKING狀態的節點投票數據(外部節點重啓或網絡抖動狀況下),說明發起投票的節點數據跟集羣不一致,這時,當前節點須要向集羣廣播出最新的內存Vote(id,zxid),落後的節點收到該Vote後,會及時註冊到leader上,並完成數據同步,跟上集羣節奏,提供正常服務。
全新集羣選舉
- 每一機器都給本身一票。
- 主要服務器ID的值,值越大選舉權重越大。
- 投票數過半,選舉結束。
非全新集羣選舉
- 邏輯時鐘:邏輯時鐘小的選舉結果被忽略
- 數據ID:數據ID大的勝出
- 服務ID:數據ID相同,服務器ID大的勝出,被選舉爲leader。
選舉過程詳細說明
Leader選舉是集羣正常運行的前提,當集羣啓動或Leader失聯後,就會進入Leader選舉流程。
- 全部節點進入LOOKING狀態
- 每一個節點廣播攜帶自身ID和ZXID的選票,投票推舉本身爲Leader
- 節點接收其餘節點發送的選票,把選票信息和本身推舉的選票進行PK(選票中ZXID大者勝出,ZXID相同,則ID大者勝出)
- 若是外部選票獲勝,則保存此選票信息,並把選票廣播出去(同意該選票)
- 循環上述3-4步驟
- 當有選票獲得超過半數節點同意,且該選票的全部者也同意該選票,則選舉成功,該選票全部者成爲Leader
- Leader切換爲LEADING,Follower切換爲FOLLOWING,Observer切換爲OBSERVING狀態,選舉結束,進入數據同步流程。
數據同步流程
數據同步流程,是要以Leader數據爲基礎,讓集羣數據達到一致狀態。
- 新Leader把本地快照加載到內存,並經過日誌應用快照以後的全部事務,確保Leader數據庫是最新的。
- Follower和Observer把自身的ZXID和Leader的ZXID進行比較,肯定每一個節點的同步策略
- 根據同步策略,Leader把數據同步到各節點
- 每一個節點同步結束後,Leader向節點發送NEWLEADER指令
- 同步完成的Follower節點返回ACK
- 當Leader收到過半節點反饋的ACK時,認爲同步完成
Leader向Follower節點發送UPTODATE指令,通知集羣同步完成,開始對外服務。
zk應用舉例
- 命名服務:經過使用命名服務,客戶端應用可以根據指定名字來獲取資源或服務的地址,提供者等信息。經過建立全局惟一的path做爲一個名字。
- 分佈式鎖:獨佔鎖,獲取數據以前要求全部的應用去zk集羣的指定目錄去建立一個臨時非序列化的節點。誰建立成功誰就能得到鎖,操做完成後斷開節點。其它應用若是須要操做這個文件就可去監聽這個目錄是否存在。
- 控制時序:經過建立一個臨時序列化節點來控制時序性。
- 心跳檢測:讓不一樣的進程都在ZK的一個指定節點下建立臨時子節點,不一樣的進程直接能夠根據這個臨時子節點來判斷對應的進程是否存活。大大減小了系統耦合。
- master選舉:每一個客戶端請求建立同一個臨時節點,那麼最終必定只有一個客戶端請求可以建立成功。利用這個特性,就能很容易地在分佈式環境中進行 Master 選舉了。成功建立該節點的客戶端所在的機器就成爲了Master。同時,其餘沒有成功建立該節點的客戶端,都會在該節點上註冊一個子節點變動的 Watcher,用於監控當前 Master 機器是否存活,一旦發現當前的Master掛了,那麼其餘客戶端將會從新進行 Master 選舉。
zookeeper缺點:
1. 非高可用:極端狀況下zk會丟棄一些請求:機房之間鏈接出現故障。
- zookeeper master就只能照顧一個機房,其餘機房運行的業務模塊因爲沒有master都只能停掉,對網絡抖動很是敏感。
- 選舉過程速度很慢且zk選舉期間沒法對外提供服務。
- zk的性能有限:典型的zookeeper的tps大概是一萬多,沒法覆蓋系統內部天天動輒幾十億次的調用。所以每次請求都去zookeeper獲取業務系統master信息是不可能的。所以zookeeper的client必須本身緩存業務系統的master地址。
- zk自己的權限控制很是薄弱.
- 羊羣效應: 全部的客戶端都嘗試對一個臨時節點去加鎖,當一個鎖被佔有的時候,其餘的客戶端都會監聽這個臨時節點。一旦鎖被釋放,Zookeeper反向通知添加監聽的客戶端,而後大量的客戶端都嘗試去對同一個臨時節點建立鎖,最後也只有一個客戶端能得到鎖,可是大量的請求形成了很大的網絡開銷,加劇了網絡的負載,影響Zookeeper的性能.
 * 解決方法:是獲取鎖時建立一個臨時順序節點,順序最小的那個才能獲取到鎖,以後嘗試加鎖的客戶端就監聽本身的上一個順序節點,當上一個順序節點釋放鎖以後,本身嘗試加鎖,其他的客戶端都對上一個臨時順序節點監聽,不會一窩蜂的去嘗試給同一個節點加鎖致使羊羣效應。
- zk進行讀取操做,讀取到的數據多是過時的舊數據,不是最新的數據。若是一個zk集羣有10000臺節點,當進行寫入的時候,若是已經有6K個節點寫入成功,zk就認爲本次寫請求成功。可是這時候若是一個客戶端讀取的恰好是另外4K個節點的數據,那麼讀取到的就是舊的過時數據。
zookeeper腦裂:
假死:因爲心跳超時(網絡緣由致使的)認爲leader死了,但其實leader還存活着。
腦裂
因爲假死會發起新的leader選舉,選舉出一個新的leader,但舊的leader網絡又通了,致使出現了兩個leader ,有的客戶端鏈接到老的leader,而有的客戶端則鏈接到新的leader。
quorum(半數機制)機制解決腦裂
在zookeeper中Quorums有3個做用:
- 集羣中最少的節點數用來選舉leader保證集羣可用。
- 通知客戶端數據已經安全保存前集羣中最少數量的節點數已經保存了該數據。一旦這些節點保存了該數據,客戶端將被通知已經安全保存了,能夠繼續其餘任務。而集羣中剩餘的節點將會最終也保存了該數據。
- 假設某個leader假死,其他的followers選舉出了一個新的leader。這時,舊的leader復活而且仍然認爲本身是leader,這個時候它向其餘followers發出寫請求也是會被拒絕的。由於每當新leader產生時,會生成一個epoch標號(標識當前屬於那個leader的統治時期),這個epoch是遞增的,followers若是確認了新的leader存在,知道其epoch,就會拒絕epoch小於現任leader epoch的全部請求。那有沒有follower不知道新的leader存在呢,有可能,但確定不是大多數,不然新leader沒法產生。Zookeeper的寫也遵循quorum機制,所以,得不到大多數支持的寫是無效的,舊leader即便各類認爲本身是leader,依然沒有什麼做用。