寫在前面:
zookeeper源碼比較復(fù)雜,本文講解的重點(diǎn)為各個zookeeper服務(wù)節(jié)點(diǎn)之間的state選舉。至于各個節(jié)點(diǎn)之間的數(shù)據(jù)同步,不在文本的側(cè)重講解范圍內(nèi)。
在沒有對zookeeper組件有一個整體架構(gòu)認(rèn)識的基礎(chǔ)上,不建議直接死磕細(xì)節(jié)。本文寫作的目的也是基于此,閱讀本文,希望讀者能夠?qū)ookeeper集群架構(gòu)有一個簡單的認(rèn)識。
本文附有整體架構(gòu)的執(zhí)行流程圖,請一定要跟著代碼多走幾遍。本文的二-六節(jié)會對該流程中的關(guān)鍵點(diǎn)進(jìn)行講解
文章來源:http://www.zghlxwxcb.cn/news/detail-724213.html
一、前提共識
1、了解zookeeper集群搭建
不了解如何搭建集群的可以參考網(wǎng)上別人的文章:Zookeeper集群搭建及使用
本文需要了解集群相關(guān)的配置為zoo.cfg文件的節(jié)點(diǎn)配置信息:
server.1=第一臺服務(wù)器的主機(jī)名:2881:3881
server.2=第二臺服務(wù)器的主機(jī)名:2882:3882
server.3=第三臺服務(wù)器的主機(jī)名:2883:3882
這里針對每一個zookeeper服務(wù),分別含有288x和388x兩個端口。第一個端口用來同步節(jié)點(diǎn)之間的數(shù)據(jù),第二個端口用來進(jìn)行節(jié)點(diǎn)的state選舉(對應(yīng)代碼為ServerState枚舉類)
在源碼中會new很多的Socket通信,一定要區(qū)分開不同端口的實(shí)例化對象。
2、入口為QuorumPeerMain.main方法
至于為什么直接是這個方法,可以查看對應(yīng)的腳本文件。大部分使用腳本文件進(jìn)行執(zhí)行的,都是同樣的方式(如tomcat)。
如果左邊的sh文件不方便看,可以看右邊的sh文件(畢竟邏輯更簡單)

3、整體流程圖(重點(diǎn))
一定要一定要,跟著流程圖多走幾遍

4、需要有Socket網(wǎng)絡(luò)編程、多線程相關(guān)基礎(chǔ)
源代碼中出現(xiàn)大量的Socket通信,也包含創(chuàng)建大量的多線程代碼。不了解這兩部分知識的小伙伴,看起來可能比較吃力
二、基礎(chǔ)流程
1、入口main

2、流程圖
這部分比較簡單,主要完成的幾件事情可以總結(jié)為:
- 解析配置文件的配置信息
- 初始化zookeeper用于接收客戶端命令(create、delete、get)的服務(wù)端,可選擇Netty或者NIO方式,并且完成對應(yīng)的服務(wù)端的啟動
- 設(shè)置對應(yīng)的集群leader選舉的選舉類型(設(shè)置為3,未來會在Switch中用到該值)
- 啟動一個內(nèi)嵌的jetty服務(wù)器,用來查看未來服務(wù)端的信息
- 初始化對應(yīng)的多級隊列架構(gòu)
- 進(jìn)行具體的集群leader節(jié)點(diǎn)的選舉
三、多級隊列構(gòu)建流程
1、多級隊列架構(gòu)講解
看不懂這個圖的,建議先把源碼跟一遍,再回頭看
首先看一下多級隊列架構(gòu)圖

