ZooKeeper的由來算法
主要是解決分佈式環境下的服務協調問題而產生的,實現ZooKeeper須要作什麼?服務器
2PC提交網絡
ZooKeeper採起的是類2PC的策略數據結構
ZAB協議tcp
消息廣播是一個簡化版本的2PC:分佈式
崩潰恢復模式須要解決的問題源碼分析
崩潰恢復模式須要知足:server
leader選舉中間件
leader選舉源碼分析blog
集羣中已經存在Leader
集羣中不存在Leader
對於集羣中已經存在Leader而言,此種狀況通常都是某臺機器啓動得較晚,在其啓動以前,集羣已經在正常工做,對這種狀況,該機器試圖去選舉Leader時,會被告知當前服務器的Leader信息,對於該機器而言,僅僅須要和Leader機器創建起鏈接,並進行狀態同步便可。
Leader選舉實現細節
id:被推舉的Leader的SID。
zxid:被推舉的Leader事務ID。
electionEpoch:邏輯時鐘,用來判斷多個投票是否在同一輪選舉週期中,
該值在服務端是一個自增序列,每次進入新一輪的投票後,都會對該值進行加1操做。
peerEpoch:被推舉的Leader的epoch。
state:當前服務器的狀態。
QuorumCnxManager:網絡I/O
每臺服務器在啓動的過程當中,會啓動一個QuorumPeerManager,
負責各臺服務器之間的底層Leader選舉過程當中的網絡通訊。
(1)消息隊列。
QuorumCnxManager內部維護了一系列的隊列,用來保存接收到的、待發送的消息以及消息的發送器,除接收隊列之外,其餘隊列都按照SID分組造成隊列集合
如一個集羣中除了自身還有3臺機器,那麼就會爲這3臺機器分別建立一個發送隊列,互不干擾。
· recvQueue:消息接收隊列,用於存放那些從其餘服務器接收到的消息。
· queueSendMap:消息發送隊列,用於保存那些待發送的消息,按照SID進行分組。
· senderWorkerMap:發送器集合,每一個SenderWorker消息發送器,都對應一臺遠程Zookeeper服務器,負責消息的發送,也按照SID進行分組。
· lastMessageSent:最近發送過的消息,爲每一個SID保留最近發送過的一個消息。
(2) 創建鏈接。
爲了可以相互投票,Zookeeper集羣中的全部機器都須要兩兩創建起網絡鏈接。
QuorumCnxManager在啓動時會建立一個ServerSocket來監聽Leader選舉的通訊端口(默認爲3888)。
爲了不兩臺機器之間重複地建立TCP鏈接,Zookeeper只容許SID大的服務器主動和其餘機器創建鏈接,不然斷開鏈接。
一旦鏈接創建,就會根據遠程服務器的SID來建立相應的消息發送器SendWorker和消息接收器RecvWorker,並啓動。
(3) 消息接收與發送。
消息接收:由消息接收器RecvWorker負責,因爲Zookeeper爲每一個遠程服務器都分配一個單獨的RecvWorker,
所以,每一個RecvWorker只須要不斷地從這個TCP鏈接中讀取消息,並將其保存到recvQueue隊列中。
消息發送:因爲Zookeeper爲每一個遠程服務器都分配一個單獨的SendWorker,
所以,每一個SendWorker只須要不斷地從對應的消息發送隊列中獲取出一個消息發送便可,同時將這個消息放入lastMessageSent中。
FastLeaderElection:選舉算法核心:
· 外部投票:特指其餘服務器發來的投票。
· 內部投票:服務器自身當前的投票。
· 選舉輪次:Zookeeper服務器Leader選舉的輪次,即logicalclock。
· PK:對內部投票和外部投票進行對比來肯定是否須要變動內部投票。
(1)選票管理
· sendqueue:選票發送隊列,用於保存待發送的選票。
· recvqueue:選票接收隊列,用於保存接收到的外部投票。
· WorkerReceiver:選票接收器。其會不斷地從QuorumCnxManager中獲取其餘服務器發來的選舉消息,並將其轉換成一個選票,而後保存到recvqueue中,在選票接收過程當中,若是發現該外部選票的選舉輪次小於當前服務器的,那麼忽略該外部投票,同時當即發送本身的內部投票。
· WorkerSender:選票發送器,不斷地從sendqueue中獲取待發送的選票,並將其傳遞到底層QuorumCnxManager中。
(2) 算法核心
FastLeaderElection模塊是如何與底層網絡I/O進行交互
Leader選舉的基本流程以下:
1. 自增選舉輪次。
Zookeeper規定全部有效的投票都必須在同一輪次中,在開始新一輪投票時,會首先對logicalclock進行自增操做。
2. 初始化選票。
在開始進行新一輪投票以前,每一個服務器都會初始化自身的選票,而且在初始化階段,每臺服務器都會將本身推舉爲Leader。
3. 發送初始化選票。
完成選票的初始化後,服務器就會發起第一次投票。
Zookeeper會將剛剛初始化好的選票放入sendqueue中,由發送器WorkerSender負責發送出去。
4. 接收外部投票。
每臺服務器會不斷地從recvqueue隊列中獲取外部選票。
若是服務器發現沒法獲取到任何外部投票,那麼就會當即確認本身是否和集羣中其餘服務器保持着有效的鏈接,
若是沒有鏈接,則立刻創建鏈接,若是已經創建了鏈接,則再次發送本身當前的內部投票。
5. 判斷選舉輪次。
在發送完初始化選票以後,接着開始處理外部投票。在處理外部投票時,會根據選舉輪次來進行不一樣的處理。
· 外部投票的選舉輪次大於內部投票。若服務器自身的選舉輪次落後於該外部投票對應服務器的選舉輪次,那麼就會當即更新本身的選舉輪次(logicalclock),而且清空全部已經收到的投票,而後使用初始化的投票來進行PK以肯定是否變動內部投票。最終再將內部投票發送出去。
· 外部投票的選舉輪次小於內部投票。若服務器接收的外選票的選舉輪次落後於自身的選舉輪次,那麼Zookeeper就會直接忽略該外部投票,不作任何處理,並返回步驟4。
· 外部投票的選舉輪次等於內部投票。此時能夠開始進行選票PK。
6. 選票PK。
在進行選票PK時,符合任意一個條件就須要變動投票。
· 若外部投票中推舉的Leader服務器的選舉輪次大於內部投票,那麼須要變動投票。
· 若選舉輪次一致,那麼就對比二者的ZXID,若外部投票的ZXID大,那麼須要變動投票。
· 若二者的ZXID一致,那麼就對比二者的SID,若外部投票的SID大,那麼就須要變動投票。
7. 變動投票。
通過PK後,若肯定了外部投票優於內部投票,那麼就變動投票,即便用外部投票的選票信息來覆蓋內部投票,變動完成後,再次將這個變動後的內部投票發送出去。
8. 選票歸檔。
不管是否變動了投票,都會將剛剛收到的那份外部投票放入選票集合recvset中進行歸檔。recvset用於記錄當前服務器在本輪次的Leader選舉中收到的全部外部投票(按照服務隊的SID區別,如{(1, vote1), (2, vote2)...})。
9. 統計投票。
完成選票歸檔後,就能夠開始統計投票,統計投票是爲了統計集羣中是否已經有過半的服務器承認了當前的內部投票,若是肯定已經有過半服務器承認了該投票,則終止投票。不然返回步驟4。
10. 更新服務器狀態。
若已經肯定能夠終止投票,那麼就開始更新服務器狀態,服務器首選判斷當前被過半服務器承認的投票所對應的Leader服務器是不是本身,如果本身,則將本身的服務器狀態更新爲LEADING,若不是,則根據具體狀況來肯定本身是FOLLOWING或是OBSERVING。
以上10個步驟就是FastLeaderElection的核心,其中步驟4-9會通過幾輪循環,直到有Leader選舉產生。