一、zookeeper 初始化:
在Zookeeper啟動期間,首先會進行數(shù)據(jù)初始化工作柒瓣,用于將存儲在磁盤上的數(shù)據(jù)文件加載到Zookeeper
服務(wù)器內(nèi)存中喳魏,主要包括了從快照文件中加載快照數(shù)據(jù)和根據(jù)事物日志進行數(shù)據(jù)訂正兩個過程卖毁。以下是Zookeeper數(shù)據(jù)初始化的過程。
? ??1.FileTxnSnapLog是Zookeeper事務(wù)日志和快照數(shù)據(jù)訪問層贩毕,內(nèi)容中有FileTxnLog(事務(wù)日志管理器)初始化
FileSnap(快照數(shù)據(jù)管理器)初始化挠轴。
? ??2.構(gòu)建內(nèi)存數(shù)據(jù)庫ZKDatabase,首先構(gòu)建一個初始化DataTree,創(chuàng)建一些Zookeeper默認(rèn)節(jié)點/./zookeeper,/zookeeper/quota
3.PlayBackListener監(jiān)聽器主要用來接收事務(wù)應(yīng)用過程中的回調(diào)耳幢?
4.反序列化快照文件,并進行文件的checksum校驗以確定快照文件的正確性欧啤。
5.獲得快照最新ZXID(zxid_for_snap)睛藻,獲取事務(wù)日志ZXID比zxid_for_snap大的事務(wù),將根據(jù)事務(wù)日志更新對應(yīng)的數(shù)據(jù)邢隧,更新完所有事務(wù)日志將獲得事務(wù)最大ZXID(lastProcessedZxid)每當(dāng)有一個事務(wù)被應(yīng)用到內(nèi)存數(shù)據(jù)庫中去后,Zookeeper同是回調(diào)PlayBackListener監(jiān)聽器店印,將這一事務(wù)操作記錄轉(zhuǎn)換成Proposal,并保存到ZKDatabase.committedLog中倒慧,以便Follower進行快照同步按摘。
6.epoch:紀(jì)元、時代纫谅。標(biāo)識當(dāng)前Leader周期炫贤。每次選舉產(chǎn)生一個新的Leader服務(wù)器之后,就會生成一個新的epoch付秕。在運行期間集群中機器相互通信的過程中兰珍,都會帶上這個epoch以確保彼此在同一個Leader周期內(nèi)。
二询吴、zookeeper各個服務(wù)器角色:
在Zookeeper集群中掠河,分別有Leader、Follower和Observer三種類型的服務(wù)器角色猛计。其中非Leader也稱為Leaner服務(wù)器唠摹。
1.Leader
Leader服務(wù)器是整個Zookeeper集群工作機制中的核心,其主要工作有以下兩個:
1)事務(wù)請求的唯一調(diào)度和處理者奉瘤,保證集群事務(wù)處理的順序性勾拉。
2)集群內(nèi)部各服務(wù)器的調(diào)度者。
?使用責(zé)任鏈模式來處理每個客戶端請求是Zookeeper的一大特色盗温。
? ? 請求處理鏈:
非事務(wù)請求將直接提交給CommitProcessor否則將除了將請求提交給CommitProcessor外望艺,根據(jù)請求類型創(chuàng)建對應(yīng)的Proposal提議,并發(fā)送給所有的Follower服務(wù)器來發(fā)起一次集群內(nèi)的事務(wù)投票肌访。同時找默,講過失去請求交付給SynRequestProcessor進行事務(wù)日志的記錄以及事務(wù)的快照。
LearnerHandler:
? ? 為了保持很整個內(nèi)部的實時通信吼驶,Leader服務(wù)器會與每一個Follower/Observer服務(wù)器都建立一個TCP長連接惩激,同時也會為每個Follower/Observer服務(wù)器都創(chuàng)建一個名為LeanerHandler的實體店煞。LeanerHandler是Learner服務(wù)器的管理器,主要負(fù)責(zé)Learner服務(wù)器與Leader服務(wù)器之間的網(wǎng)絡(luò)通信风钻,包括數(shù)據(jù)同步顷蟀、請求轉(zhuǎn)發(fā)和Proposal提議的投票等。
2.Follower
Follower服務(wù)器是Zookeeper集群的跟隨者骡技,其主要工作有以下三個:
1)處理客戶端非事務(wù)請求鸣个,轉(zhuǎn)發(fā)事務(wù)請求給Leader服務(wù)器。
2)參與事務(wù)請求Proposal的投票布朦。
3)參與Leader選舉投票囤萤。
? ? ? ? 由于不需要負(fù)責(zé)對事務(wù)請求的投票處理,因此相對來說Follower服務(wù)器的請求處理鏈會簡單一些是趴,如下:
FollowerRequestProcessor是Follower服務(wù)器的第一個請求處理器翁垂,其主要工作就是識別出當(dāng)前請求是否是事務(wù)請求建椰。如果是事務(wù)請求則將請求發(fā)送給Leader服務(wù)器牺弄。
SendAckRequestProcessor承擔(dān)事務(wù)日志記錄反饋的角色焕襟,在完成事務(wù)日志記錄后,會向Leader服務(wù)器發(fā)送ACK消息以表明自身完成了事務(wù)日志的記錄工作肛搬。
3.Observer
? ? ? ? 該服務(wù)器充當(dāng)一個觀察者的角色没佑,觀察Zookeeper 集群的最新狀態(tài)變化并將這些狀態(tài)變更同步過來,與Follower唯一區(qū)別是Observer不參與任何形式的投票温赔,包括事務(wù)請求Proposal的投票和Leader選舉投票图筹。
? ? ? ? Observer請求處理鏈,如下:
zookeeper leader選舉:
1.Leader選舉概述:
每個服務(wù)器都會以(myid,ZXID)的方式發(fā)起投票让腹,每個zookeeper服務(wù)器都會有一個myid,且唯一远剩。
? ?選舉流程:
? ? 1)每個server都會發(fā)出一個投票
? ? 2)接收來自各個服務(wù)器的投票
? ? 3)處理投票:
優(yōu)先檢查ZXID如果,其他ZXID比自己ZXID大骇窍,則更新自己的投票
如果ZXID相同瓜晤,則比較myid,如果自己myid最大,則不更新自己的投票腹纳,否則將更新痢掠。
? ? 4)統(tǒng)計投票:投票過半
5)改變服務(wù)器狀態(tài)
如果運行中Leader掛掉,則先修改非Observer服務(wù)器的狀態(tài)為Locking嘲恍,在進行Leader選舉流程足画。
2.Leader算法分析:
SID: ?服務(wù)器ID ,ZXID: 事務(wù)ID, Vote: 投票, Quorum: 過半機器數(shù) quorum=(n/2+1)
集群中已經(jīng)存在Leader服務(wù)器佃牛,則會被告知當(dāng)前Leader的信息淹辞,將不進行Leader的選舉。
1)每個server都會發(fā)出(SID俘侠,ZXID)的投票
2)收到各個服務(wù)器投票(vote_sid, vote_zxid)
3) ? ?vote_zxid>self_zxid ,投票(vote_sid,vote_zxid)
vote_zxid=self_zxid象缀,vote_sid>self_sid,投票(vote_sid,vote_zxid)
否則將不進行投票
? ? ? 4) ? ?統(tǒng)計過半的投票
? ? ? ? 總之蔬将,通常那臺服務(wù)器上的數(shù)據(jù)越新,那么將越有可能成為Leader央星,原因很簡單霞怀,數(shù)據(jù)越新,那么它的ZXID也就越大莉给,也就是越能夠保證數(shù)據(jù)的回復(fù)毙石。當(dāng)然,如果集群中幾個服務(wù)器具有相同的ZXID,那么SID較大的那臺服務(wù)器成為Leader颓遏。
3.Leader實現(xiàn)細(xì)節(jié):
? ? 服務(wù)器狀態(tài):QuorumPeer.ServerState狀態(tài)徐矩,
LOOKING(尋找Leader狀態(tài)),F(xiàn)OLLOWING州泊,LEADING,OBSERVING
投票數(shù)據(jù)結(jié)構(gòu):Vote(id,zxid,electionEpoch(邏輯時鐘漂洋,每進入新一輪的投票后遥皂,都會對改制進行加1操作)?peerEpoch被推舉的Leader的epoch?,state)
QuorumCnxManager:每臺服務(wù)器啟動的時候,都會啟動一個QuorumCnxManager,負(fù)責(zé)各臺服務(wù)器之間的底層Leader選舉過程中的網(wǎng)絡(luò)通信刽漂。這個類內(nèi)部維護了一系列的隊列演训,用于保存接收到的、待發(fā)送的消息贝咙,以及消息的發(fā)送器样悟。除接受隊列以外,這里提到的所有隊列都有一個共同點---按SID分組形成隊列集合庭猩,假設(shè)集群中除自身外還有4臺機器窟她,那么當(dāng)前服務(wù)器就會為這4臺服務(wù)器分別創(chuàng)建一個發(fā)送隊列,互不干擾蔼水。
recvQueue: 消息接收隊列震糖,用于存放那些從其他服務(wù)器接收到的消息。
queueSendMap:消息發(fā)送隊列趴腋,用于保存那些待發(fā)送的消息吊说。是Map,按照SID進行分組优炬,分別為集群中的每臺機器分配一個單獨隊列颁井,從而保證各臺機器之間的消息發(fā)送互不影響。
senderWorkerMap:發(fā)送器集合蠢护,每個SendWorker消息發(fā)送器雅宾,都對應(yīng)一臺遠(yuǎn)程Zookeeper服務(wù)器,負(fù)責(zé)消息的發(fā)送葵硕。
lastMessageSent:最近發(fā)送過的消息秀又。為每個SID保留最新的發(fā)送消息。
? ? QuorumCnxManager在啟動的時候吐辙,會創(chuàng)建一個ServerSocket來監(jiān)聽Leader選舉的通信端口宣决。開啟端口監(jiān)聽后,Zookeeper就能夠不斷的接受到來自其他服務(wù)器的創(chuàng)建連接請求昏苏,在接收到來自其他服務(wù)器的TCP連接請求時尊沸,會交由receiveConnection函數(shù)來處理贤惯。
為了避免兩臺機器之間重復(fù)地創(chuàng)建TCP連接洼专,Zookeeper設(shè)計一種建立TCP連接的規(guī)則:只允許SID大的服務(wù)器主動和其他服務(wù)器建立連接,否則斷開連接孵构。如果當(dāng)前服務(wù)器的SID創(chuàng)建相應(yīng)的消息發(fā)送器SendWorker和消息接收器RecvWorker ,并啟動他們屁商。
RecvWorker只需要不斷從這個TCP連接中讀取消息,并將其保存到recvQueue隊列中颈墅。
SendWorker只需要不斷地從對應(yīng)的消息發(fā)送隊列中獲取出一個消息來發(fā)送即可蜡镶,同時將這個消息放入lastMessageSent中來作為最近發(fā)送過的消息。如果當(dāng)前遠(yuǎn)程服務(wù)器的消息發(fā)送隊列為空恤筛,那么這個時候就需要從lastMessageSent中取出一個最近發(fā)送過的消息來進行再次發(fā)送官还。這個細(xì)節(jié)的處理主要是為了解決這樣一類的分布式問題:接受方在消息接受前,或者是在接收到消息后服務(wù)器掛掉了毒坛,導(dǎo)致消息尚未被正確處理望伦。那么如此重復(fù)發(fā)送是否會導(dǎo)致其他問題呢?當(dāng)然煎殷,這里可以放心的一點是屯伞,Zookeeper能夠保證接收方在處理消息的時候,會對重復(fù)消息進行正確的處理豪直。