我們可以將該多級隊列可以分為兩層:
第一層:應(yīng)用層,可以理解為我們上層api隊列
代表隊列:sendqueue、recvqueue
對應(yīng)線程名稱:WorkerSender、WorkerReceiver
第二層:傳輸層,可以理解為后臺傳送數(shù)據(jù)的隊列
代表隊列:recvQueue、queueSendMap(map的value為傳輸層隊列)、senderWorkerMap(map的value為SendWorker線程)
對應(yīng)線程名稱:SendWorker、RecvWorker
牢記上面這幾個隊列、map、線程的名字。否則代碼你會看不清楚
處理多級隊列流程大致可以描述為
1、首先會初始化對應(yīng)的ServerSocket端,同時進(jìn)行accept阻塞監(jiān)聽數(shù)據(jù);
2、如果處理leader節(jié)點(diǎn)對應(yīng)的端口接收到數(shù)據(jù),那么就會處理該請求。剛開始初始化的時候是不會有數(shù)據(jù)的,畢竟Socket客戶端都還沒有開始建立,在第10步才建立的;
3、然后會對信息中的sid進(jìn)行判斷,然后剔除掉不需要的socket連接。因為socket通信是全體連接都發(fā)一遍,zk的判定規(guī)則為只能由sid大的發(fā)給sid小的,所以如果小的發(fā)送給了大的,就會關(guān)閉連接,并且開啟一個反向連接;
4、如果是小的發(fā)送給大的,此時就會初始化出我們多級隊列的第二級隊列傳輸層隊列,以及對應(yīng)的SendWorker線程任務(wù)。然后把這個SendWorker任務(wù)放入senderWorkerMap中,未來可以直接取出來直接使用;
5、這里的SendWorker任務(wù),就是真實(shí)的把數(shù)據(jù)通過Socket請求把數(shù)據(jù)發(fā)送給遠(yuǎn)端的邏輯;
6、接下來開始處理第一級隊列應(yīng)用層隊列;
7、即初始化sendqueue和recvqueue這兩個上層傳輸隊列;
8、然后就是初始化對應(yīng)的Socket客戶端。當(dāng)然在初始化客戶端的前提是,我們有業(yè)務(wù)數(shù)據(jù)放到了第一級的隊列中,即第7步中的隊列中要有數(shù)據(jù);
9、上層業(yè)務(wù)隊列有數(shù)據(jù)以后,會將業(yè)務(wù)數(shù)據(jù)封裝為一個ByteBuffer類型的requestBuffer對象,然后把數(shù)據(jù)直接發(fā)送給傳輸層的隊列(queueSendMap中對應(yīng)sid的value對應(yīng)的隊列);
10、輸入發(fā)送后,初始化對應(yīng)的Socket客戶端連接,這樣就能夠把數(shù)據(jù)發(fā)送出去,與此同時,第2步的accept也能接收到數(shù)據(jù),最終形成兩級隊列工作工作的閉環(huán)。
第10步使用Socket發(fā)送的數(shù)據(jù),會與第2步的ServerSocket連接打通。一定要明確這是在集群環(huán)境下的通信,明白這一步,很重要
與之對應(yīng)的recvqueue只是邏輯反一下,首先RecvWorker線程執(zhí)行會獲取到數(shù)據(jù),發(fā)送給一級隊列應(yīng)用層隊列recvqueue,一級隊列對應(yīng)的WQorkRecevier線程任務(wù),就會執(zhí)行對應(yīng)的邏輯,然后處理數(shù)據(jù)。
2、入口startLeaderElection


3、初始化二級隊列
1)Listener方法內(nèi)部
- 綁定服務(wù)端信息
- accept數(shù)據(jù)
- 處理數(shù)據(jù)

2)根據(jù)接收到的信息的sid情況進(jìn)行不同的邏輯
- 如果是自己的就發(fā)給自己
- 如果不是自己,并且滿足zk的sid由大機(jī)器發(fā)送給小機(jī)器的規(guī)則,則開啟對應(yīng)的兩個用于處理二級隊列(傳輸層隊列)的線程任務(wù)

3)以SendWorker線程任務(wù)為例
- queueSendMap中獲取對應(yīng)sid的隊列,然后獲取應(yīng)用層隊列中的數(shù)據(jù),并將其發(fā)送給傳輸層隊列
- 最終調(diào)用send方法,完成數(shù)據(jù)的傳輸


4、初始化一級隊列
1)完成一級隊列的初始化

2)完成一級隊列對應(yīng)的線程任務(wù)的初始化

3)調(diào)用對應(yīng)線程的run方法,然后調(diào)用process方法處理數(shù)據(jù)

4)把數(shù)據(jù)發(fā)送給運(yùn)輸層隊列

5)建立socket客戶端連接
- 首先調(diào)用內(nèi)部的connetOne方法
- 然后把數(shù)據(jù)封裝為一個任務(wù),放進(jìn)線程池
- 在線程任務(wù)中會初始化對應(yīng)的Socket客戶端連接
- 封裝數(shù)據(jù)寫入到輸出流。此時二級隊列初始化出來的阻塞的ServerSocket就能夠開始向下執(zhí)行,處理接收到的數(shù)據(jù),形成閉環(huán)

將線程任務(wù)放進(jìn)線程池代碼

該任務(wù)會初始化對應(yīng)的客戶端

封裝對應(yīng)的輸出數(shù)據(jù),并完成發(fā)送

四、LOOKING節(jié)點(diǎn)選舉流程
1、節(jié)點(diǎn)選舉基礎(chǔ)流程講解

