這麼流行的ZooKeeper,原來是這樣設計的

點擊上方☝,輕鬆關注!及時獲取有趣有料的技術文章,回覆“面試

”獲取職精品Java面試資料一份


這麼流行的ZooKeeper,原來是這樣設計的


為什麼會有ZooKeeper


我們知道要寫一個分佈式應用是非常困難的,主要原因就是局部故障。一個消息通過網絡在兩個節點之間傳遞時,網絡如果發生故障,發送方並不知道接收方是否接收到了這個消息。有可能是收到消息以後發生了網絡故障,也有可能是沒有收到消息,又或者可能接收方的進程死了。發送方唯一的確認方法就是再次連接發送消息,並向他進行詢問。這就是局部故障:根本不知道操作是否失敗。因此,大部分分佈式應用需要一個主控、協調控制器來管理物理分佈的子進程。所以大部分應用需要開發私有的協調程序,協調程序的反覆編寫浪費時間,這個時候就需要一個通用的、伸縮性好的協調器。就是因為這樣的場景,ZooKeeper應運而生,ZooKeeper的設計目的,就是為了減輕分佈式應用程序所承擔的協調任務。

ZooKeeper常用的應用場景


01 / 分佈式協調

分佈式協調簡單說就是有人對ZooKeeper中的數據做了監聽,如果修改了ZooKeeper中被監聽的數據,ZooKeeper反過來會告訴給發起監聽的人數據的變更。比如在Kafka的設計中,Kafka的一個節點在ZooKeeper中創建了一個數據,Kafka的策略是誰創建了這個數據誰就是Kafka集群的主節點,其餘的節點都會去監聽這個數據。如果主節點宕機了,這ZooKeeper對應的數據就會發生變更,既而監聽這個數據的其餘節點就會感知到主節點宕機了,然後重新進行選舉。

02 / 元數據管理

很多分佈式的程序需要集中式的管理自己的元數據,這個時候ZooKeeper就是一個很好的選擇。比如Kafka,Storm等分佈式的工具就會把集群裡核心的元數據存放在ZooKeeper中。

03 / 高可用

很多分佈式的項目都是主從式的架構,正常情況下集群裡有一個是主節點,其餘的都是從節點。但是如果只有一個主節點的話,程序就會有單點故障問題,那麼這個時候就需要部署多個主節點實現高可用了,利用ZooKeeper從多個主節點中選出一個作為master,其餘的作為StandBy。比如鼎鼎大名的HDFS就是靠ZooKeeper實現的高可用。

04 / 分佈式鎖


在企業裡面很多的項目需要分佈式鎖,我們可以使用ZooKeeper搞分佈式鎖,不過這兒大家要注意一點,ZooKeeper確實是可以搞分佈式鎖的,但是ZooKeeper不支持太高的併發,也就是說如果是高併發的情況下,分佈式鎖用ZooKeeper可能也不太適合,如果在高併發的情況下建議大家使用Redis去搞分佈式鎖,但是併發不太高的情況下用ZooKeeper搞分佈式鎖是比較方便的,也有很多人確實是這麼使用的。

ZooKeeper核心原理


01 / ZooKeeper集群架構

這麼流行的ZooKeeper,原來是這樣設計的

在ZooKeeper集群當中,集群中的服務器角色分為leader和learner,learner又分為observer和follower,具體功能如下:


0x01、leader(領導者)

為客戶端提供讀和寫的功能,負責投票的發起和決議,集群裡面只有leader才能接受寫的服務。


0x02、follower(跟隨者)

為客戶端提供讀服務,如果是寫的服務則轉發給leader。在選舉過程中進行投票。


0x03、observer(觀察者)

為客戶端提供讀服務,如果是寫服務就轉發個leader。不參與leader的選舉投票。也不參與寫的過半原則機制。在不影響寫的前提下,提高集群讀的性能,此角色於zookeeper3.3系列新增的角色。


0x04、client(客戶端)

連接zookeeper集群的使用者,請求的發起者,獨立於zookeeper集群的角色。


02 / ZooKeeper讀寫機制

在ZooKeeper的選舉中,如果過半的節點都選一個節點為leader的話,那麼這個節點就會是leader節點,也就是因為這個原因,ZooKeeper集群,只要有過半的節點是存活的,那麼這個ZooKeeper就可以正常的提供服務。比如有5個ZooKeeper節點,其中有2個節點宕機了,這個時候還有3個節點存活,存活個數超過半數,此時集群還是正常提供服務,所以ZooKeeper集群本生是沒有高可用問題的。又因為存活的判斷依據是超過半數,所以我們一般搭建ZooKeeper集群的時候,都使用奇數臺,這樣會比較節約機器,比如我們安裝一個6臺的ZooKeeper集群,如果宕機了3臺就會導致集群不可用,因為這個時候存活的節點數沒有超過半數了,所以6臺和5臺的效果是一樣的,我們用5臺比較合適。

