《我想進大廠》之Zookeeper奪命連環9問
談談你對Zookeeper的理解?
Zookeeper是一個開源的分散式協調服務,由雅虎公司建立,由於最初雅虎公司的內部研究小組的專案大多以動物的名字命名,所以後來就以Zookeeper(動物管理員)來命名了,而就是由Zookeeper來負責這些分散式元件環境的協調工作。
他的目標是可以提供高效能、高可用和順序訪問控制的能力,同時也是為了解決分散式環境下資料一致性的問題。
叢集
首先,Zookeeper叢集中有幾個關鍵的概念,Leader、Follower和Observer,Zookeeper中通常只有Leader節點可以寫入,Follower和Observer都只是負責讀,但是Follower會參與節點的選舉和過半寫成功,Observer則不會,他只是單純的提供讀取資料的功能。
通常這樣設定的話,是為了避免太多的從節點參與過半寫的過程,導致影響效能,這樣Zookeeper只要使用一個幾臺機器的小叢集就可以實現高效能了,如果要橫向擴充套件的話,只需要增加Observer節點即可。
Zookeeper建議叢集節點個數為奇數,只要超過一半的機器能夠正常提供服務,那麼整個叢集都是可用的狀態。
資料節點Znode
Zookeeper中資料儲存於記憶體之中,這個資料節點就叫做Znode,他是一個樹形結構,比如/a/b/c類似。
而Znode又分為持久節點、臨時節點、順序節點三大類。
持久節點是指只要被建立,除非主動移除,否則都應該一直儲存在Zookeeper中。
臨時節點不同的是,他的生命週期和客戶端Session會話一樣,會話失效,那麼臨時節點就會被移除。
還有就是臨時順序節點和持久順序節點,除了基本的特性之外,子節點的名稱還具有有序性。
會話Session
會話自然就是指Zookeeper客戶端和服務端之間的通訊,他們使用TCP長連線的方式保持通訊,通常,肯定會有心跳檢測的機制,同時他可以接受來自伺服器的Watch事件通知。
事件監聽器Wather
使用者可以在指定的節點上註冊Wather,這樣在事件觸發的時候,客戶端就會收到來自服務端的通知。
許可權控制ACL
Zookeeper使用ACL來進行許可權的控制,包含以下5種:
-
CREATE,建立子節點許可權
-
DELETE,刪除子節點許可權
-
READ,獲取節點資料和子節點列表許可權
-
WRITE,更新節點許可權
-
ADMIN,設定節點ACL許可權
所以,Zookeeper通過叢集的方式來做到高可用,通過記憶體資料節點Znode來達到高效能,但是儲存的資料量不能太大,通常適用於讀多寫少的場景。
Zookeeper有哪些應用場景?
-
命名服務Name Service,依賴Zookeeper可以生成全域性唯一的節點ID,來對分散式系統中的資源進行管理。
-
分散式協調,這是Zookeeper的核心使用了。利用Wather的監聽機制,一個系統的某個節點狀態發生改變,另外系統可以得到通知。
-
叢集管理,分散式叢集中狀態的監控和管理,使用Zookeeper來儲存。
-
Master選舉,利用Zookeeper節點的全域性唯一性,同時只有一個客戶端能夠建立成功的特點,可以作為Master選舉使用,建立成功的則作為Master。
-
分散式鎖,利用Zookeeper建立臨時順序節點的特性。
說說Wather監聽機制和它的原理?
Zookeeper可以提供分散式資料的釋出/訂閱功能,依賴的就是Wather監聽機制。
客戶端可以向服務端註冊Wather監聽,服務端的指定事件觸發之後,就會向客戶端傳送一個事件通知。
他有幾個特性:
-
一次性:一旦一個Wather觸發之後,Zookeeper就會將它從儲存中移除
-
客戶端序列:客戶端的Wather回撥處理是串行同步的過程,不要因為一個Wather的邏輯阻塞整個客戶端
-
輕量:Wather通知的單位是WathedEvent,只包含通知狀態、事件型別和節點路徑,不包含具體的事件內容,具體的時間內容需要客戶端主動去重新獲取資料
主要流程如下:
-
客戶端向服務端註冊Wather監聽
-
儲存Wather物件到客戶端本地的WatherManager中
-
服務端Wather事件觸發後,客戶端收到服務端通知,從WatherManager中取出對應Wather物件執行回撥邏輯
Zookeeper是如何保證資料一致性的?
Zookeeper通過ZAB原子廣播協議來實現資料的最終順序一致性,他是一個類似2PC兩階段提交的過程。
由於Zookeeper只有Leader節點可以寫入資料,如果是其他節點收到寫入資料的請求,則會將之轉發給Leader節點。
主要流程如下:
-
Leader收到請求之後,將它轉換為一個proposal提議,並且為每個提議分配一個全域性唯一遞增的事務ID:zxid,然後把提議放入到一個FIFO的佇列中,按照FIFO的策略傳送給所有的Follower
-
Follower收到提議之後,以事務日誌的形式寫入到本地磁碟中,寫入成功後返回ACK給Leader
-
Leader在收到超過半數的Follower的ACK之後,即可認為資料寫入成功,就會發送commit命令給Follower告訴他們可以提交proposal了
ZAB包含兩種基本模式,崩潰恢復和訊息廣播。
整個叢集服務在啟動、網路中斷或者重啟等異常情況的時候,首先會進入到崩潰恢復狀態,此時會通過選舉產生Leader節點,當叢集過半的節點都和Leader狀態同步之後,ZAB就會退出恢復模式。之後,就會進入訊息廣播的模式。
那麼,Zookeeper如何進行Leader選舉的?
Leader的選舉可以分為兩個方面,同時選舉主要包含事務zxid和myid,節點主要包含LEADING\FOLLOWING\LOOKING3個狀態。
-
服務啟動期間的選舉
-
服務執行期間的選舉
服務啟動期間的選舉
-
首先,每個節點都會對自己進行投票,然後把投票資訊廣播給叢集中的其他節點
-
節點接收到其他節點的投票資訊,然後和自己的投票進行比較,首先zxid較大的優先,如果zxid相同那麼則會去選擇myid更大者,此時大家都是LOOKING的狀態
-
投票完成之後,開始統計投票資訊,如果叢集中過半的機器都選擇了某個節點機器作為leader,那麼選舉結束
-
最後,更新各個節點的狀態,leader改為LEADING狀態,follower改為FOLLOWING狀態
服務執行期間的選舉
如果開始選舉出來的leader節點宕機了,那麼執行期間就會重新進行leader的選舉。
-
leader宕機之後,非observer節點都會把自己的狀態修改為LOOKING狀態,然後重新進入選舉流程
-
生成投票資訊(myid,zxid),同樣,第一輪的投票大家都會把票投給自己,然後把投票資訊廣播出去
-
接下來的流程和上面的選舉是一樣的,都會優先以zxid,然後選擇myid,最後統計投票資訊,修改節點狀態,選舉結束
那選舉之後又是怎樣進行資料同步的?
那實際上Zookeeper在選舉之後,Follower和Observer(統稱為Learner)就會去向Leader註冊,然後就會開始資料同步的過程。
資料同步包含3個主要值和4種形式。
PeerLastZxid:Learner伺服器最後處理的ZXID
minCommittedLog:Leader提議快取佇列中最小ZXID
maxCommittedLog:Leader提議快取佇列中最大ZXID
直接差異化同步 DIFF同步
如果PeerLastZxid在minCommittedLog和maxCommittedLog之間,那麼則說明Learner伺服器還沒有完全同步最新的資料。
-
首先Leader向Learner傳送DIFF指令,代表開始差異化同步,然後把差異資料(從PeerLastZxid到maxCommittedLog之間的資料)提議proposal傳送給Learner
-
傳送完成之後傳送一個NEWLEADER命令給Learner,同時Learner返回ACK表示已經完成了同步
-
接著等待叢集中過半的Learner響應了ACK之後,就傳送一個UPTODATE命令,Learner返回ACK,同步流程結束
先回滾再差異化同步 TRUNC+DIFF同步
這個設定針對的是一個異常的場景。
如果Leader剛生成一個proposal,還沒有來得及傳送出去,此時Leader宕機,重新選舉之後作為Follower,但是新的Leader沒有這個proposal資料。
舉個栗子:
假設現在的Leader是A,minCommittedLog=1,maxCommittedLog=3,剛好生成的一個proposal的ZXID=4,然後掛了。
重新選舉出來的Leader是B,B之後又處理了2個提議,然後minCommittedLog=1,maxCommittedLog=5。
這時候A的PeerLastZxid=4,在(1,5)之間。
那麼這一條只存在於A的提議怎麼處理?
A要進行事務回滾,相當於拋棄這條資料,並且回滾到最接近於PeerLastZxid的事務,對於A來說,也就是PeerLastZxid=3。
流程和DIFF一致,只是會先發送一個TRUNC命令,然後再執行差異化DIFF同步。
僅回滾同步 TRUNC同步
針對PeerLastZxid大於maxCommittedLog的場景,流程和上述一致,事務將會被回滾到maxCommittedLog的記錄。
這個其實就更簡單了,也就是你可以認為TRUNC+DIFF中的例子,新的Leader B沒有處理提議,所以B中minCommittedLog=1,maxCommittedLog=3。
所以A的PeerLastZxid=4就會大於maxCommittedLog了,也就是A只需要回滾就行了,不需要執行差異化同步DIFF了。
全量同步 SNAP同步
適用於兩個場景:
-
PeerLastZxid小於minCommittedLog
-
Leader伺服器上沒有提議快取佇列,並且PeerLastZxid不等於Leader的最大ZXID
這兩種場景下,Leader將會發送SNAP命令,把全量的資料都發送給Learner進行同步。
有可能會出現資料不一致的問題嗎?
還是會存在的,我們可以分成3個場景來描述這個問題。
查詢不一致
因為Zookeeper是過半成功即代表成功,假設我們有5個節點,如果123節點寫入成功,如果這時候請求訪問到4或者5節點,那麼有可能讀取不到資料,因為可能資料還沒有同步到4、5節點中,也可以認為這算是資料不一致的問題。
解決方案可以在讀取前使用sync命令。
leader未傳送proposal宕機
這也就是資料同步說過的問題。
leader剛生成一個proposal,還沒有來得及傳送出去,此時leader宕機,重新選舉之後作為follower,但是新的leader沒有這個proposal。
這種場景下的日誌將會被丟棄。
leader傳送proposal成功,傳送commit前宕機
如果傳送proposal成功了,但是在將要傳送commit命令前宕機了,如果重新進行選舉,還是會選擇zxid最大的節點作為leader,因此,這個日誌並不會被丟棄,會在選舉出leader之後重新同步到其他節點當中。
如果作為註冊中心,Zookeeper 和Eureka、Consul、Nacos有什麼區別?
Nacos | Eureka | Consul | Zookeeper | |
---|---|---|---|---|
一致性協議 | CP+AP | AP | CP | CP |
健康檢查 | TCP/HTTP/MYSQL/Client Beat | Client Beat | TCP/HTTP/gRPC/Cmd | Keep Alive |
負載均衡策略 | 權重/ metadata/Selector | Ribbon | Fabio | — |
雪崩保護 | 有 | 有 | 無 | 無 |
自動登出例項 | 支援 | 支援 | 不支援 | 支援 |
訪問協議 | HTTP/DNS | HTTP | HTTP/DNS | TCP |
監聽支援 | 支援 | 支援 | 支援 | 支援 |
多資料中心 | 支援 | 支援 | 支援 | 不支援 |
跨註冊中心同步 | 支援 | 不支援 | 支援 | 不支援 |
SpringCloud整合 | 支援 | 支援 | 支援 | 不支援 |
Dubbo整合 | 支援 | 不支援 | 不支援 | 支援 |
K8S整合 | 支援 | 不支援 | 支援 | 不支援 |
最後,你對於CAP理論怎麼理解?
CAP是一個分散式系統設計的定理,他包含3個部分,並且最多隻能同時滿足其中兩個。
-
Consistency一致性,因為在一個分散式系統中,資料肯定需要在不同的節點之間進行同步,就比如Zookeeper,所以一致性就是指的是資料在不同的節點之間怎樣保證一致性,對於純理論的C而言,預設的規則是忽略掉延遲的,因為如果考慮延遲的話,因為資料同步的過程無論如何都會有延遲的,延遲的過程必然會帶來資料的不一致。
-
Availability可用性,這個指的是對於每一個請求,節點總是可以在合理的時間返回合理的響應,比如Zookeeper在進行資料同步時,無法對外提供讀寫服務,不滿足可用性要求。這裡常有的一個例子是說Zookeeper選舉期間無法提供服務不滿足A,這個說法並不準確,因為CAP關注的是資料的讀寫,選舉可以認為不在考慮範圍之內。所以,可以認為對於資料的讀寫,無論響應超時還是返回異常都可以認為是不滿足A。
-
Partition-tolerance分割槽容錯性,因為在一個分散式系統當中,很有可能由於部分節點的網路問題導致整個叢集之間的網路不連通,所以就產生了網路分割槽,整個叢集的環境被分隔成不同的的子網,所以,一般說網路不可能100%的不產生問題,所以P一定會存在。
為什麼只能同時滿足CAP中的兩個呢?
以A\B兩個節點同步資料舉例,由於P的存在,那麼可能AB同步資料出現問題。
如果選擇AP,由於A的資料未能正確同步到B,所以AB資料不一致,無法滿足C。
如果選擇CP,那麼B就不能提供服務,就無法滿足A。
巨人的肩膀:
https://my.oschina.net/yunqi/blog/3040280
《從Paxos到Zookeeper分散式一致性原理與實踐》
&n