流程選舉大致流程可以描述為:
1、第一輪選票:myid為1和myid為2的兩臺機(jī)器,分別把自己的選票發(fā)送出去(vote形式),兩臺機(jī)器會分別對自己收到的選票和自己的選票進(jìn)行比較。會根據(jù)指定的判斷規(guī)則進(jìn)行選擇(粗略可以理解為,是周期、zxid和機(jī)器id幾個要素中大的);
2、myid為1的機(jī)器收到(2,0),會和自己的選票(1,0)進(jìn)行比較,發(fā)現(xiàn)還是(2,0)大,所以最終它會把(2,0)返回回去。此輪中,兩臺機(jī)器發(fā)送的選票都沒有超過半數(shù)(一共3臺服務(wù),配置文件中能獲?。?;
3、第二輪選票:myid為1和myid為2的兩臺機(jī)器,再次發(fā)送自己的選票信息。此時由于myid為1的這臺機(jī)器發(fā)送的就是上一輪接收到的(2,0)選票。那么在第二輪投票時myid為1的機(jī)器就會投出(2,0),與此同時,myid為2的機(jī)器投出的選票也是(2,0);
4、此輪中投出的選票(2,0)超過了半數(shù)(2/3)。最終myid為2的這臺機(jī)器被選為了leader節(jié)點(diǎn);
5、當(dāng)myid為3的節(jié)點(diǎn)進(jìn)來的時候,雖然它的(3,0)大于(2,0),但是其周期比較?。▍⑴c投票的周期次數(shù)),所以myid為2的機(jī)器依然還是leader節(jié)點(diǎn)。
2、LOOKING節(jié)點(diǎn)流程
每一個節(jié)點(diǎn)進(jìn)來以后的第一步邏輯就是這個位置

3、投票PK邏輯源碼
1)首先會把自己的投票信息發(fā)送出去,然后再獲取自己的recvqueue隊列(傳輸層隊列,具體邏輯在多級隊列章節(jié)進(jìn)行過講解)中的投票信息,只要滿足條件,就可以不斷的去獲取隊列中的數(shù)據(jù)

2)緊接著對接收到的選票進(jìn)行邏輯判斷。根據(jù)其不同的state走不同的邏輯,最終根據(jù)判斷邏輯再次把消息發(fā)送出去

3)這是很重要的一個判斷規(guī)則,根據(jù)指定的要素進(jìn)行判定兩個選票的規(guī)則

4)會調(diào)用該方法,最終走到其半數(shù)判定的條件。最終返回選擇出來的leader節(jié)點(diǎn),即endVote對象,如果沒有選擇到leader節(jié)點(diǎn),則返回null


4、其余節(jié)點(diǎn)
LOOKING節(jié)點(diǎn)的流程為主要邏輯,在第一輪的while循環(huán)過后,就能夠明確每種節(jié)點(diǎn)對應(yīng)的類型是什么,第二次while循環(huán)的時候,就會走到對應(yīng)的節(jié)點(diǎn)方法中去。詳細(xì)的執(zhí)行邏輯可請參考開頭的那張流程圖

五、Zookeeper之間數(shù)據(jù)同步
這里就回到了第一節(jié)中的,第一個共識。即配置集群的時候是有兩個端口,最后面的端口,是用于集群節(jié)點(diǎn)的選舉,倒數(shù)第二個端口則是用于同步Zookeeper節(jié)點(diǎn)之間的數(shù)據(jù)。
既然出現(xiàn)了新的端口通信,那么就會初始化新的服務(wù)端
1、初始化LEADING節(jié)點(diǎn)

2、初始化新端口的服務(wù)端
這里就會根據(jù)新的配置,初始化新的服務(wù)端,用來接收zookeeper客戶端之間的命令

六、LEADING節(jié)點(diǎn)掛之后流程
1、LEADING節(jié)點(diǎn)會不斷發(fā)送PING命令
1)如果是leader節(jié)點(diǎn)則會走這段邏輯

2)在一個while死循環(huán)內(nèi)部,會周期性的給learner發(fā)送ping數(shù)據(jù)

3)封裝對應(yīng)的PING數(shù)據(jù)包

2、FOLLOWING節(jié)點(diǎn)不斷接收命令
1)只要機(jī)器還運(yùn)行著,就會輪循該方法,節(jié)點(diǎn)是FOLLOWING的機(jī)器就會調(diào)用follwLeader方法

2)該方法中會調(diào)用readPacket方法,去接收數(shù)據(jù)。后面的processPacket方法會對接受到的數(shù)據(jù)進(jìn)行處理。在這過程中就會出現(xiàn)獲取不到數(shù)據(jù),然后拋異常。那么就會跳轉(zhuǎn)到上一步,在updateServerState()方法中,就會根據(jù)邏輯,把自己的FOLLWING節(jié)點(diǎn)設(shè)置為LOOKING節(jié)點(diǎn),繼而觸發(fā)后續(xù)的再次選舉流程

至此Zookeeper中Leader節(jié)點(diǎn)選取的流程就告一段落。開頭那張整體的流程圖,一定要多跟著源碼多走幾遍。文章來源地址http://www.zghlxwxcb.cn/news/detail-724213.html
到了這里,關(guān)于淺談Zookeeper集群選舉Leader節(jié)點(diǎn)源碼的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!