集群没有leader_面试官问:说说你对ZooKeeper集群与Leader选举的理解?
ZooKeeper是一個(gè)開源分布式協(xié)調(diào)服務(wù)、分布式數(shù)據(jù)一致性解決方案。可基于ZooKeeper實(shí)現(xiàn)命名服務(wù)、集群管理、Master選舉、分布式鎖等功能。
高可用
為了保證ZooKeeper的可用性,在生產(chǎn)環(huán)境中我們使用ZooKeeper集群模式對(duì)外提供服務(wù),并且集群規(guī)模至少由3個(gè)ZooKeeper節(jié)點(diǎn)組成。
集群至少由3個(gè)節(jié)點(diǎn)組成
ZooKeeper其實(shí)2個(gè)節(jié)點(diǎn)也可以組成集群并對(duì)外提供服務(wù),但我們使用集群主要目的是為了高可用。如果2個(gè)節(jié)點(diǎn)組成集群,其中1個(gè)節(jié)點(diǎn)掛了,另外ZooKeeper節(jié)點(diǎn)不能正常對(duì)外提供服務(wù)。因此也失去了集群的意義。
如果3個(gè)節(jié)點(diǎn)組成集群,其中1個(gè)節(jié)點(diǎn)掛掉后,根據(jù)ZooKeeper的Leader選舉機(jī)制是可以從另外2個(gè)節(jié)點(diǎn)選出一個(gè)作為L(zhǎng)eader的,集群可以繼續(xù)對(duì)外提供服務(wù)。
并非節(jié)點(diǎn)越多越好
- 節(jié)點(diǎn)越多,使用的資源越多
- 節(jié)點(diǎn)越多,ZooKeeper節(jié)點(diǎn)間花費(fèi)的通訊成本越高,節(jié)點(diǎn)間互連的Socket也越多。影響ZooKeeper集群事務(wù)處理
- 節(jié)點(diǎn)越多,造成腦裂的可能性越大
集群規(guī)模為奇數(shù)
集群規(guī)模除了考慮自身成本和資源外還要結(jié)合ZooKeeper特性考慮:
- 節(jié)省資源
- 3節(jié)點(diǎn)集群和4節(jié)點(diǎn)集群,我們選擇使用3節(jié)點(diǎn)集群;5節(jié)點(diǎn)集群和6節(jié)點(diǎn)集群,我們選擇使用5節(jié)點(diǎn)集群。以此類推。因?yàn)樯a(chǎn)環(huán)境為了保證高可用,3節(jié)點(diǎn)集群最多只允許掛1臺(tái),4節(jié)點(diǎn)集群最多也只允許掛1臺(tái)(過(guò)半原則中解釋了原因)。同理5節(jié)點(diǎn)集群最多允許掛2臺(tái),6節(jié)點(diǎn)集群最多也只允許掛2臺(tái)。
- 出于對(duì)資源節(jié)省的考慮,我們應(yīng)該使用奇數(shù)節(jié)點(diǎn)來(lái)滿足相同的高可用性。
- 集群可用性
- 當(dāng)集群中節(jié)點(diǎn)間網(wǎng)絡(luò)通訊出現(xiàn)問(wèn)題時(shí)奇數(shù)和偶數(shù)對(duì)集群的影響
集群配置
ZooKeeper集群配置至少需要2處變更:
1、增加集群配置
在{ZK_HOME}/conf/zoo.cfg中增加集群的配置,結(jié)構(gòu)以server.id=ip:port1:port2為標(biāo)準(zhǔn)。
比如下面配置文件中表示由3個(gè)ZooKeeper組成的集群:
server.1=localhost:2881:3881server.2=localhost:2882:3882server.3=localhost:2883:38832、配置節(jié)點(diǎn)id
zoo.cfg中配置集群時(shí)需要指定server.id,這個(gè)id需要在dataDir(zoo.cfg中配置)指定的目錄中創(chuàng)建myid文件,文件內(nèi)容就是當(dāng)前ZooKeeper節(jié)點(diǎn)的id。
集群角色
ZooKeeper沒(méi)有使用Master/Slave的概念,而是將集群中的節(jié)點(diǎn)分為了3類角色:
- Leader
- 在一個(gè)ZooKeeper集群中,只能存在一個(gè)Leader,這個(gè)Leader是集群中事務(wù)請(qǐng)求唯一的調(diào)度者和處理者,所謂事務(wù)請(qǐng)求是指會(huì)改變集群狀態(tài)的請(qǐng)求;Leader根據(jù)事務(wù)ID可以保證事務(wù)處理的順序性。
- 如果一個(gè)集群中存在多個(gè)Leader,這種現(xiàn)象稱為「腦裂」。試想一下,一個(gè)集群中存在多個(gè)Leader會(huì)產(chǎn)生什么影響?
- 相當(dāng)于原本一個(gè)大集群,裂出多個(gè)小集群,他們之間的數(shù)據(jù)是不會(huì)相互同步的。「腦裂」后集群中的數(shù)據(jù)會(huì)變得非常混亂。
- Follower
- Follower角色的ZooKeeper服務(wù)只能處理非事務(wù)請(qǐng)求;如果接收到客戶端事務(wù)請(qǐng)求會(huì)將請(qǐng)求轉(zhuǎn)發(fā)給Leader服務(wù)器;參與Leader選舉;參與Leader事務(wù)處理投票處理。
- Follower發(fā)現(xiàn)集群中Leader不可用時(shí)會(huì)變更自身狀態(tài),并發(fā)起Leader選舉投票,最終集群中的某個(gè)Follower會(huì)被選為L(zhǎng)eader。
- Observer
- Observer與Follower很像,可以處理非事務(wù)請(qǐng)求;將事務(wù)請(qǐng)求轉(zhuǎn)發(fā)給Leader服務(wù)器。
- 與Follower不同的是,Observer不會(huì)參與Leader選舉;不會(huì)參與Leader事務(wù)處理投票。
- Observer用于不影響集群事務(wù)處理能力的前提下提升集群的非事務(wù)處理能力。
Leader選舉
Leader在集群中是非常重要的一個(gè)角色,負(fù)責(zé)了整個(gè)事務(wù)的處理和調(diào)度,保證分布式數(shù)據(jù)一致性的關(guān)鍵所在。既然Leader在ZooKeeper集群中這么重要所以一定要保證集群在任何時(shí)候都有且僅有一個(gè)Leader存在。
如果集群中Leader不可用了,需要有一個(gè)機(jī)制來(lái)保證能從集群中找出一個(gè)最優(yōu)的服務(wù)晉升為L(zhǎng)eader繼續(xù)處理事務(wù)和調(diào)度等一系列職責(zé)。這個(gè)過(guò)程稱為L(zhǎng)eader選舉。
選舉機(jī)制
ZooKeeper選舉Leader依賴下列原則并遵循優(yōu)先順序:
1、選舉投票必須在同一輪次中進(jìn)行
如果Follower服務(wù)選舉輪次不同,不會(huì)采納投票。
2、數(shù)據(jù)最新的節(jié)點(diǎn)優(yōu)先成為L(zhǎng)eader
數(shù)據(jù)的新舊使用事務(wù)ID判定,事務(wù)ID越大認(rèn)為節(jié)點(diǎn)數(shù)據(jù)約接近Leader的數(shù)據(jù),自然應(yīng)該成為L(zhǎng)eader。
3、比較server.id,id值大的優(yōu)先成為L(zhǎng)eader
如果每個(gè)參與競(jìng)選節(jié)點(diǎn)事務(wù)ID一樣,再使用server.id做比較。server.id是節(jié)點(diǎn)在集群中唯一的id,myid文件中配置。
不管是在集群?jiǎn)?dòng)時(shí)選舉Leader還是集群運(yùn)行中重新選舉Leader。集群中每個(gè)Follower角色服務(wù)都是以上面的條件作為基礎(chǔ)推選出合適的Leader,一旦出現(xiàn)某個(gè)節(jié)點(diǎn)被過(guò)半推選,那么該節(jié)點(diǎn)晉升為L(zhǎng)eader。
過(guò)半原則
ZooKeeper集群會(huì)有很多類型投票。Leader選舉投票;事務(wù)提議投票;這些投票依賴過(guò)半原則。就是說(shuō)ZooKeeper認(rèn)為投票結(jié)果超過(guò)了集群總數(shù)的一半,便可以安全的處理后續(xù)事務(wù)。
- 事務(wù)提議投票
- 假設(shè)有3個(gè)節(jié)點(diǎn)組成ZooKeeper集群,客戶端請(qǐng)求添加一個(gè)節(jié)點(diǎn)。Leader接到該事務(wù)請(qǐng)求后給所有Follower發(fā)起「創(chuàng)建節(jié)點(diǎn)」的提議投票。如果Leader收到了超過(guò)集群一半數(shù)量的反饋,繼續(xù)給所有Follower發(fā)起commit。此時(shí)Leader認(rèn)為集群過(guò)半了,就算自己掛了集群也是安全可靠的。
- Leader選舉投票
- 假設(shè)有3個(gè)節(jié)點(diǎn)組成ZooKeeper集群,這時(shí)Leader掛了,需要投票選舉Leader。當(dāng)相同投票結(jié)果過(guò)半后Leader選出。
- 集群可用節(jié)點(diǎn)
- ZooKeeper集群中每個(gè)節(jié)點(diǎn)有自己的角色,對(duì)于集群可用性來(lái)說(shuō)必須滿足過(guò)半原則。這個(gè)過(guò)半是指Leader角色 + Follower角色可用數(shù)大于集群中Leader角色 + Follower角色總數(shù)。
- 假設(shè)有5個(gè)節(jié)點(diǎn)組成ZooKeeper集群,一個(gè)Leader、兩個(gè)Follower、兩個(gè)Observer。當(dāng)掛掉兩個(gè)Follower或掛掉一個(gè)Leader和一個(gè)Follower時(shí)集群將不可用。因?yàn)镺bserver角色不參與任何形式的投票。
所謂過(guò)半原則算法是說(shuō)票數(shù) > 集群總節(jié)點(diǎn)數(shù)/2。其中集群總節(jié)點(diǎn)數(shù)/2的計(jì)算結(jié)果會(huì)向下取整。
在ZooKeeper源代碼QuorumMaj.java中實(shí)現(xiàn)了這個(gè)算法。下面代碼片段有所縮減。
public boolean containsQuorum(HashSet set) { /** n是指集群總數(shù) */ int half = n / 2; return (set.size() > half);}回過(guò)頭我們看一下奇數(shù)和偶數(shù)集群在Leader選舉的結(jié)果
所以3節(jié)點(diǎn)和4節(jié)點(diǎn)組成的集群在ZooKeeper過(guò)半原則下都最多只能掛1節(jié)點(diǎn),但是4比3要多浪費(fèi)一個(gè)節(jié)點(diǎn)資源。
場(chǎng)景實(shí)戰(zhàn)
我們以兩個(gè)場(chǎng)景來(lái)了解集群不可用時(shí)Leader重新選舉的過(guò)程。
3節(jié)點(diǎn)集群重選Leader
假設(shè)有3節(jié)點(diǎn)組成的集群,分別是server.1(Follower)、server.2(Leader)、server.3(Follower)。此時(shí)server.2不可用了。集群會(huì)產(chǎn)生以下變化:
1、集群不可用
因?yàn)長(zhǎng)eader掛了,集群不可用于事務(wù)請(qǐng)求了。
2、狀態(tài)變更
所有Follower節(jié)點(diǎn)變更自身狀態(tài)為L(zhǎng)OOKING,并且變更自身投票。投票內(nèi)容就是自己節(jié)點(diǎn)的事務(wù)ID和server.id。我們以(事務(wù)ID, server.id)表示。
假設(shè)server.1的事務(wù)id是10,變更的自身投票就是(10, 1);server.3的事務(wù)id是8,變更的自身投票就是(8, 3)。
3、首輪投票
將變更的投票發(fā)給集群中所有的Follower節(jié)點(diǎn)。server.1將(10, 1)發(fā)給集群中所有Follower,包括它自己。server.3也一樣,將(8, 3)發(fā)給所有Follower。
所以server.1將收到(10, 1)和(8, 3)兩個(gè)投票,server.3將收到(8, 3)和(10, 1)兩個(gè)投票。
4、投票PK
每個(gè)Follower節(jié)點(diǎn)除了發(fā)起投票外,還接其他Follower發(fā)來(lái)的投票,并與自己的投票PK(比較兩個(gè)提議的事務(wù)ID以及server.id),PK結(jié)果決定是否要變更自身狀態(tài)并再次投票。
對(duì)于server.1來(lái)說(shuō)收到(10, 1)和(8, 3)兩個(gè)投票,與自己變更的投票比較后沒(méi)有一個(gè)比自身投票(10, 1)要大的,所以server.1維持自身投票不變。
對(duì)于server.3來(lái)說(shuō)收到(10, 1)和(8, 3)兩個(gè)投票,與自身變更的投票比較后認(rèn)為server.1發(fā)來(lái)的投票要比自身的投票大,所以server.3會(huì)變更自身投票并將變更后的投票發(fā)給集群中所有Follower。
5、第二輪投票
server.3將自身投票變更為(10, 1)后再次將投票發(fā)給集群中所有Follower。
對(duì)于server.1來(lái)說(shuō)在第二輪收到了(10, 1)投票,server.1經(jīng)過(guò)PK后繼續(xù)維持不變。
對(duì)于server.3來(lái)說(shuō)在第二輪收到了(10, 1)投票,因?yàn)閟erver.3自身已變更為(10, 3)投票,所以本次也維持不變。
此時(shí)server.1和server.3在投票上達(dá)成一致。
6、投票接收桶
節(jié)點(diǎn)接收的投票存儲(chǔ)在一個(gè)接收桶里,每個(gè)Follower的投票結(jié)果在桶內(nèi)只記錄一次。ZooKeeper源碼中接收桶用Map實(shí)現(xiàn)。
下面代碼片段是ZooKeeper定義的接收桶,以及向桶內(nèi)寫入數(shù)據(jù)。Map.Key是Long類型,用來(lái)存儲(chǔ)投票來(lái)源節(jié)點(diǎn)的server.id,Vote則是對(duì)應(yīng)節(jié)點(diǎn)的投票信息。節(jié)點(diǎn)收到投票后會(huì)更新這個(gè)接收桶,也就是說(shuō)桶里存儲(chǔ)了所有Follower節(jié)點(diǎn)的投票并且僅存最后一次的投票結(jié)果。
HashMap recvset = new HashMap();recvset.put(n.sid, new Vote(n.leader, n.zxid, n.electionEpoch, n.peerEpoch));7、統(tǒng)計(jì)投票
接收到投票后每次都會(huì)嘗試統(tǒng)計(jì)投票,投票統(tǒng)計(jì)過(guò)半后選舉成功。
投票統(tǒng)計(jì)的數(shù)據(jù)來(lái)源于投票接收桶里的投票數(shù)據(jù),我們從頭描述這個(gè)場(chǎng)景,來(lái)看一下接收桶里的數(shù)據(jù)變化情況。
server.2掛了后,server.1和server.3發(fā)起第一輪投票。
server.1接收到來(lái)自server.1的(10, 1)投票和來(lái)自server.3的(8, 3)投票。
server.3同樣接收到來(lái)自server.1的(10, 1)投票和來(lái)自server.3的(8, 3)投票。此時(shí)server.1和server.3接收桶里的數(shù)據(jù)是這樣的:
server.3經(jīng)過(guò)PK后認(rèn)為server.1的選票比自己要大,所以變更了自己的投票并重新發(fā)起投票。
server.1收到了來(lái)自server.3的(10, 1)投票;server.3收到了來(lái)自sever.3的(10, 1)投票。此時(shí)server.1和server.3接收桶里的數(shù)據(jù)變成了這樣:
基于ZooKeeper過(guò)半原則:桶內(nèi)投票選舉server.1作為L(zhǎng)eader出現(xiàn)2次,滿足了過(guò)半 2 > 3/2 即 2>1。
最后sever.1節(jié)點(diǎn)晉升為L(zhǎng)eader,server.3變更為Follower。
集群擴(kuò)容Leader啟動(dòng)時(shí)機(jī)
ZooKeeper集群擴(kuò)容需要在zoo.cfg配置文件中加入新節(jié)點(diǎn)。擴(kuò)容流程在ZooKeeper擴(kuò)容中介紹。這里我們以3節(jié)點(diǎn)擴(kuò)容到5節(jié)點(diǎn)時(shí),Leader啟動(dòng)時(shí)機(jī)做一個(gè)討論。
假設(shè)目前有3個(gè)節(jié)點(diǎn)組成集群,分別是server.1(Follower)、server.2(Leader)、server.3(Follower),假設(shè)集群中節(jié)點(diǎn)事務(wù)ID相同。配置文件如下。
server.1=localhost:2881:3881server.2=localhost:2882:3882server.3=localhost:2883:38831、新節(jié)點(diǎn)加入集群
集群中新增server.4和server.5兩個(gè)節(jié)點(diǎn),首先修改server.4和server.5的zoo.cfg配置并啟動(dòng)。節(jié)點(diǎn)4和5在啟動(dòng)后會(huì)變更自身投票狀態(tài),發(fā)起一輪Leader選舉投票。server.1、server.2、server.3收到投票后由于集群中已有選定Leader,所以會(huì)直接反饋server.4和server.5投票結(jié)果:server.2是Leader。server.4和server.5收到投票后基于過(guò)半原則認(rèn)定server.2是Leader,自身便切換為Follower。
#節(jié)點(diǎn)server.1、server.2、server.3配置server.1=localhost:2881:3881server.2=localhost:2882:3882server.3=localhost:2883:3883#節(jié)點(diǎn)server.4、server.5配置server.1=localhost:2881:3881server.2=localhost:2882:3882server.3=localhost:2883:3883server.4=localhost:2884:3884server.5=localhost:2885:38852、停止Leader
server.4和server.5的加入需要修改集群server.1、server.2、server.3的zoo.cfg配置并重啟。但是Leader節(jié)點(diǎn)何時(shí)重啟是有講究的,因?yàn)長(zhǎng)eader重啟會(huì)導(dǎo)致集群中Follower發(fā)起Leader重新選舉。在server.4和server.5兩個(gè)新節(jié)點(diǎn)正常加入后,集群不會(huì)因?yàn)樾鹿?jié)點(diǎn)加入變更Leader,所以目前server.2依然是Leader。
我們以一個(gè)錯(cuò)誤的順序啟動(dòng),看一下集群會(huì)發(fā)生什么樣的變化。修改server.2zoo.cfg配置文件,增加server.4和server.5的配置并停止server.2服務(wù)。停止server.2后,Leader不存在了,集群中所有Follower會(huì)發(fā)起投票。當(dāng)server.1和server.3發(fā)起投票時(shí)并不會(huì)將投票發(fā)給server.4和server.5,因?yàn)樵趕erver.1和server.3的集群配置中不包含server.4和server.5節(jié)點(diǎn)。相反,server.4和server.5會(huì)把選票發(fā)給集群中所有節(jié)點(diǎn)。也就是說(shuō)對(duì)于server.1和server.3他們認(rèn)為集群中只有3個(gè)節(jié)點(diǎn)。對(duì)于server.4和server.5他們認(rèn)為集群中有5個(gè)節(jié)點(diǎn)。
根據(jù)過(guò)半原則,server.1和server.3很快會(huì)選出一個(gè)新Leader,我們這里假設(shè)server.3晉級(jí)成為了新Leader。但是我們沒(méi)有啟動(dòng)server.2的情況下,因?yàn)橥镀辈粷M足過(guò)半原則,server.4和server.5會(huì)一直做投票選舉Leader的動(dòng)作。截止到現(xiàn)在集群中節(jié)點(diǎn)狀態(tài)是這樣的:
3、啟動(dòng)Leader
現(xiàn)在,我們啟動(dòng)server.2。因?yàn)閟erver.2zoo.cfg已經(jīng)是server.1到serverv.5的全量配置,在server.2啟動(dòng)后會(huì)發(fā)起選舉投票,同時(shí)serverv.4和serverv.5也在不斷的發(fā)起選舉投票。當(dāng)server.2的選舉輪次和serverv.4與serverv.5選舉輪次對(duì)齊后,最終server.2會(huì)變更自己的狀態(tài),認(rèn)定server.5是Leaader。
意想不到的事情發(fā)生了,出現(xiàn)兩個(gè)Leader:
ZooKeeper集群擴(kuò)容時(shí),如果Leader節(jié)點(diǎn)最后啟動(dòng)就可以避免這類問(wèn)題發(fā)生,因?yàn)樵贚eader節(jié)點(diǎn)重啟前,所有的Follower節(jié)點(diǎn)zoo.cfg配置已經(jīng)是相同的,他們基于同一個(gè)集群配置兩兩互聯(lián),做投票選舉。
有了路線解析圖,有沒(méi)有免費(fèi)資料?有沒(méi)有志同道合的小伙伴共同進(jìn)步?
讀者福利
免費(fèi)分享java高清思維方向?qū)D以及阿里架構(gòu)師精講的架構(gòu)視頻分享(包括高可用,高并發(fā),spring源碼,mybatis源碼,JVM,大數(shù)據(jù),Netty等多個(gè)技術(shù)知識(shí)的架構(gòu)視頻資料和各種電子書籍閱讀)視頻資料獲取方式轉(zhuǎn)發(fā)后關(guān)注我私信關(guān)鍵詞【架構(gòu)資料】領(lǐng)取!
精講架構(gòu)視頻資料獲取方式:轉(zhuǎn)發(fā)后關(guān)注我私信關(guān)鍵詞【架構(gòu)資料】領(lǐng)取!
以及一些一線互聯(lián)網(wǎng)公司的面試題解析含答案
一線互聯(lián)網(wǎng)公司的面試題解析含答案
精講架構(gòu)視頻資料獲取方式:轉(zhuǎn)發(fā)后關(guān)注我私信關(guān)鍵詞【架構(gòu)資料】領(lǐng)取!
總結(jié)
以上是生活随笔為你收集整理的集群没有leader_面试官问:说说你对ZooKeeper集群与Leader选举的理解?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 网页中打开msn窗口方法
- 下一篇: c语言最好的文本编辑器,【软件分享】两个