面試阿里,騰訊90%會被問到的zookeeper,把這篇文章看完就夠了

java技術分享 發佈 2020-06-13T14:14:53+00:00

因為每當新leader產生時,會生成一個epoch標號,這個epoch是遞增的,followers如果確認了新的leader存在,知道其epoch,就會拒絕epoch小於現任leader epoch的所有請求。

Zookeeper概述

zookeeper高容錯數據一致性協議(CP)的分布式小文件系統,提供類似於文件系統的目錄方式的數據存儲。

  1. 全局數據一致性:每個server保存一份相同的數據副本,client無論連接到哪個server展示的數據都是一致的。
  2. 可靠性:一旦事務成功提交,就會被保留下來。
  3. 有序性:客戶端發起的事務請求,在也會順序的應用在Zookeeper中。
  4. 數據更新原子性:一次數據更新要麼成功要麼失敗,不存在中間狀態。
  5. 實時性:保證客戶端在一個間隔時間範圍內獲取服務的更新消息或伺服器失效信息。

zookeeper單機

數據模型:

每一個節點都是znode(兼具文件和目錄兩種特點),每一個znode都具有原子操作。znode存儲的數據大小有限制(默認1MB),通過絕對路徑引用。 znode分為3個部分:

  • stat:狀態信息,描述znode的版本和權限等信息。
  • data:與該znode關聯的數據。
  • children:該znode下的子節點。

znode節點類型

  1. 臨時節點:該節點的生命周期依賴於創建它的會話,一旦會話結束臨時節點就會被刪除。臨時節點不允許擁有子節點。
  2. 永久節點:只能通過客戶端顯示執行刪除操作。
  3. 臨時序列化節點。
  4. 永久序列化節點。

znode序列化:znode的名字後面追加一個不斷增加的序列號。每一個序列號對父節點來說是唯一的,可以記錄每一個子節點的先後順序。

znode節點屬性

zookeeper的節點屬性包括節點數據,狀態,權限等信息。



znode ACL權限控制

ACL權限控制使用 schema:id:permission來標識。 示例:setAcl /test2 ip:128.0.0.1:crwda

Schema



permission


權限相關命令



zookeeper:提供了分布式發布/訂閱功能,能讓多個訂閱者同時監聽某一個主題對象,通過watche機制來實現。

zookeeper watch機制

一個Watch事件是一個一次性的觸發器,當被設置了Watch的數據發生了改變的時候,則伺服器將這個改變發送給設置了Watch的客戶端,以便通知它們。

  • 父節點的創建,修改,刪除都會觸發Watcher事件。
  • 子節點的創建,刪除會觸發Watcher事件。

監聽器watch特性



監聽器原理

  1. 首先要有一個main()線程,在main線程中創建Zookeeper客戶端,這時就會創建兩個線程,一個負責網絡連接通信(connet),一個負責監聽(listener)。
  2. 通過connect線程將註冊的監聽事件發送給Zookeeper服務端。
  3. 在Zookeeper服務端的註冊監聽器列表中將註冊的監聽事件添加到列表中。
  4. Zookeeper監聽到有數據或路徑變化,就會將這個消息發送

給listener線程。

  1. listener線程內部調用了process()方法來觸發Watcher。

zookeeper會話管理

  1. 客戶端會不時地向所連接的ZkServer發送ping消息,ZkServer接收到ping消息,或者任何其它消息的時候,都會將客戶端的session_id,session_timeout記錄在一個map中。
  2. Leader ZkServer會周期性地向所有的follower發送心跳消息,follower接收到ping消息後,會將記錄session信息的map作為返回消息,返回給leader,同時清空follower本地的map。 Leader使用這些信息重新計算客戶端的超時時間。
  3. 一旦在session timout的時間到,leader即沒有從其它follower上收集到客戶端的session信息,也沒有直接接收到該客戶端的任何請求,那麼該客戶端的session就會被關閉。

zookeeper數據模型

  1. zk維護的數據主要有:客戶端的會話(session)狀態及數據節(dataNode)信息。
  2. zk在內存中構造了個DataTree的數據結構,維護著path到dataNode的映射以及dataNode間的樹狀層級關係。為了提高讀取性能,集群中每個服務節點都是將數據全量存儲在內存中。所以,zk最適於讀多寫少且輕量級數據的應用場景。

3.數據僅存儲在內存是很不安全的,zk採用事務日誌文件及快照文件的方案來落盤數據,保障數據在不丟失的情況下能快速恢復。

Zookeeper集群