對應一個ZooKeeper集群,我們可能有多個客戶端,客戶端能任意連接其中一臺ZooKeeper節點,但是所有的客戶端都只能往leader節點上面去寫數據,所有的客戶端能從所有的節點上面讀取數據。如果有客戶端連接的是follower節點,然後往follower上發送了寫數據的請求,這個時候follower就會把這個寫請求轉發給leader節點處理。leader接受到寫請求就會往其他節點(包括自己)同步數據,如果過半的節點接受到消息後發送回來ack消息,那麼leader節點就對這條消息進行commit,commit後該消息就對用戶可見了。因為需要過半的節點發送ack後,leader才對消息進行commit,這個時候會有一個問題,如果集群越大,那麼等待過半節點發送回來ack消息這個過程就需要越久,也就是說節點越多雖然會增加集群的讀性能,但是會影響到集群的寫性能,所以我們一般建議ZooKeeper的集群規模在3到5個節點左右。為了解決這個問題,後來的ZooKeeper中增加了一個observer 的角色,這個節點不參與投票,只是負責同步數據。比如我們leader寫數據需要過半的節點發送ack響應,這個observer節點是不參與過半的數量統計的。它只是負責從leader同步數據,然後提供給客戶端讀取,所以引入這個角色目的就是為了增加集群讀的性能,然後不影響集群的寫性能。用戶搭建集群的時候可以自己設置該角色。


03 / Zookeeper 特點


0x01、一致性

client客戶端無論連接到集群中的哪個節點,讀到的數據都是一樣的


0x02、實時性

ZooKeeper保證客戶端在一定的時間間隔內獲得結果,包括成功和失敗,但是由於網絡延遲原因,ZooKeeper不能保證兩臺客戶端同時得到剛更新的消息。如果都需要最新的消息需要調用sync()接口。


0x03、原子性

leader在同步數據的時候,同步過程保證事務性,要麼都成功,要麼都失敗。


0x04、順序性

一臺服務器上如果消息a在消息b前發佈,那麼所有的server上的消息a都是在消息b前發佈的。


04 / Zookeeper數據一致性保證


剛剛我們看到了ZooKeeper有多個特點,但是我相信多個特點中,大家最好奇都就是Zookeeper是如何保證數據一致性的。ZooKeeper保證數據一致性用的是ZAB協議。通過這個協議來進行ZooKeeper集群間的數據同步,保證數據的一致性。


0x01、兩階段提交+過半寫機制

這麼流行的ZooKeeper,原來是這樣設計的

ZooKeeper寫數據的機制是客戶端把寫請求發送到leader節點上(如果發送的是follower節點,follower節點會把寫請求轉發到leader節點),leader節點會把數據通過proposal請求發送到所有節點(包括自己),所有到節點接受到數據以後都會寫到自己到本地磁盤上面,寫好了以後會發送一個ack請求給leader,leader只要接受到過半的節點發送ack響應回來,就會發送commit消息給各個節點,各個節點就會把消息放入到內存中(放內存是為了保證高性能),該消息就會用戶可見了。那麼這個時候,如果ZooKeeper要想保證數據一致性,就需要考慮如下兩個情況,情況一:leader執行commit了,還沒來得及給follower發送commit的時候,leader宕機了,這個時候如何保證消息一致性?情況二:客戶端把消息寫到leader了,但是leader還沒發送proposal消息給其他節點,這個時候leader宕機了,leader宕機後恢復的時候此消息又該如何處理?


0x02、ZAB的崩潰恢復機制

針對情況一,當leader宕機以後,ZooKeeper會選舉出來新的leader,新的leader啟動以後要到磁盤上面去檢查是否存在沒有commit的消息,如果存在,就繼續檢查看其他follower有沒有對這條消息進行了commit,如果有過半節點對這條消息進行了ack,但是沒有commit,那麼新對leader要完成commit的操作。


0x03、ZAB恢復中刪除數據機制

針對情況二,客戶端把消息寫到leader了,但是leader還沒發送portal消息給其他節點,這個時候leader宕機了,這個時候對於用戶來說,這條消息是寫失敗的。假設過了一段時間以後leader節點又恢復了,不過這個時候角色就變為了follower了,它在檢查自己磁盤的時候會發現自己有一條消息沒有進行commit,此時就會檢測消息的編號,消息是有編號的,由高32位和低32位組成,高32位是用來體現是否發生過leader切換的,低32位就是展示消息的順序的。這個時候當前的節點就會根據高32位知道目前leader已經切換過了,所以就把當前的消息刪除,然後從新的leader同步數據,這樣保證了數據一致性。

最後


ZooKeeper是我們學習架構的過程當中,非常非常重要的一個知識點,我們今天只是從其中一些角度去分析的ZooKeeper,我們後面會有很多文章從不同角度深入全面的剖析ZooKeeper,幫助大家掌握ZooKeeper,歡迎大家持續關注。


分享到:


相關文章: