http://www.cnblogs.com/leesf456/p/6140503.html
一、前言
在前面學習了Zookeeper中服務器的三種角色及其之間的通信,接著學習對于客戶端的一次請求,Zookeeper是如何進行處理的。
二、請求處理
2.1 會話創建請求
Zookeeper服務端對于會話創建的處理,大體可以分為請求接收、會話創建、預處理、事務處理、事務應用和會話響應六大環節,其大體流程如

1. 請求接收
(1) I/O層接收來自客戶端的請求。NIOServerCnxn維護每一個客戶端連接,客戶端與服務器端的所有通信都是由NIOServerCnxn負責,其負責統一接收來自客戶端的所有請求,并將請求內容從底層網絡I/O中完整地讀取出來。
(2) 判斷是否是客戶端會話創建請求。每個會話對應一個NIOServerCnxn實體,對于每個請求,Zookeeper都會檢查當前NIOServerCnxn實體是否已經被初始化,如果尚未被初始化,那么就可以確定該客戶端一定是會話創建請求。
(3) 反序列化ConnectRequest請求。一旦確定客戶端請求是否是會話創建請求,那么服務端就可以對其進行反序列化,并生成一個ConnectRequest載體。
(4) 判斷是否是ReadOnly客戶端。如果當前Zookeeper服務器是以ReadOnly模式啟動,那么所有來自非ReadOnly型客戶端的請求將無法被處理。因此,服務端需要先檢查是否是ReadOnly客戶端,并以此來決定是否接受該會話創建請求。
(5) 檢查客戶端ZXID。正常情況下,在一個Zookeeper集群中,服務端的ZXID必定大于客戶端的ZXID,因此若發現客戶端的ZXID大于服務端ZXID,那么服務端不接受該客戶端的會話創建請求。
(6) 協商sessionTimeout。在客戶端向服務器發送超時時間后,服務器會根據自己的超時時間限制最終確定該會話超時時間,這個過程就是sessionTimeout協商過程。
(7) 判斷是否需要重新激活創建會話。服務端根據客戶端請求中是否包含sessionID來判斷該客戶端是否需要重新創建會話,若客戶單請求中包含sessionID,那么就認為該客戶端正在進行會話重連,這種情況下,服務端只需要重新打開這個會話,否則需要重新創建。
2. 會話創建
(1) 為客戶端生成sessionID。在為客戶端創建會話之前,服務端首先會為每個客戶端分配一個sessionID,服務端為客戶端分配的sessionID是全局唯一的。
(2) 注冊會話。向SessionTracker中注冊會話,SessionTracker中維護了sessionsWithTimeout和sessionsById,在會話創建初期,會將客戶端會話的相關信息保存到這兩個數據結構中。
(3) 激活會話。激活會話涉及Zookeeper會話管理的分桶策略,其核心是為會話安排一個區塊,以便會話清理程序能夠快速高效地進行會話清理。
(4) 生成會話密碼。服務端在創建一個客戶端會話時,會同時為客戶端生成一個會話密碼,連同sessionID一同發給客戶端,作為會話在集群中不同機器間轉移的憑證。
3. 預處理
(1) 將請求交給PrepRequestProcessor處理器處理。在提交給第一個請求處理器之前,Zookeeper會根據該請求所屬的會話,進行一次激活會話操作,以確保當前會話處于激活狀態,完成會話激活后,則提交請求至處理器。
(2) 創建請求事務頭。對于事務請求,Zookeeper會為其創建請求事務頭,服務端后續的請求處理器都是基于該請求頭來識別當前請求是否是事務請求,請求事務頭包含了一個事務請求最基本的一些信息,包括sessionID、ZXID(事務請求對應的事務ZXID)、CXID(客戶端的操作序列)和請求類型(如create、delete、setData、createSession等)等。
(3) 創建請求事務體。由于此時是會話創建請求,其事務體是CreateSessionTxn。
(4) 注冊于激活會話。處理由非Leader服務器轉發過來的會話創建請求。
4. 事務處理
(1) 將請求交給ProposalRequestProcessor處理器。與提議相關的處理器,從ProposalRequestProcessor開始,請求的處理將會進入三個子處理流程,分別是Sync流程、Proposal流程、Commit流程。