集群角色

  1. Leader:集群工作的核心,事務請求的唯一調度和處理者,保證事務處理的順序性。對於有寫操作的請求,需統一轉發給Leader處理。Leader需決定編號執行操作。
  2. Follower:處理客戶端非事務請求,轉發事務請求轉發給Leader,參與Leader選舉。
  3. Observer觀察者:進行非事務請求的獨立處理,對於事務請求,則轉發給Leader伺服器進行處理.不參與投票。

事務

  1. 事務:ZooKeeper中,能改變ZooKeeper伺服器狀態的操作稱為事務操作。一般包括數據節點創建與刪除、數據內容更新和客戶端會話創建與失效等操作。對應每一個事務請求,ZooKeeper 都會為其分配一個全局唯一的事務ID,用 ZXID 表示,通常是一個64位的數字。每一個ZXID對應一次更新操作,從這些ZXID中可以間接地識別出ZooKeeper處理這些事務操作請求的全局順序。
  2. 事務日誌:所有事務操作都是需要記錄到日誌文件中的,可通過 dataLogDir配置文件目錄,文件是以寫入的第一條事務zxid為後綴,方便後續的定位查找。zk會採取「磁碟空間預分配」的策略,來避免磁碟Seek頻率,提升zk伺服器對事務請求的影響能力。默認設置下,每次事務日誌寫入操作都會實時刷入磁碟,也可以設置成非實時(寫到內存文件流,定時批量寫入磁碟),但那樣斷電時會帶來丟失數據的風險。事務日誌記錄的次數達到一定數量後,就會將內存資料庫序列化一次,使其持久化保存到磁碟上,序列化後的文件稱為"快照文件"。有了事務日誌和快照,就可以讓任意節點恢復到任意時間點

  1. 數據快照:數據快照是zk數據存儲中另一個非常核心的運行機制。數據快照用來記錄zk伺服器上某一時刻的全量內存數據內容,並將其寫入到指定的磁碟文件中,可通過dataDir配置文件目錄。可配置參數snapCount,設置兩次快照之間的事務操作個數,zk節點記錄完事務日誌時,會統計判斷是否需要做數據快照(距離上次快照,事務操作次數等於[snapCount/2~snapCount] 中的某個值時,會觸發快照生成操作,隨機值是為了避免所有節點同時生成快照,導致集群影響緩慢)。

過半原則

1. 過半:所謂「過半」是指大於集群機器數量的一半,即大於或等於(n/2+1),此處的「集群機器數量」不包括observer角色節點。leader廣播一個事務消息後,當收到半數以上的ack信息時,就認為集群中所有節點都收到了消息,然後leader就不需要再等待剩餘節點的ack,直接廣播commit消息,提交事務。半數選舉導致zookeeper通常由2n+1台server組成。

  1. zookeeper的兩階段提交:zookeeper中,客戶端會隨機連接到 zookeeper 集群中的一個節點,如果是讀請求,就直接從當前節點中讀取數據。如果是寫請求,那麼請求會被轉發給 leader 提交事務,然後 leader 會廣播事務,只要有超過半數節點寫入成功,那麼寫請求就會被提交。 Leader將寫請求轉化為一個Proposal(提議),將其分發給集群中的所有Follower節點。 Leader等待所有的Follower節點的反饋,一旦超過半數Follower進行了正確的反饋,那麼Leader就會再次向所有的Follower節點發送Commit消息,要求各個Follower節點對前面的一個Proposal節點進行提交。 leader節點將最新數據同步給Observer節點。 返回給客戶端執行的結果。

ZAB協議

ZooKeeper 能夠保證數據一致性主要依賴於 ZAB 協議的消息廣播崩潰恢復數據同步三個過程。

消息廣播

  1. 一個事務請求進來之後,Leader節點會將寫請求包裝成提議(Proposal)事務,並添加一個全局唯一的 64 位遞增事務 ID,Zxid。
  2. Leader 節點向集群中其他節點廣播Proposal事務,Leader 節點和 Follower 節點是解耦的,通信都會經過一個 FIFO 的消息隊列,Leader 會為每一個 Follower 節點分配一個單獨的 FIFO 隊列,然後把 Proposal 發送到隊列中。
  3. Follower 節點收到對應的Proposal之後會把它持久到磁碟上,當完全寫入之後,發一個ACK給Leader。
  4. 當Leader節點收到超過半數Follower節點的ACK之後會提交本地機器上的事務,同時開始廣播commit,Follower節點收到 commit 之後,完成各自的事務提交。

消息廣播類似一個分布式事務的兩階段提交模式。在這種模式下,無法處理因Leader在發起事務請求後節點宕機帶來的數據不一致問題。因此ZAB協議引入了崩潰恢復機制。

崩潰恢復

當整個集群在啟動時,或者Leader失聯後,ZAB協議就會進入恢復模式,恢復模式的流程如下:

  1. 集群通過過民主舉機制產生新的Leader,紀元號加1,開始新紀元
  2. 其他節點從新的Leader同步狀態
  3. 過半節點完成狀態同步,退出恢復模式,進入消息廣播模式

Leader選舉流程

server工作狀態



選舉原則

  1. 選舉投票必須在同一輪次中進行,如果Follower服務選舉輪次不同,不會採納投票。
  2. 數據最新的節點優先成為Leader,數據的新舊使用事務ID判定,事務ID越大認為節點數據約接近Leader的數據,自然應該成為Leader。
  3. 如果每個個參與競選節點事務ID一樣,再使用server.id做比較。server.id是節點在集群中唯一的id,myid文件中配置。

選舉階段

集群間互傳的消息稱為投票,投票Vote主要包括二個維度的信息:ID、ZXID  ID 候選者的伺服器ID ZXID 候選者的事務ID,從機器DataTree內存中獲取,確保事務已經在機器上被commit過。

選主過程中主要有三個線程在工作

  1. 選舉線程:主動調用lookForLeader方法的線程,通過阻塞隊sendqueue及recvqueue與其它兩個線程協作。
  2. WorkerReceiver線程:選票接收器,不斷獲取其它伺服器發來的選舉消息,篩選後會保存到recvqueue隊列中。zk伺服器啟動時,開始正常工作,不停止
  3. WorkerSender線程:選票發送器,會不斷地從sendqueue隊列中獲取待發送的選票,並廣播至集群。
  4. WorkerReceiver線程一直在工作,即使當前節點處於LEADING或者FOLLOWING狀態,它起到了一個過濾的作用,當前節點為LOOKING時,才會將外部投票信息轉交給選舉線程處理;
  5. 如果當前節點處於非LOOKING狀態,收到了處於LOOKING狀態的節點投票數據(外部節點重啟或網絡抖動情況下),說明發起投票的節點數據跟集群不一致,這時,當前節點需要向集群廣播出最新的內存Vote(id,zxid),落後的節點收到該Vote後,會及時註冊到leader上,並完成數據同步,跟上集群節奏,提供正常服務。

全新集群選舉

  1. 每一機器都給自己一票。
  2. 主要伺服器ID的值,值越大選舉權重越大。
  3. 投票數過半,選舉結束。

非全新集群選舉

  1. 邏輯時鐘:邏輯時鐘小的選舉結果被忽略
  2. 數據ID:數據ID大的勝出
  3. 服務ID:數據ID相同,伺服器ID大的勝出,被選舉為leader。

選舉過程詳細說明

Leader選舉是集群正常運行的前提,當集群啟動或Leader失聯後,就會進入Leader選舉流程。

  1. 所有節點進入LOOKING狀態
  2. 每個節點廣播攜帶自身ID和ZXID的選票,投票推舉自己為Leader
  3. 節點接收其他節點發送的選票,把選票信息和自己推舉的選票進行PK(選票中ZXID大者勝出,ZXID相同,則ID大者勝出)
  4. 如果外部選票獲勝,則保存此選票信息,並把選票廣播出去(贊成該選票)
  5. 循環上述3-4步驟
  6. 當有選票得到超過半數節點讚成,且該選票的所有者也贊成該選票,則選舉成功,該選票所有者成為Leader
  7. Leader切換為LEADING,Follower切換為FOLLOWING,Observer切換為OBSERVING狀態,選舉結束,進入數據同步流程。

數據同步流程

數據同步流程,是要以Leader數據為基礎,讓集群數據達到一致狀態。

  1. 新Leader把本地快照加載到內存,並通過日誌應用快照之後的所有事務,確保Leader資料庫是最新的。
  2. Follower和Observer把自身的ZXID和Leader的ZXID進行比較,確定每個節點的同步策略
  3. 根據同步策略,Leader把數據同步到各節點
  4. 每個節點同步結束後,Leader向節點發送NEWLEADER指令
  5. 同步完成的Follower節點返回ACK
  6. 當Leader收到過半節點反饋的ACK時,認為同步完成

Leader向Follower節點發送UPTODATE指令,通知集群同步完成,開始對外服務。

