根據二)中的分析,如果一台zookeeper服務器成為集群中的leader,那么一定是當前所有服務器中保存數據最多的服務器,所以在這台服務器成為leader之后,首先要做的事情就是與集群中的其它服務器(現在是follower)同步數據,保證大家的數據一致,這個過程完畢了才開始正式處理來自客戶端的連接請求.
首先來看Leader做的工作:
二)中提到的同步數據時使用的邏輯時鍾,它的初始值是0,每次選舉過程都會遞增的,在leader正式上任之后做的第一件事情,就是根據當前保存的數據id值,設置最新的邏輯時鍾值:
long epoch = self.getLastLoggedZxid() >> 32L; epoch++; zk.setZxid(epoch << 32L);
(函數Leader::lead()中)
隨后,leader構建NEWLEADER封包,該封包的數據是當前最大數據的id,廣播給所有的follower,也就是告知follower leader保存的數據id是多少,大家看看是不是需要同步.
然后,leader根據follower數量給每個follower創建一個線程LearnerHandler,專門負責接收它們的同步數據請求.
leader主線程開始阻塞在這里,等待其他follower的回應(也就是LearnerHandler線程的處理結果),同樣的,只有在超過半數的follower已經同步數據完畢,這個過程才能結束,leader才能正式成為leader.
所以其實leader與follower同步數據的大部分操作都在LearnerHandler線程中處理的,接着看這一塊.
leader接收到的來自某個follower封包一定是FOLLOWERINFO,該封包告知了該服務器保存的數據id.之后根據這個數據id與本機保存的數據進行比較:
1) 如果數據完全一致,則發送DIFF封包告知follower當前數據就是最新的了.
2) 判斷這一階段之內有沒有已經被提交的提議值,如果有,那么:
a) 如果有部分數據沒有同步,那么會發送DIFF封包將有差異的數據同步過去.同時將follower沒有的數據逐個發送COMMIT封包給follower要求記錄下來.
b) 如果follower數據id更大,那么會發送TRUNC封包告知截除多余數據.
3) 如果這一階段內沒有提交的提議值,直接發送SNAP封包將快照同步發送給follower.
以上消息完畢之后,發送UPTODATE封包告知follower當前數據就是最新的了,再次發送NEWLEADER封包宣稱自己是leader,等待follower的響應.
其次來看follower做的工作,follower與leader同步數據的操作在函數Follower::followLeader中進行.
首先會嘗試與leader建立連接,這里有一個機制,如果一定時間內沒有連接上,就報錯退出,重新回到選舉狀態.
其次在函數learner::registerWithLeader中發送FOLLOWERINFO封包,該封包中帶上自己的最大數據id,也就是會告知leader本機保存的最大數據id.
最后,根據前面對LeaderHandler的分析,leader會根據不同的情況發送DIFF,UPTODATE,TRUNC,SNAP,依次進行處理就是了,此時follower跟leader的數據也就同步上了.
由於leader端發送的最后一個封包是UPTODATE,因此在接收到這個封包之后follower結束同步數據過程,發送ACK封包回復leader.
以上過程中,任何情況出現的錯誤,服務器將自動將選舉狀態切換到LOOKING狀態,重新開始進行選舉.