Sync流程
使用SyncRequestProcessor處理器記錄事務日志,針對每個事務請求,都會通過事務日志的形式將其記錄,完成日志記錄后,每個Follower都會向Leader發送ACK消息,表明自身完成了事務日志的記錄,以便Leader統計每個事務請求的投票情況。
Proposal流程
每個事務請求都需要集群中過半機器投票認可才能被真正應用到內存數據庫中,這個投票與統計過程就是Proposal流程。
· 發起投票。若當前請求是事務請求,Leader會發起一輪事務投票,在發起事務投票之前,會檢查當前服務端的ZXID是否可用。
· 生成提議Proposal。若ZXID可用,Zookeeper會將已創建的請求頭和事務體以及ZXID和請求本身序列化到Proposal對象中,此Proposal對象就是一個提議。
· 廣播提議。Leader以ZXID作為標識,將該提議放入投票箱outstandingProposals中,同時將該提議廣播給所有Follower。
· 收集投票。Follower接收到Leader提議后,進入Sync流程進行日志記錄,記錄完成后,發送ACK消息至Leader服務器,Leader根據這些ACK消息來統計每個提議的投票情況,當一個提議獲得半數以上投票時,就認為該提議通過,進入Commit階段。
· 將請求放入toBeApplied隊列中。
· 廣播Commit消息。Leader向Follower和Observer發送COMMIT消息。向Observer發送INFORM消息,向Leader發送ZXID。
Commit流程
· 將請求交付CommitProcessor。CommitProcessor收到請求后,將其放入queuedRequests隊列中。
· 處理queuedRequest隊列請求。CommitProcessor中單獨的線程處理queuedRequests隊列中的請求。
· 標記nextPending。若從queuedRequests中取出的是事務請求,則需要在集群中進行投票處理,同時將nextPending標記位當前請求。
· 等待Proposal投票。在進行Commit流程的同時,Leader會生成Proposal并廣播給所有Follower服務器,此時,Commit流程等待,直到投票結束。
· 投票通過。若提議獲得過半機器認可,則進入請求提交階段,該請求會被放入commitedRequests隊列中,同時喚醒Commit流程。
· 提交請求。若commitedRequests隊列中存在可以提交的請求,那么Commit流程則開始提交請求,將請求放入toProcess隊列中,然后交付下一個請求處理器:FinalRequestProcessor。
5. 事務應用
(1) 交付給FinalRequestProcessor處理器。FinalRequestProcessor處理器檢查outstandingChanges隊列中請求的有效性,若發現這些請求已經落后于當前正在處理的請求,那么直接從outstandingChanges隊列中移除。
(2) 事務應用。之前的請求處理僅僅將事務請求記錄到了事務日志中,而內存數據庫中的狀態尚未改變,因此,需要將事務變更應用到內存數據庫。
(3) 將事務請求放入隊列commitProposal。完成事務應用后,則將該請求放入commitProposal隊列中,commitProposal用來保存最近被提交的事務請求,以便集群間機器進行數據的快速同步。
6. 會話響應
(1) 統計處理。Zookeeper計算請求在服務端處理所花費的時間,統計客戶端連接的基本信息,如lastZxid(最新的ZXID)、lastOp(最后一次和服務端的操作)、lastLatency(最后一次請求處理所花費的時間)等。
(2) 創建響應ConnectResponse。會話創建成功后的響應,包含了當前客戶端和服務端之間的通信協議版本號、會話超時時間、sessionID和會話密碼。
(3) 序列化ConnectResponse。
(4) I/O層發送響應給客戶端。
2.2 SetData請求
服務端對于SetData請求大致可以分為四步,預處理、事務處理、事務應用、請求響應。
1. 預處理
(1) I/O層接收來自客戶端的請求。
(2) 判斷是否是客戶端"會話創建"請求。對于SetData請求,按照正常事務請求進行處理。
(3) 將請求交給PrepRequestProcessor處理器進行處理。
(4) 創建請求事務頭。
(5) 會話檢查。檢查該會話是否有效。
(6) 反序列化請求,并創建ChangeRecord記錄。反序列化并生成特定的SetDataRequest請求,請求中包含了數據節點路徑path、更新的內容data和期望的數據節點版本version。同時根據請求對應的path,Zookeeper生成一個ChangeRecord記錄,并放入outstandingChanges隊列中。
(7) ACL檢查。檢查客戶端是否具有數據更新的權限。
(8) 數據版本檢查。通過version屬性來實現樂觀鎖機制的寫入校驗。
(9) 創建請求事務體SetDataTxn。
(10) 保存事務操作到outstandingChanges隊列中。
2. 事務處理
對于事務請求,服務端都會發起事務處理流程。所有事務請求都是由ProposalRequestProcessor處理器處理,通過Sync、Proposal、Commit三個子流程相互協作完成。
3. 事務應用
(1) 交付給FinalRequestProcessor處理器。
(2) 事務應用。將請求事務頭和事務體直接交給內存數據庫ZKDatabase進行事務應用,同時返回ProcessTxnResult對象,包含了數據節點內容更新后的stat。
(3) 將事務請求放入commitProposal隊列。
4. 請求響應
(1) 創建響應體SetDataResponse。其包含了當前數據節點的最新狀態stat。
(2) 創建響應頭。包含當前響應對應的事務ZXID和請求處理是否成功的標識。
(3) 序列化響應。
(4) I/O層發送響應給客戶端。
2.3 GetData請求
服務端對于GetData請求的處理,大致分為三步,預處理、非事務處理、請求響應。
1. 預處理
(1) I/O層接收來自客戶端的請求。
(2) 判斷是否是客戶端"會話創建"請求。
(3) 將請求交給PrepRequestProcessor處理器進行處理。
(4) 會話檢查。
2. 非事務處理
(1) 反序列化GetDataRequest請求。
(2) 獲取數據節點。
(3) ACL檢查。
(4) 獲取數據內容和stat,注冊Watcher。
3. 請求響應
(1) 創建響應體GetDataResponse。響應體包含當前數據節點的內容和狀態stat。
(2) 創建響應頭。
(3) 統計處理。
(4) 序列化響應。
(5) I/O層發送響應給客戶端。
三、總結
本篇博文講解了Zookeeper服務端對于客戶端不同請求的處理的具體流程,可能從文字上看步驟會顯得相對枯燥,但是會為之后的源碼分析打下很好的基礎,謝謝各位園友的觀看~