zk應用舉例

  1. 命名服務:通過使用命名服務,客戶端應用能夠根據指定名字來獲取資源或服務的地址,提供者等信息。通過創建全局唯一的path作為一個名字。
  2. 分布式鎖:獨占鎖,獲取數據之前要求所有的應用去zk集群的指定目錄去創建一個臨時非序列化的節點。誰創建成功誰就能獲得鎖,操作完成後斷開節點。其它應用如果需要操作這個文件就可去監聽這個目錄是否存在。
  3. 控制時序:通過創建一個臨時序列化節點來控制時序性。
  4. 心跳檢測:讓不同的進程都在ZK的一個指定節點下創建臨時子節點,不同的進程直接可以根據這個臨時子節點來判斷對應的進程是否存活。大大減少了系統耦合。
  5. master選舉:每個客戶端請求創建同一個臨時節點,那麼最終一定只有一個客戶端請求能夠創建成功。利用這個特性,就能很容易地在分布式環境中進行 Master 選舉了。成功創建該節點的客戶端所在的機器就成為了Master。同時,其他沒有成功創建該節點的客戶端,都會在該節點上註冊一個子節點變更的 Watcher,用於監控當前 Master 機器是否存活,一旦發現當前的Master掛了,那麼其他客戶端將會重新進行 Master 選舉。

zookeeper缺點:

1. 非高可用:極端情況下zk會丟棄一些請求:機房之間連接出現故障。

  1. zookeeper master就只能照顧一個機房,其他機房運行的業務模塊由於沒有master都只能停掉,對網絡抖動非常敏感。
  2. 選舉過程速度很慢且zk選舉期間無法對外提供服務。
  3. zk的性能有限:典型的zookeeper的tps大概是一萬多,無法覆蓋系統內部每天動輒幾十億次的調用。因此每次請求都去zookeeper獲取業務系統master信息是不可能的。因此zookeeper的client必須自己緩存業務系統的master地址。
  4. zk本身的權限控制非常薄弱.
  5. 羊群效應: 所有的客戶端都嘗試對一個臨時節點去加鎖,當一個鎖被占有的時候,其他的客戶端都會監聽這個臨時節點。一旦鎖被釋放,Zookeeper反向通知添加監聽的客戶端,然後大量的客戶端都嘗試去對同一個臨時節點創建鎖,最後也只有一個客戶端能獲得鎖,但是大量的請求造成了很大的網絡開銷,加重了網絡的負載,影響Zookeeper的性能.

 * 解決方法:是獲取鎖時創建一個臨時順序節點,順序最小的那個才能獲取到鎖,之後嘗試加鎖的客戶端就監聽自己的上一個順序節點,當上一個順序節點釋放鎖之後,自己嘗試加鎖,其餘的客戶端都對上一個臨時順序節點監聽,不會一窩蜂的去嘗試給同一個節點加鎖導致羊群效應。

  1. zk進行讀取操作,讀取到的數據可能是過期的舊數據,不是最新的數據。如果一個zk集群有10000台節點,當進行寫入的時候,如果已經有6K個節點寫入成功,zk就認為本次寫請求成功。但是這時候如果一個客戶端讀取的剛好是另外4K個節點的數據,那麼讀取到的就是舊的過期數據。

zookeeper腦裂:

假死:由於心跳超時(網絡原因導致的)認為leader死了,但其實leader還存活著。

腦裂

由於假死會發起新的leader選舉,選舉出一個新的leader,但舊的leader網絡又通了,導致出現了兩個leader ,有的客戶端連接到老的leader,而有的客戶端則連接到新的leader。

quorum(半數機制)機制解決腦裂

在zookeeper中Quorums有3個作用:

  1. 集群中最少的節點數用來選舉leader保證集群可用。
  2. 通知客戶端數據已經安全保存前集群中最少數量的節點數已經保存了該數據。一旦這些節點保存了該數據,客戶端將被通知已經安全保存了,可以繼續其他任務。而集群中剩餘的節點將會最終也保存了該數據。
  3. 假設某個leader假死,其餘的followers選舉出了一個新的leader。這時,舊的leader復活並且仍然認為自己是leader,這個時候它向其他followers發出寫請求也是會被拒絕的。因為每當新leader產生時,會生成一個epoch標號(標識當前屬於那個leader的統治時期),這個epoch是遞增的,followers如果確認了新的leader存在,知道其epoch,就會拒絕epoch小於現任leader epoch的所有請求。那有沒有follower不知道新的leader存在呢,有可能,但肯定不是大多數,否則新leader無法產生。Zookeeper的寫也遵循quorum機制,因此,得不到大多數支持的寫是無效的,舊leader即使各種認為自己是leader,依然沒有什麼作用。


作者:前程有光
連結:https://juejin.im/post/5edcf88c518825430f54cec6
來源:掘金
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

關鍵字: