【分布式】Zookeeper请求处理
一、前言
在前面學(xué)習(xí)了Zookeeper中服務(wù)器的三種角色及其之間的通信,接著學(xué)習(xí)對于客戶端的一次請求,Zookeeper是如何進(jìn)行處理的。
二、請求處理
2.1 會話創(chuàng)建請求
Zookeeper服務(wù)端對于會話創(chuàng)建的處理,大體可以分為請求接收、會話創(chuàng)建、預(yù)處理、事務(wù)處理、事務(wù)應(yīng)用和會話響應(yīng)六大環(huán)節(jié),其大體流程如
1. 請求接收
(1) I/O層接收來自客戶端的請求。NIOServerCnxn維護(hù)每一個客戶端連接,客戶端與服務(wù)器端的所有通信都是由NIOServerCnxn負(fù)責(zé),其負(fù)責(zé)統(tǒng)一接收來自客戶端的所有請求,并將請求內(nèi)容從底層網(wǎng)絡(luò)I/O中完整地讀取出來。
(2) 判斷是否是客戶端會話創(chuàng)建請求。每個會話對應(yīng)一個NIOServerCnxn實體,對于每個請求,Zookeeper都會檢查當(dāng)前NIOServerCnxn實體是否已經(jīng)被初始化,如果尚未被初始化,那么就可以確定該客戶端一定是會話創(chuàng)建請求。
(3) 反序列化ConnectRequest請求。一旦確定客戶端請求是否是會話創(chuàng)建請求,那么服務(wù)端就可以對其進(jìn)行反序列化,并生成一個ConnectRequest載體。
(4) 判斷是否是ReadOnly客戶端。如果當(dāng)前Zookeeper服務(wù)器是以ReadOnly模式啟動,那么所有來自非ReadOnly型客戶端的請求將無法被處理。因此,服務(wù)端需要先檢查是否是ReadOnly客戶端,并以此來決定是否接受該會話創(chuàng)建請求。
(5) 檢查客戶端ZXID。正常情況下,在一個Zookeeper集群中,服務(wù)端的ZXID必定大于客戶端的ZXID,因此若發(fā)現(xiàn)客戶端的ZXID大于服務(wù)端ZXID,那么服務(wù)端不接受該客戶端的會話創(chuàng)建請求。
(6) 協(xié)商sessionTimeout。在客戶端向服務(wù)器發(fā)送超時時間后,服務(wù)器會根據(jù)自己的超時時間限制最終確定該會話超時時間,這個過程就是sessionTimeout協(xié)商過程。
(7) 判斷是否需要重新激活創(chuàng)建會話。服務(wù)端根據(jù)客戶端請求中是否包含sessionID來判斷該客戶端是否需要重新創(chuàng)建會話,若客戶單請求中包含sessionID,那么就認(rèn)為該客戶端正在進(jìn)行會話重連,這種情況下,服務(wù)端只需要重新打開這個會話,否則需要重新創(chuàng)建。
2. 會話創(chuàng)建
(1) 為客戶端生成sessionID。在為客戶端創(chuàng)建會話之前,服務(wù)端首先會為每個客戶端分配一個sessionID,服務(wù)端為客戶端分配的sessionID是全局唯一的。
(2) 注冊會話。向SessionTracker中注冊會話,SessionTracker中維護(hù)了sessionsWithTimeout和sessionsById,在會話創(chuàng)建初期,會將客戶端會話的相關(guān)信息保存到這兩個數(shù)據(jù)結(jié)構(gòu)中。
(3) 激活會話。激活會話涉及Zookeeper會話管理的分桶策略,其核心是為會話安排一個區(qū)塊,以便會話清理程序能夠快速高效地進(jìn)行會話清理。
(4) 生成會話密碼。服務(wù)端在創(chuàng)建一個客戶端會話時,會同時為客戶端生成一個會話密碼,連同sessionID一同發(fā)給客戶端,作為會話在集群中不同機(jī)器間轉(zhuǎn)移的憑證。
3. 預(yù)處理
(1) 將請求交給PrepRequestProcessor處理器處理。在提交給第一個請求處理器之前,Zookeeper會根據(jù)該請求所屬的會話,進(jìn)行一次激活會話操作,以確保當(dāng)前會話處于激活狀態(tài),完成會話激活后,則提交請求至處理器。
(2) 創(chuàng)建請求事務(wù)頭。對于事務(wù)請求,Zookeeper會為其創(chuàng)建請求事務(wù)頭,服務(wù)端后續(xù)的請求處理器都是基于該請求頭來識別當(dāng)前請求是否是事務(wù)請求,請求事務(wù)頭包含了一個事務(wù)請求最基本的一些信息,包括sessionID、ZXID(事務(wù)請求對應(yīng)的事務(wù)ZXID)、CXID(客戶端的操作序列)和請求類型(如create、delete、setData、createSession等)等。
(3) 創(chuàng)建請求事務(wù)體。由于此時是會話創(chuàng)建請求,其事務(wù)體是CreateSessionTxn。
(4) 注冊于激活會話。處理由非Leader服務(wù)器轉(zhuǎn)發(fā)過來的會話創(chuàng)建請求。
4. 事務(wù)處理
(1) 將請求交給ProposalRequestProcessor處理器。與提議相關(guān)的處理器,從ProposalRequestProcessor開始,請求的處理將會進(jìn)入三個子處理流程,分別是Sync流程、Proposal流程、Commit流程。
Sync流程
使用SyncRequestProcessor處理器記錄事務(wù)日志,針對每個事務(wù)請求,都會通過事務(wù)日志的形式將其記錄,完成日志記錄后,每個Follower都會向Leader發(fā)送ACK消息,表明自身完成了事務(wù)日志的記錄,以便Leader統(tǒng)計每個事務(wù)請求的投票情況。
Proposal流程
每個事務(wù)請求都需要集群中過半機(jī)器投票認(rèn)可才能被真正應(yīng)用到內(nèi)存數(shù)據(jù)庫中,這個投票與統(tǒng)計過程就是Proposal流程。
· 發(fā)起投票。若當(dāng)前請求是事務(wù)請求,Leader會發(fā)起一輪事務(wù)投票,在發(fā)起事務(wù)投票之前,會檢查當(dāng)前服務(wù)端的ZXID是否可用。
· 生成提議Proposal。若ZXID可用,Zookeeper會將已創(chuàng)建的請求頭和事務(wù)體以及ZXID和請求本身序列化到Proposal對象中,此Proposal對象就是一個提議。
· 廣播提議。Leader以ZXID作為標(biāo)識,將該提議放入投票箱outstandingProposals中,同時將該提議廣播給所有Follower。
· 收集投票。Follower接收到Leader提議后,進(jìn)入Sync流程進(jìn)行日志記錄,記錄完成后,發(fā)送ACK消息至Leader服務(wù)器,Leader根據(jù)這些ACK消息來統(tǒng)計每個提議的投票情況,當(dāng)一個提議獲得半數(shù)以上投票時,就認(rèn)為該提議通過,進(jìn)入Commit階段。
· 將請求放入toBeApplied隊列中。
· 廣播Commit消息。Leader向Follower和Observer發(fā)送COMMIT消息。向Observer發(fā)送INFORM消息,向Leader發(fā)送ZXID。
Commit流程
· 將請求交付CommitProcessor。CommitProcessor收到請求后,將其放入queuedRequests隊列中。
· 處理queuedRequest隊列請求。CommitProcessor中單獨的線程處理queuedRequests隊列中的請求。
· 標(biāo)記nextPending。若從queuedRequests中取出的是事務(wù)請求,則需要在集群中進(jìn)行投票處理,同時將nextPending標(biāo)記位當(dāng)前請求。
· 等待Proposal投票。在進(jìn)行Commit流程的同時,Leader會生成Proposal并廣播給所有Follower服務(wù)器,此時,Commit流程等待,直到投票結(jié)束。
· 投票通過。若提議獲得過半機(jī)器認(rèn)可,則進(jìn)入請求提交階段,該請求會被放入commitedRequests隊列中,同時喚醒Commit流程。
· 提交請求。若commitedRequests隊列中存在可以提交的請求,那么Commit流程則開始提交請求,將請求放入toProcess隊列中,然后交付下一個請求處理器:FinalRequestProcessor。
5. 事務(wù)應(yīng)用
(1) 交付給FinalRequestProcessor處理器。FinalRequestProcessor處理器檢查outstandingChanges隊列中請求的有效性,若發(fā)現(xiàn)這些請求已經(jīng)落后于當(dāng)前正在處理的請求,那么直接從outstandingChanges隊列中移除。
(2) 事務(wù)應(yīng)用。之前的請求處理僅僅將事務(wù)請求記錄到了事務(wù)日志中,而內(nèi)存數(shù)據(jù)庫中的狀態(tài)尚未改變,因此,需要將事務(wù)變更應(yīng)用到內(nèi)存數(shù)據(jù)庫。
(3) 將事務(wù)請求放入隊列commitProposal。完成事務(wù)應(yīng)用后,則將該請求放入commitProposal隊列中,commitProposal用來保存最近被提交的事務(wù)請求,以便集群間機(jī)器進(jìn)行數(shù)據(jù)的快速同步。
6. 會話響應(yīng)
(1) 統(tǒng)計處理。Zookeeper計算請求在服務(wù)端處理所花費的時間,統(tǒng)計客戶端連接的基本信息,如lastZxid(最新的ZXID)、lastOp(最后一次和服務(wù)端的操作)、lastLatency(最后一次請求處理所花費的時間)等。
(2) 創(chuàng)建響應(yīng)ConnectResponse。會話創(chuàng)建成功后的響應(yīng),包含了當(dāng)前客戶端和服務(wù)端之間的通信協(xié)議版本號、會話超時時間、sessionID和會話密碼。
(3) 序列化ConnectResponse。
(4) I/O層發(fā)送響應(yīng)給客戶端。
2.2 SetData請求
服務(wù)端對于SetData請求大致可以分為四步,預(yù)處理、事務(wù)處理、事務(wù)應(yīng)用、請求響應(yīng)。
1. 預(yù)處理
(1) I/O層接收來自客戶端的請求。
(2) 判斷是否是客戶端"會話創(chuàng)建"請求。對于SetData請求,按照正常事務(wù)請求進(jìn)行處理。
(3) 將請求交給PrepRequestProcessor處理器進(jìn)行處理。
(4) 創(chuàng)建請求事務(wù)頭。
(5) 會話檢查。檢查該會話是否有效。
(6) 反序列化請求,并創(chuàng)建ChangeRecord記錄。反序列化并生成特定的SetDataRequest請求,請求中包含了數(shù)據(jù)節(jié)點路徑path、更新的內(nèi)容data和期望的數(shù)據(jù)節(jié)點版本version。同時根據(jù)請求對應(yīng)的path,Zookeeper生成一個ChangeRecord記錄,并放入outstandingChanges隊列中。
(7) ACL檢查。檢查客戶端是否具有數(shù)據(jù)更新的權(quán)限。
(8) 數(shù)據(jù)版本檢查。通過version屬性來實現(xiàn)樂觀鎖機(jī)制的寫入校驗。
(9) 創(chuàng)建請求事務(wù)體SetDataTxn。
(10) 保存事務(wù)操作到outstandingChanges隊列中。
2. 事務(wù)處理
對于事務(wù)請求,服務(wù)端都會發(fā)起事務(wù)處理流程。所有事務(wù)請求都是由ProposalRequestProcessor處理器處理,通過Sync、Proposal、Commit三個子流程相互協(xié)作完成。
3. 事務(wù)應(yīng)用
(1) 交付給FinalRequestProcessor處理器。
(2) 事務(wù)應(yīng)用。將請求事務(wù)頭和事務(wù)體直接交給內(nèi)存數(shù)據(jù)庫ZKDatabase進(jìn)行事務(wù)應(yīng)用,同時返回ProcessTxnResult對象,包含了數(shù)據(jù)節(jié)點內(nèi)容更新后的stat。
(3) 將事務(wù)請求放入commitProposal隊列。
4. 請求響應(yīng)
(1) 創(chuàng)建響應(yīng)體SetDataResponse。其包含了當(dāng)前數(shù)據(jù)節(jié)點的最新狀態(tài)stat。
(2) 創(chuàng)建響應(yīng)頭。包含當(dāng)前響應(yīng)對應(yīng)的事務(wù)ZXID和請求處理是否成功的標(biāo)識。
(3) 序列化響應(yīng)。
(4) I/O層發(fā)送響應(yīng)給客戶端。
2.3 GetData請求
服務(wù)端對于GetData請求的處理,大致分為三步,預(yù)處理、非事務(wù)處理、請求響應(yīng)。
1. 預(yù)處理
(1) I/O層接收來自客戶端的請求。
(2) 判斷是否是客戶端"會話創(chuàng)建"請求。
(3) 將請求交給PrepRequestProcessor處理器進(jìn)行處理。
(4) 會話檢查。
2. 非事務(wù)處理
(1) 反序列化GetDataRequest請求。
(2) 獲取數(shù)據(jù)節(jié)點。
(3) ACL檢查。
(4) 獲取數(shù)據(jù)內(nèi)容和stat,注冊Watcher。
3. 請求響應(yīng)
(1) 創(chuàng)建響應(yīng)體GetDataResponse。響應(yīng)體包含當(dāng)前數(shù)據(jù)節(jié)點的內(nèi)容和狀態(tài)stat。
(2) 創(chuàng)建響應(yīng)頭。
(3) 統(tǒng)計處理。
(4) 序列化響應(yīng)。
(5) I/O層發(fā)送響應(yīng)給客戶端。
三、總結(jié)
本篇博文講解了Zookeeper服務(wù)端對于客戶端不同請求的處理的具體流程,可能從文字上看步驟會顯得相對枯燥,但是會為之后的源碼分析打下很好的基礎(chǔ),謝謝各位園友的觀看~
總結(jié)
以上是生活随笔為你收集整理的【分布式】Zookeeper请求处理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mybatis表关联彻底理解
- 下一篇: WebHook 与jenkins