-
- 參考:https://www.bilibili.com/video/BV1tt41137sT?p=4
- 視頻中帶註釋的源碼:https://github.com/boomblog/zookeeper-vip1/blob/master/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java
- 啓動
- 傳參
- 腳本啓動的時候 .sh 發現調用了一個類 傳入配置文件參數 就啓動了
- 我們可以通過idea 加入參數,也能啓動。
- 找夥伴
- 一開始先啓動server1,server1會嘗試連接server2和server3,結果都失敗,然後會重試
- 然後我們來個客戶端連接的話,因爲沒有leader,會連上就斷開連上就斷開,不斷重試,直到有leader。
- 加載數據
- 讀入我們的配置
- 從硬盤中把持久化的內容讀到內存裏
- 開啓socket : cnxnFactory.start()
- 使用NIO
- 開啓領導選舉
- 選舉傳輸層(負責傳輸選票)QuorumCnxManager
- 每個服務器要向其他服務器發送選票
- 每個服務器對應一個隊列 queueSendMap,裏面放發給這個服務器的選票
- 每個服務器要從其他服務器接收選票
- ArrayBlockingQueue<Message> recvQueue
- 每個服務器要向其他服務器發送選票
- 創建個listener,接收別的小夥伴的請求(創建個新線程)
- 選舉應用層(負責pk等邏輯操作)
https://mp.weixin.qq.com/s/z73f6rQXYvh2byfkO8tMoA? 備註下,這兩層的示意圖- 這一層也有自己的sendQueue和recvQueue
- 選舉傳輸層(負責傳輸選票)QuorumCnxManager
- 真正開始選舉:super.start() 就是 QuorumPeer.run(): FastLeaderElection
- 先投給自己
- 接受其他小夥伴的選票
- 如果拿不到別人的選票(取隊列爲空),纔會去建立連接(connectAll)
- 如果兩個server 相互主動連接對方,怎麼辦?
- 拿到對方的id,和自己的id比較,如果對方的大,關掉連接。
- 所以永遠是小的做爲server端,大的作爲客戶端建立連接。
- 如果兩個server 相互主動連接對方,怎麼辦?
- 如果拿不到別人的選票(取隊列爲空),纔會去建立連接(connectAll)
- pk
- 排序順序:epoch zxid server_id
- 選票其實也是長這樣(epoch, zxid, server_id)
- 投票
- 從server n接收到一個選票,將其更新成贏了的的選票(可能是他,也可能是我自己)放進以server_id爲key的map中(投票箱)
- 統計
- 統計過半數
- 傳參
- 跟上面的流程對應的代碼:
- org.apache.zookeeper.server.quorum.QuorumPeerMain#runFromConfig
- quorumPeer.start();開啓線程
- org.apache.zookeeper.server.quorum.QuorumPeer#run。MainLoop,根據status做不同的事情
- case LOOKING(剛啓動就是這個,正在尋找老大)
- org.apache.zookeeper.server.quorum.QuorumPeer#startLeaderElection
- currentVote = new Vote(myid, getLastLoggedZxid(), getCurrentEpoch());
先投自己
- this.electionAlg = createElectionAlgorithm(electionType);
默認是用3:fastElection。然後會啓動兩個線程,瘋狂從兩個隊列(WorkerSender、WorkerReceiver)poll- WorkerSender:就是把選票發出去
- WorkerReceiver:接受選票並處理
- currentVote = new Vote(myid, getLastLoggedZxid(), getCurrentEpoch());
- setCurrentVote(makeLEStrategy().lookForLeader());
看下要不要改票。/** * Starts a new round of leader election. Whenever our QuorumPeer changes its state to LOOKING, this method is invoked, and it sends notifications to all other peers. */開始新的一輪選舉,當QuorumPeer改變狀態爲LOOKING,就會調用這個方法,然後會發通知給所有其他小夥伴- HashMap<Long, Vote> recvset
收到的選票,也就是本地投票箱
- HashMap<Long, Vote> outofelection
- 選票上寫上自己
- sendNotifications(); 選票發送。實際上是填好選票放到發送隊列裏。別的線程來負責發送。
- while ((self.getPeerState() == ServerState.LOOKING) &&(!stop))
只要還在LOOKING狀態,而且程序沒有停止,就一直循環下面邏輯:- Notification n = recvqueue.poll
接收隊列拖出一個通知
- 如果是空就繼續發通知sendNotification
- 如果有一個隊列都清空了(haveDelivered)(queueSendMap),就再sendNotification【sendqueue(應用層,只有一個隊列)和queueSendMap(傳輸層,分不同的小夥伴有不同的隊列)】
- 否則,嘗試連接所有其他小夥伴
- 如果收到有效選票
- switch (n.state)
- case LOOKING:
收到的選票也在LOOKING- 如果對方的epoch比較大
- 那就更新自己存的
- 清空收到的選票recvset
- 按照規則排序,對方選票牛皮則跟着對方投,否則還按自己的投
epoch > zxid > serverid
- 如果對方的epoch比自己小,可能是莫名其妙哪出問題了,打debug日誌然後break循環
- 對方的epoch跟我一樣
- 那也按照規則排序,誰牛皮就按誰的投
- 把對方的選票放進投票箱recvset
- 如果termPredicate返回true:從recvqueue中清空選票,只要找到一張更牛皮的選票就把票放回去繼續循環。如果是真的隊列空了,就結束了,看看是不是自己當了老大,是的話就把state改成LEADING,否則改成FOLLOWING
/** * Termination predicate. Given a set of votes, determines if have * sufficient to declare the end of the election round. 瞅一眼投票箱,看看是不是足夠宣告投票結束。- 其中一種校驗方法是獲得過半數的選票
- 如果對方的epoch比較大
- case FOLLOWING ||LEADING:
- case LOOKING:
- Notification n = recvqueue.poll
- HashMap<Long, Vote> recvset
- org.apache.zookeeper.server.quorum.QuorumPeer#startLeaderElection
- case FOLLOWING
- case LEADING
- case LOOKING(剛啓動就是這個,正在尋找老大)
- org.apache.zookeeper.server.quorum.QuorumPeer#run。MainLoop,根據status做不同的事情
- quorumPeer.start();開啓線程
ZooKeeper 選舉過程 源碼
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.