前言
一線大廠ZooKeeper的十二連問,你頂得了嘛?
本文已經收錄到github
github.com/whx123/Java…
1. 面試官:作業中使用過Zookeeper嘛?你知道它是什么,有什么用途呢?
小菜雞的我:
- 有使用過的,使用ZooKeeper作為dubbo的注冊中心,使用ZooKeeper實作分布式鎖,
- ZooKeeper,它是一個開放原始碼的分布式協調服務,它是一個集群的管理者,它將簡單易用的介面提供給用戶,
- 可以基于Zookeeper 實作諸如資料發布/訂閱、負載均衡、命名服務、分布式協調/通知、集群管理、Master 選舉、分布式鎖和分布式佇列等功能,
- Zookeeper的用途:命名服務、配置管理、集群管理、分布式鎖、佇列管理
用途跟功能不是一個意思咩?給我一個眼神,讓我自己體會
2. 面試官:說下什么是命名服務,什么是配置管理,又什么是集群管理吧
小菜雞的我(幸好我刷過面試題),無所畏懼
- 命名服務就是:
命名服務是指通過指定的名字來獲取資源或者服務地址,Zookeeper可以創建一個全域唯一的路徑,這個路徑就可以作為一個名字,被命名的物體可以是集群中的機器,服務的地址,或者是遠程的物件等,一些分布式服務框架(RPC、RMI)中的服務地址串列,通過使用命名服務,客戶端應用能夠根據特定的名字來獲取資源的物體、服務地址和提供者資訊等,
- 配置管理: :
實際專案開發中,我們經常使用.properties或者xml需要配置很多資訊,如資料庫連接資訊、fps地址埠等等,因為你的程式一般是分布式部署在不同的機器上(如果你是單機應用當我沒說),如果把程式的這些配置資訊保存在zk的znode節點下,當你要修改配置,即znode會發生變化時,可以通過改變zk中某個目錄節點的內容,利用watcher通知給各個客戶端,從而更改配置,
- 集群管理
集群管理包括集群監控和集群控制,其實就是監控集群機器狀態,剔除機器和加入機器,zookeeper可以方便集群機器的管理,它可以實時監控znode節點的變化,一旦發現有機器掛了,該機器就會與zk斷開連接,對用的臨時目錄節點會被洗掉,其他所有機器都收到通知,新機器加入也是類似醬紫,所有機器收到通知:有新兄弟目錄加入啦,
3. 面試官:你提到了znode節點,那你知道znode有幾種型別呢?zookeeper的資料模型是怎樣的呢?
小菜雞的我(我先想想):
zookeeper的資料模型
ZooKeeper的視圖資料結構,很像Unix檔案系統,也是樹狀的,這樣可以確定每個路徑都是唯一的,zookeeper的節點統一叫做znode,它是可以通過路徑來標識,結構圖如下:
znode的4種型別
根據節點的生命周期,znode可以分為4種型別,分別是持久節點(PERSISTENT)、持久順序節點(PERSISTENT_SEQUENTIAL)、臨時節點(EPHEMERAL)、臨時順序節點(EPHEMERAL_SEQUENTIAL)
-
持久節點(PERSISTENT)
這類節點被創建后,就會一直存在于Zk服務器上,直到手動洗掉,
-
持久順序節點(PERSISTENT_SEQUENTIAL)
它的基本特性同持久節點,不同在于增加了順序性,父節點會維護一個自增整性數字,用于子節點的創建的先后順序,
-
臨時節點(EPHEMERAL)
臨時節點的生命周期與客戶端的會話系結,一旦客戶端會話失效(非TCP連接斷開),那么這個節點就會被自動清理掉,zk規定臨時節點只能作為葉子節點,
-
臨時順序節點(EPHEMERAL_SEQUENTIAL)
基本特性同臨時節點,添加了順序的特性,
4、面試官:你知道znode節點里面存盤的是什么嗎?每個節點的資料最大不能超過多少呢?
小菜雞的我:
znode節點里面存盤的是什么?
Znode資料節點的代碼如下
public class DataNode implements Record {
byte data[];
Long acl;
public StatPersisted stat;
private Set<String> children = null;
}
復制代碼
哈哈,Znode包含了存盤資料、訪問權限、子節點參考、節點狀態資訊,如圖:
- data: znode存盤的業務資料資訊
- ACL: 記錄客戶端對znode節點的訪問權限,如IP等,
- child: 當前節點的子節點參考
- stat: 包含Znode節點的狀態資訊,比如事務id、版本號、時間戳等等,
每個節點的資料最大不能超過多少呢
為了保證高吞吐和低延遲,以及資料的一致性,znode只適合存盤非常小的資料,不能超過1M,最好都小于1K,
5、面試官:你知道znode節點上的監聽機制嘛?講下Zookeeper watch機制吧,
小菜雞的我:
- Watcher機制
- 監聽機制的作業原理
- Watcher特性總結
Watcher監聽機制
Zookeeper 允許客戶端向服務端的某個Znode注冊一個Watcher監聽,當服務端的一些指定事件觸發了這個Watcher,服務端會向指定客戶端發送一個事件通知來實作分布式的通知功能,然后客戶端根據 Watcher通知狀態和事件型別做出業務上的改變,
可以把Watcher理解成客戶端注冊在某個Znode上的觸發器,當這個Znode節點發生變化時(增刪改查),就會觸發Znode對應的注冊事件,注冊的客戶端就會收到異步通知,然后做出業務的改變,
Watcher監聽機制的作業原理
- ZooKeeper的Watcher機制主要包括客戶端執行緒、客戶端 WatcherManager、Zookeeper服務器三部分,
- 客戶端向ZooKeeper服務器注冊Watcher的同時,會將Watcher物件存盤在客戶端的WatchManager中,
- 當zookeeper服務器觸發watcher事件后,會向客戶端發送通知, 客戶端執行緒從 WatcherManager 中取出對應的 Watcher 物件來執行回呼邏輯,
Watcher特性總結
- 一次性:一個Watch事件是一個一次性的觸發器,一次性觸發,客戶端只會收到一次這樣的資訊,
- 異步的: Zookeeper服務器發送watcher的通知事件到客戶端是異步的,不能期望能夠監控到節點每次的變化,Zookeeper只能保證最終的一致性,而無法保證強一致性,
- 輕量級: Watcher 通知非常簡單,它只是通知發生了事件,而不會傳遞事件物件內容,
- 客戶端串行: 執行客戶端 Watcher 回呼的程序是一個串行同步的程序,
- 注冊 watcher用getData、exists、getChildren方法
- 觸發 watcher用create、delete、setData方法
6、面試官:你對Zookeeper的資料結構都有一定了解,那你講下Zookeeper的特性吧
小菜雞的我:(我背過書,啊哈哈)
- 順序一致性:從同一客戶端發起的事務請求,最終將會嚴格地按照順序被應用到 ZooKeeper 中去,
- 原子性:所有事務請求的處理結果在整個集群中所有機器上的應用情況是一致的,也就是說,要么整個集群中所有的機器都成功應用了某一個事務,要么都沒有應用,
- 單一視圖:無論客戶端連到哪一個 ZooKeeper 服務器上,其看到的服務端資料模型都是一致的,
- 可靠性: 一旦服務端成功地應用了一個事務,并完成對客戶端的回應,那么該事務所引起的服務端狀態變更將會被一直保留下來,
- 實時性(最終一致性): Zookeeper 僅僅能保證在一定的時間段內,客戶端最終一定能夠從服務端上讀取到最新的資料狀態,
7、面試官:你剛提到順序一致性,那zookeeper是如何保證事務的順序一致性的呢?
小菜雞的我:(完蛋了這題不會)
這道題可以看下這篇文章(本題答案來自該文章):聊一聊ZooKeeper的順序一致性
需要了解事務ID,即zxid,ZooKeeper的在選舉時通過比較各結點的zxid和機器ID選出新的主結點的,zxid由Leader節點生成,有新寫入事件時,Leader生成新zxid并隨提案一起廣播,每個結點本地都保存了當前最近一次事務的zxid,zxid是遞增的,所以誰的zxid越大,就表示誰的資料是最新的,
ZXID的生成規則如下:
ZXID有兩部分組成:
- 任期:完成本次選舉后,直到下次選舉前,由同一Leader負責協調寫入;
- 事務計數器:單調遞增,每生效一次寫入,計數器加一,
ZXID的低32位是計數器,所以同一任期內,ZXID是連續的,每個結點又都保存著自身最新生效的ZXID,通過對比新提案的ZXID與自身最新ZXID是否相差“1”,來保證事務嚴格按照順序生效的,
8、面試官:你提到了Leader,你知道Zookeeper的服務器有幾種角色嘛?Zookeeper下Server作業狀態又有幾種呢?
小菜雞的我:
Zookeeper 服務器角色
Zookeeper集群中,有Leader、Follower和Observer三種角色
Leader
Leader服務器是整個ZooKeeper集群作業機制中的核心,其主要作業:
- 事務請求的唯一調度和處理者,保證集群事務處理的順序性
- 集群內部各服務的調度者
Follower
Follower服務器是ZooKeeper集群狀態的跟隨者,其主要作業:
- 處理客戶端非事務請求,轉發事務請求給Leader服務器
- 參與事務請求Proposal的投票
- 參與Leader選舉投票
Observer
Observer是3.3.0 版本開始引入的一個服務器角色,它充當一個觀察者角色——觀察ZooKeeper集群的最新狀態變化并將這些狀態變更同步過來,其作業:
- 處理客戶端的非事務請求,轉發事務請求給 Leader 服務器
- 不參與任何形式的投票
Zookeeper下Server作業狀態
服務器具有四種狀態,分別是 LOOKING、FOLLOWING、LEADING、OBSERVING,
- 1.LOOKING:尋找Leader狀態,當服務器處于該狀態時,它會認為當前集群中沒有 Leader,因此需要進入 Leader 選舉狀態,
- 2.FOLLOWING:跟隨者狀態,表明當前服務器角色是Follower,
- 3.LEADING:領導者狀態,表明當前服務器角色是Leader,
- 4.OBSERVING:觀察者狀態,表明當前服務器角色是Observer,
9、面試官:你說到服務器角色是基于ZooKeeper集群的,那你畫一下ZooKeeper集群部署圖吧?ZooKeeper是如何保證主從節點資料一致性的呢?
小菜雞的我:
ZooKeeper集群部署圖
- 如果是寫入資料,先寫入主服務器(主節點),再通知從服務器,
- 如果是讀取資料,既讀主服務器的,也可以讀從服務器的,
ZooKeeper如何保證主從節點資料一致性
我們知道集群是主從部署結構,要保證主從節點一致性問題,無非就是兩個主要問題:
- 主服務器掛了,或者重啟了
- 主從服務器之間同步資料~
Zookeeper是采用ZAB協議(Zookeeper Atomic Broadcast,Zookeeper原子廣播協議)來保證主從節點資料一致性的,ZAB協議支持崩潰恢復和訊息廣播兩種模式,很好解決了這兩個問題:
- 崩潰恢復:Leader掛了,進入該模式,選一個新的leader出來
- 訊息廣播: 把更新的資料,從Leader同步到所有Follower
Leader服務器掛了,所有集群中的服務器進入LOOKING狀態,首先,它們會選舉產生新的Leader服務器;接著,新的Leader服務器與集群中Follower服務進行資料同步,當集群中超過半數機器與該 Leader服務器完成資料同步之后,退出恢復模式進入訊息廣播模式,Leader 服務器開始接收客戶端的事務請求生成事務Proposal進行事務請求處理,
10、面試官:Leader掛了,進入崩潰恢復,是如何選舉Leader的呢?你講一下ZooKeeper選舉機制吧
小菜雞的我:
服務器啟動或者服務器運行期間(Leader掛了),都會進入Leader選舉,我們來看一下~假設現在ZooKeeper集群有五臺服務器,它們myid分別是服務器1、2、3、4、5,如圖:
服務器啟動的Leader選舉
zookeeper集群初始化階段,服務器(myid=1-5)依次啟動,開始zookeeper選舉Leader~
- 服務器1(myid=1)啟動,當前只有一臺服務器,無法完成Leader選舉
- 服務器2(myid=2)啟動,此時兩臺服務器能夠相互通訊,開始進入Leader選舉階段
- 每個服務器發出一個投票
服務器1 和 服務器2都將自己作為Leader服務器進行投票,投票的基本元素包括:服務器的myid和ZXID,我們以(myid,ZXID)形式表示,初始階段,服務器1和服務器2都會投給自己,即服務器1的投票為(1,0),服務器2的投票為(2,0),然后各自將這個投票發給集群中的其他所有機器,
- 接受來自各個服務器的投票
每個服務器都會接受來自其他服務器的投票,同時,服務器會校驗投票的有效性,是否本輪投票、是否來自LOOKING狀態的服務器,
- 處理投票
收到其他服務器的投票,會將被人的投票跟自己的投票PK,PK規則如下:
- 優先檢查ZXID,ZXID比較大的服務器優先作為leader,
- 如果ZXID相同的話,就比較myid,myid比較大的服務器作為leader, 服務器1的投票是(1,0),它收到投票是(2,0),兩者zxid都是0,因為收到的myid=2,大于自己的myid=1,所以它更新自己的投票為(2,0),然后重新將投票發出去,對于服務器2呢,即不再需要更新自己的投票,把上一次的投票資訊發出即可,
- 統計投票
每次投票后,服務器會統計所有投票,判斷是否有過半的機器接受到相同的投票資訊,服務器2收到兩票,少于3(n/2+1,n為總服務器),所以繼續保持LOOKING狀態
- 每個服務器發出一個投票
- 服務器3(myid=3)啟動,繼續進入Leader選舉階段
跟前面流程一致,服務器1和2先投自己一票,因為服務器3的myid最大,所以大家把票改投給它,此時,服務器為3票(大于等于n/2+1),所以服務器3當選為Leader, 服務器1,2更改狀態為FOLLOWING,服務器3更改狀態為LEADING;
- 服務器4啟動,發起一次選舉,
此時服務器1,2,3已經不是LOOKING狀態,不會更改選票資訊,選票資訊結果:服務器3為3票,服務器4為1票,服務器4并更改狀態為FOLLOWING;
- 服務器5啟動,發起一次選舉,
同理,服務器也是把票投給服務器3,服務器5并更改狀態為FOLLOWING;
- 投票結束,服務器3當選為Leader
服務器運行期間的Leader選舉
zookeeper集群的五臺服務器(myid=1-5)正在運行中,突然某個瞬間,Leader服務器3掛了,這時候便開始Leader選舉~
- 1.變更狀態
Leader 服務器掛了之后,余下的非Observer服務器都會把自己的服務器狀態更改為LOOKING,然后開始進入Leader選舉流程,
- 2.每個服務器發起投票
每個服務器都把票投給自己,因為是運行期間,所以每臺服務器的ZXID可能不相同,假設服務1,2,4,5的zxid分別為333,666,999,888,則分別產生投票(1,333),(2,666),(4,999)和(5,888),然后各自將這個投票發給集群中的其他所有機器,
- 3.接受來自各個服務器的投票
- 4.處理投票
投票規則是跟Zookeeper集群啟動期間一致的,優先檢查ZXID,大的優先作為Leader,所以顯然服務器zxid=999具有優先權,
- 5.統計投票
- 6.改變服務器狀態
11、面試官: 你前面提到在專案中使用過Zookeeper的分布式鎖,講一下zk分布式鎖的實作原理吧?
小菜雞的我:
- 獲取鎖程序 (創建臨時節點,檢查序號最小)
- 釋放鎖 (洗掉臨時節點,監聽通知)
獲取鎖程序
- 當第一個客戶端請求過來時,Zookeeper客戶端會創建一個持久節點/locks,如果它(Client1)想獲得鎖,需要在locks節點下創建一個順序節點lock1.如圖
- 接著,客戶端Client1會查找locks下面的所有臨時順序子節點,判斷自己的節點lock1是不是排序最小的那一個,如果是,則成功獲得鎖,
- 這時候如果又來一個客戶端client2前來嘗試獲得鎖,它會在locks下再創建一個臨時節點lock2
- 客戶端client2一樣也會查找locks下面的所有臨時順序子節點,判斷自己的節點lock2是不是最小的,此時,發現lock1才是最小的,于是獲取鎖失敗,獲取鎖失敗,它是不會甘心的,client2向它排序靠前的節點lock1注冊Watcher事件,用來監聽lock1是否存在,也就是說client2搶鎖失敗進入等待狀態,
- 此時,如果再來一個客戶端Client3來嘗試獲取鎖,它會在locks下再創建一個臨時節點lock3
- 同樣的,client3一樣也會查找locks下面的所有臨時順序子節點,判斷自己的節點lock3是不是最小的,發現自己不是最小的,就獲取鎖失敗,它也是不會甘心的,它會向在它前面的節點lock2注冊Watcher事件,以監聽lock2節點是否存在,
釋放鎖
我們再來看看釋放鎖的流程,zookeeper的客戶端業務完成或者故障,都會洗掉臨時節點,釋放鎖,如果是任務完成,Client1會顯式呼叫洗掉lock1的指令
同理,Client2獲得鎖之后,Client3也對它虎視眈眈,啊哈哈~
12. 面試官:好的,最后一道題,你說說dubbo和Zookeeper的關系吧,為什么選擇Zookeeper作為注冊中心?
小菜雞的我(答了這么多道題,不會還不給我過吧?):
dubbo的注冊中心可以選Zookeeper,memcached,redis等,為什么選擇Zookeeper,因為它的功能特性咯~
- 命名服務,服務提供者向Zookeeper指定節點寫入url,完成服務發布,
- 負載均衡,注冊中心的承載能力有限,而Zookeeper集群配合web應用很容易達到負載均衡,
- zk支持監聽事件,特別適合發布/訂閱的場景,dubbo的生產者和消費者就類似這場景,
- 資料模型簡單,資料存在記憶體,可謂高性能
- Zookeeper其他特點都可以搬出來講一下~
看完三件事??
如果你覺得這篇內容對你還蠻有幫助,我想邀請你幫我三個小忙:
-
點贊,轉發,有你們的 『點贊和評論』,才是我創造的動力,
-
關注公眾號 『 java爛豬皮 』,不定期分享原創知識,
-
同時可以期待后續文章ing??
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/161518.html
標籤:其他
上一篇:Java 檔案 IO 操作
