zookeeper動(dòng)物管理員,是一個(gè)很形象的名字,是一個(gè)分布式協(xié)調(diào)服務(wù)。它可以用來做分布式配置管理,服務(wù)注冊(cè)及發(fā)現(xiàn),分布式鎖。在CAP中,屬于CP型。
下圖是zookeeper的架構(gòu)圖:
圖中,綠色的是client,紅色的是一個(gè)leader,藍(lán)色的是其follower,紅色和藍(lán)色合稱為Ensemble(合奏團(tuán))。
- Ensemble:zookeeper集群所有server構(gòu)成一個(gè)Ensemble,要求必須至少3臺(tái)server。
- Leader:負(fù)責(zé)改變zookeeper的狀態(tài)ZooKeeper state: create, setData, and delete.
- Follower:負(fù)責(zé)執(zhí)行l(wèi)eader的命令。
- Client:client和server之間有心跳保持連接,如果server掛了zookeeper會(huì)自動(dòng)failover到別的server上去。Client連接后zookeeper都會(huì)有相應(yīng)的session,session就是一次活動(dòng)的時(shí)間段,session中可以有這次活動(dòng)的變量,client的session從client建立連接開始一直到client斷開連接為止。
- Quorum:集群中過半數(shù)的server的數(shù)量。比如集群中共有5臺(tái)機(jī)器,那么quorum是5/2+1==3臺(tái)。
一個(gè)zookeeper集群有一個(gè)leader,負(fù)責(zé)讀寫并把數(shù)據(jù)復(fù)制給follower,follower只負(fù)責(zé)讀。是主從結(jié)構(gòu)。當(dāng)leader掛掉后,zookeeper可以在200ms內(nèi)重新選出新leader,可用性比較強(qiáng)。Read操作會(huì)從當(dāng)前連接的server讀(但是因?yàn)閣rite只需要quorum數(shù)量滿足即可生效,所以有可能client讀的是一臺(tái)沒同步的機(jī)器,所以這臺(tái)機(jī)器需要先從leader sync一下再返回),write寫操作需要集群里面大多數(shù)server都確認(rèn)后才算ok,大多數(shù)指的是過半數(shù)。所以寫性能隨集群數(shù)量的增大而下降,讀性能隨集群數(shù)量增大而增加。所以,zookeeper有了observer server,他們不參與選舉,不算大多數(shù)的一部分,但是可以同步leader的數(shù)據(jù)擴(kuò)展讀能力且不降低寫能力,這樣集群增大時(shí)不至于降低太多寫能力。可見,read和write都需要leader,所以leader掛了整個(gè)cluster不能對(duì)外提供任何服務(wù)。如果集群中不夠quorum量,整個(gè)cluster也不能對(duì)外提供任何服務(wù)。
zookeeper由leader把數(shù)據(jù)復(fù)制到follower上,同傳統(tǒng)數(shù)據(jù)庫類似,zookeeper的每項(xiàng)操作也是有事務(wù)的,有事務(wù)id,叫做Zxid(zookeeper transaction id,是個(gè)遞增的數(shù)值)。先來看看zookeeper的數(shù)據(jù)模型,是kv型,其中key是類似于linux的目錄樹:
zookeeper數(shù)據(jù)模型遵循分層命名空間,其中每個(gè)節(jié)點(diǎn)稱為Znode,這是群集運(yùn)行的系統(tǒng)的一部分。和目錄不同的是即使中間的znode也能存數(shù)據(jù),數(shù)據(jù)大小不超過1M,同redis一樣,存放的數(shù)據(jù)也是二進(jìn)制安全的。如下圖:
zookeeper的znode節(jié)點(diǎn)有3種類型:
- 持久性Znode:
集合中的所有節(jié)點(diǎn)都將自己視為持久性Znode。即使在客戶端斷開連接后,這些節(jié)點(diǎn)仍會(huì)保持活動(dòng)狀態(tài)。
- 臨時(shí)Znode:
這些類型的節(jié)點(diǎn)將保持活動(dòng)狀態(tài),直到客戶端連接到它們?yōu)橹埂.?dāng)客戶端斷開連接時(shí),他們會(huì)死亡。這些類型的節(jié)點(diǎn)不允許有子節(jié)點(diǎn)。
- 順序Znode:
它可以是持久性Znode或臨時(shí)Znode。將節(jié)點(diǎn)創(chuàng)建為順序Znode時(shí),可以通過在原始名稱上附加10位序列號(hào)來分配Znode的路徑。
zookeeper如何選擇leader呢?每個(gè)server啟動(dòng)后都是looking狀態(tài),它要么選舉一個(gè)leader要么找到一個(gè)leader。有如下兩種情況:
- 如果leader已經(jīng)存在了,其他server就會(huì)通知它哪臺(tái)server是leader,然后這臺(tái)機(jī)必須和leader進(jìn)行溝通以使得自己的state和leader的state保持一致。
- 如果leader不存在,也就是所有的server都是looking狀態(tài),那server必須相互溝通以選舉出一個(gè)leader。一開始每臺(tái)server都投票給自己,然后根據(jù)規(guī)則(voteZxid > myZxid) or (voteZxid = myZxid and voteSid > mySid)決定是否改變自己的投票(比如別人比自己更up to date那就投別人),當(dāng)收到過半數(shù)投票后就會(huì)選一個(gè)勝出的,簡(jiǎn)單來說就是most up to date的那臺(tái)server勝出。
zookeeper使用兩步法來進(jìn)行數(shù)據(jù)的寫入。叫做zab協(xié)議,zookeeper atomic broadcast,即zookeeper原子廣播協(xié)議。
具體步驟是這樣:
- 客戶端發(fā)起寫請(qǐng)求給某臺(tái)server,server如果不是leader再轉(zhuǎn)給leader。
- Leader將客戶端請(qǐng)求信息轉(zhuǎn)化為事務(wù)Proposal,同時(shí)為每個(gè)Proposal分配一個(gè)事務(wù)ID(Zxid)。
- Leader為每個(gè)Follower單獨(dú)分配一個(gè)FIFO的隊(duì)列,將需要廣播的Proposal依次放入到隊(duì)列中。
- Follower接收到Proposal后,首先將其以事務(wù)日志log的方式寫入到本地磁盤中,寫入成功后給Leader反饋一個(gè)ACK響應(yīng)。
- Leader接收到半數(shù)以上Follower的ACK響應(yīng)后,即認(rèn)為消息發(fā)送成功,可以發(fā)送Commit消息。
- Leader向所有Follower廣播Commit消息,同時(shí)自身也會(huì)完成事務(wù)提交。Follower接收到Commit消息后也會(huì)完成事務(wù)的提交,寫入內(nèi)存,commit后follower自身才有這時(shí)的zxid可用于leader掛掉后的leader選舉,zookeeper同redis也是內(nèi)存型的。
之所以分為兩個(gè)階段,是為了原子性。如果第一個(gè)步驟有過半的成功響應(yīng),則commit,否則不commit,不會(huì)存在中間狀態(tài)。
另外,之所以要求過半的成功響應(yīng),是為了當(dāng)集群掛掉后數(shù)據(jù)不會(huì)丟失。因?yàn)榧涸俅慰捎眯枰^半的server ok狀態(tài),因?yàn)橹坝羞^半的成功響應(yīng),所以此時(shí)過半的server ok中必然存在log中保存有commit的數(shù)據(jù),數(shù)據(jù)不會(huì)丟失。