對(duì)于客戶端消息普办,zk創(chuàng)建了一系列的RequestProcessor
來(lái)對(duì)消息進(jìn)行鏈?zhǔn)教幚砥痰小k服務(wù)承擔(dān)不同角色時(shí)兼蜈,消息處理鏈?zhǔn)遣煌摹?/p>
一历等、Standalone
PrepRequestProcessor ->SyncRequestProcessor ->FinalRequestProcessor
PrepRequestProcessor:
主要工作是做check,并在請(qǐng)求中填充一些必須的數(shù)據(jù):
校驗(yàn)session是否合法,檢查是否有acl權(quán)限,對(duì)于創(chuàng)建目錄的請(qǐng)求,檢查父節(jié)點(diǎn)是否是臨時(shí)節(jié)點(diǎn)等氏身。巍棱。
對(duì)于寫操作,給請(qǐng)求附加上
TxnHeader
(帶請(qǐng)求時(shí)間蛋欣,zxid等)航徙。
...
SyncRequestProcessor
邏輯比較多,如在PrepRequestProcessor
所說(shuō)的,對(duì)于會(huì)改變數(shù)據(jù)內(nèi)容的請(qǐng)求或者事務(wù)請(qǐng)求陷虎,zk會(huì)在request頭部增加一個(gè)TxnHeader到踏。
1.對(duì)于帶TxnHeader的消息,會(huì)附加到日志中尚猿,并增加到SyncRequestProcessor#toFlush
隊(duì)列中窝稿,當(dāng)toFlush超限,或者從上游RequestProcessor中獲取不到新的消息時(shí)谊路,開始做flush讹躯。
flush:
將日志整體flush到磁盤
逐一將消息轉(zhuǎn)給下個(gè)RequestProcessor
2.對(duì)于存粹的讀請(qǐng)求菩彬,可以直接放行給下個(gè)RequestProcessor缠劝,但是有要求,toFlush
需要是空的骗灶,這個(gè)比較好理解惨恭,如果toFlush
不為空,那么可能之前有寫操作改變數(shù)據(jù)庫(kù)內(nèi)容耙旦,導(dǎo)致臟讀脱羡。
3.當(dāng)日志數(shù)量超過(guò)一個(gè)隨機(jī)值,(用隨機(jī)值的目的是為了讓zk集群不同時(shí)dump快照)免都,創(chuàng)建一個(gè)線程固化一個(gè)快照到磁盤锉罐。
FinalRequestProcessor
這是最后一步了,主要操作就是對(duì)zk數(shù)據(jù)庫(kù)的增刪改查绕娘。注意到脓规,單機(jī)系統(tǒng)中,可以直接改险领,但是在集群中侨舆,leader需要等過(guò)半節(jié)點(diǎn)確認(rèn)過(guò)了之后才可以修改。同時(shí)附加下面兩部操作:
1.維護(hù)一個(gè)內(nèi)存日志隊(duì)列绢陌,作用是便于leader給掉線不久的節(jié)點(diǎn)同步DIFF數(shù)據(jù)挨下。
2.組裝response報(bào)文返回給客戶端
二、Leader
LeaderRequestProcessor->PrepRequestProcessor ->ProposalRequestProcessor -> CommitProcessor-> Leader.ToBeAppliedRequestProcessor ->FinalRequestProcessor
ProposalRequestProcessor
提交議案的處理器脐湾,主要是針對(duì)寫請(qǐng)求;對(duì)于寫操作臭笆,將這個(gè)請(qǐng)求廣播給所有follower。
同時(shí)ProposalRequestProcessor內(nèi)部有一個(gè)SyncRequestProcessor->AckRequestProcessor處理鏈,處理follower的回復(fù)愁铺,當(dāng)ack過(guò)半時(shí)凿菩,commit這次request,CommitProcessor才會(huì)繼續(xù)執(zhí)行帜讲。
CommitProcessor
顧名思義衅谷,這個(gè)處理器時(shí)處理需要決議的請(qǐng)求內(nèi)容,對(duì)于讀請(qǐng)求直接放行似将。其內(nèi)部有3個(gè)隊(duì)列:
CommitProcessor#queuedRequests
消息隊(duì)列获黔,存放所有從ProposalRequestProcessor到達(dá)的消息。
CommitProcessor#pendingRequests
等待隊(duì)列在验,CommitProcessor#queuedRequests中的請(qǐng)求分為兩類玷氏,讀請(qǐng)求直接放行,寫請(qǐng)求會(huì)放到這個(gè)隊(duì)列中去腋舌。
CommitProcessor#committedRequests
存放已提交的消息盏触,和上文中的ProposalRequestProcessor聯(lián)系起來(lái),當(dāng)收到過(guò)半follower的ack回復(fù)時(shí)块饺,會(huì)有如下的函數(shù)調(diào)用發(fā)生:AckRequestProcessor#processRequest->Leader#tryToCommit->CommitProcessor#commit.
Leader.ToBeAppliedRequestProcessor
代碼很簡(jiǎn)單赞辩,就是把CommitProcessor已確認(rèn)的消息清除了,不太明白有什么作用授艰,可能就是做個(gè)記錄辨嗽。
三、Follower
Follower在確定了自己為follower之后淮腾,會(huì)從leader處同步log(全量的情況是snapshot)糟需,同步完成以后,再啟動(dòng)下面的處理器鏈:
FollowerRequestProcessor->CommitProcessor->FinalRequestProcessor
FollowerRequestProcessor
主要任務(wù)就是調(diào)用Learner#request
將寫請(qǐng)求轉(zhuǎn)發(fā)給leader
四谷朝、實(shí)例舉例
假設(shè)有一條客戶端的寫請(qǐng)求發(fā)給了Follower節(jié)點(diǎn)A洲押。 A會(huì)在FollowerRequestProcessor#run中將請(qǐng)求發(fā)給leader,Leader接收到這條寫請(qǐng)求圆凰,在ProposalRequestProcessor中將消息進(jìn)行廣播杈帐,follower節(jié)點(diǎn)收到消息以后,在選舉線程調(diào)用FollowerZooKeeperServer#logRequest
將日志寫到本地送朱,然后SyncRequestProcessor對(duì)日志進(jìn)行提交后通過(guò)下個(gè)處理器SendAckRequestProcessor
給leader返回ack娘荡。
leader收到過(guò)半回復(fù),對(duì)這條日志進(jìn)行提交固化到磁盤驶沼。同時(shí)同步給follower們炮沐,過(guò)程如下:產(chǎn)生一條攜帶原始zxid的Leader.COMMIT
類型的消息,先放入LearnerHandler#queuedPackets
(每個(gè)follower均會(huì)有一個(gè)LearnerHandler實(shí)例對(duì)接),LearnerHandler#run
將隊(duì)列中的記錄發(fā)給follower.
follower對(duì)消息進(jìn)行提交回怜,存放到CommitProcessor#committedRequests
大年,接著follower的處理鏈CommitProcessor調(diào)用下個(gè)處理器將消息存入zkdb中换薄,最后返回給客戶端結(jié)果。