從零開始的高併發(三)--- Zookeeper集羣的搭建和leader選舉

前言

① 前情摘要

上一篇 咱們提到了基於zookeeper下的分佈式鎖的簡單實現,咱們分別經過節點不可重名+watch機制(不推薦),取號 + 最小號取lock + watch的原理來各實現了一把分佈式鎖,第二種相似於去銀行辦理業務的先領號,等叫號的一種形式。java

咱們如今已經知道,zookeeper可以幫助咱們解決的是分佈式下應用進程間的協同問題,它簡單,有序,可複製且快速(基礎篇一 的2 - ④ 小節),核心有數據模型,會話機制和watch監聽機制(基礎篇一 的3 - ① ~ ④ 小節),zookeeper還有其對應的豐富的第三方客戶端方便咱們進行開發,在至此zookeeper入門的前兩篇就告一段落了。linux

② 入門篇的連接:

從零開始的高併發(一)--- zookeeper的基礎概念算法

從零開始的高併發(二)--- Zookeeper實現分佈式鎖數據庫

內容1:Zookeeper集羣的簡單搭建

本來應該和leader選舉分P說的,可是由於集羣搭建過程相對不復雜,主要的內容其實仍是paxos算法的那塊,因此咱們就合成1P來講吧,內容看起來會較多,實際上是截圖比較多而已,但願你們可以耐心安全

① 爲何咱們須要zookeeper集羣

剛剛咱們所提到的,zookeeper的可複製性就是zookeeper集羣的特色,爲了提供可靠的zookeeper服務,咱們須要集羣的支持,這個集羣還有個特色,就是這個集羣中只要有大多數的節點準備好了,就可使用這項服務。容錯集羣設置至少須要3臺服務器,強烈建議使用奇數個服務器(爲啥推薦奇數看到後面就知道啦),而後建議每一個服務都運行在單獨的機器上。服務器

② 集羣搭建配置

這裏咱們演示的是僞集羣的方式,不過一臺機器作僞集羣和幾臺機器分別作集羣須要注意的細節確定是更多的。能夠說僞集羣若是能配置穩當的話,到了真集羣下配置就會更加簡單明瞭。網絡

1.須要瞭解的配置參數

須要注意的配置
    1.initLimit:
        集羣中的小弟follower服務器,和領導leader服務器之間完成初始化同步鏈接時的能接受的最大心跳數,
        此時若是集羣環境很是大,同步數據的時間較長,這個參數咱們須要進行適當調整.
        
    注意,在zookeeper中,任什麼時候間的設置都是以ticktime的倍數來進行定義,若是咱們設置initLimit=2.那咱們能接受的最大時間就是ticktime*2
        
    2.syncLimit:
        follower和leader之間請求和應答能接受的最大心跳數
    
    集羣節點的配置
        server.id = host:port:port
        id:經過在各自的dataDir目錄下建立一個myId的文件來爲每臺機器賦予一個服務器id,這個id咱們通常用基數數字表示
        兩個port:第一個follower用來鏈接到leader,第二個用來選舉leader
複製代碼

2.zoo.cfg文件的修改

這裏咱們打開zookeeper根目錄下的conf文件夾,找到zoo.cfg文件,併發


① dataDir和dataLogDir

zookeeper官方建議咱們添加上DataLogDir來存放事務日誌。若是隻有dataDir目錄而沒有dataLogDir目錄的話,它會把運行日誌和事務日誌都放在dataDir的那個目錄上面去,事務日誌和運行日誌有什麼區別?這個事務日誌就至關於咱們zookeeper的數據庫,須要使用到讀取和恢復等功能的時候,它就須要這麼一個數據庫來恢復。框架


② port注意事項

咱們的zookeeper集羣開啓的話會有3個端口號,第一個端口號是客戶端去鏈接它的端口號。clientPort是指咱們的java客戶端去鏈接它所使用的端口號,maven

第二個是小弟follower和leader去同步數據所用到的端口號, 第三個就是咱們去進行leader選舉時所用到的端口號。在末尾加上咱們的節點配置,192.168.1.104使用的就是本機ip了,這裏下方1001就是小弟和leader同步數據所用的端口號,那萬一咱們的leader崩潰,掛掉了,咱們就須要從新去選舉,此時咱們就須要用到第三個端口,也就是2001端口號


③ 僞集羣的搭建

此時我copy了兩份zookeeper出來作僞集羣


打開 zookeeper-3.5.2-alpha 02/conf/setting.xml,也就是第二個zookeeper的setting.xml


同理打開zookeeper-3.5.2-alpha 03/conf/setting.xml,也就是第三個zookeeper的setting.xml


④ 集羣開啓前的注意事項---myid

在開啓3個集羣以前,咱們還須要去進行一個操做,看到咱們的dataDir了嗎,咱們要去到這個指定目錄下建立一個myid文件,前面也提到了myid是一個數字,咱們就用1,2,3做爲咱們zookeeper01,02,03的id便可。

注意,myid是一行只包含機器id的文本,id在集羣中必須是惟一的,其值應該在1~255之間,咱們這裏用的詞爲應該,而不是限制,若是真有一天超過了限制數集羣超過了255,這個zookeeper集羣就過大了,響應會很是慢。還有就是文件內不要有空格或者其餘字符,只須要打上1,2,3···,便可。


這裏會致使的報錯可能有兩個

由於zookeeper運行報錯的話會閃退,咱們能夠找到zkServer.cmd,右鍵編輯,在末尾加上pause來查看報錯信息

一個是myid沒放對目錄,須要放在咱們的dataDir的路徑下


二是myid裏面除了數字還有其餘的字符,這裏我刻意在1前面敲了一個空格


這些錯誤都須要儘可能去避免,不要由於本身操做太快沒注意一些小的細節。

以個人zookeeper01爲例,把myid文件放在data文件夾下便可,這個目錄結構最好仍是手動建立

不只僅是啓動集羣,後面咱們將要說到的選舉和節點間的通訊都是要使用到myid的


⑤ 集羣開啓時的報錯---ConnectException

注意,在多節點未啓動成功,好比僅僅只啓動了一個節點時,咱們啓動可能會報錯,好比如下這個Connection Exception:Connection refused:connect,這是由於咱們和02,03之間未能創建通訊致使的,因此此時咱們只須要把其餘的節點都正常開啓便可


③ 鏈接咱們的zookeeper集羣

集羣中的全部節點均可以提供服務,客戶端鏈接時,鏈接串中能夠指定多個或者所有集羣節點的鏈接地址,當一個節點鏈接不通時,客戶端將自動切換另外一個節點。指定地址的時候咱們使用英文輸入下的逗號,來進行分割

1.zookeeper的集羣監控---命令方式

這個須要在linux下來進行演示,由於電腦沒環境因此就不演示了

2.zookeeper的集羣監控---JMX

JMX(Java Management Extensions) --- Java管理擴展,是一個爲應用程序,設備,系統等1植入管理功能的框架

在咱們的jdk的bin目錄下運行jconsole.exe

打開jconsole以後,咱們看到了咱們正在運行的幾個服務,好比下圖中我正在跑的服務有jconsole自己,idea的maven,還有3個咱們正在開啓的zookeeper服務

咱們隨便選擇一個點開,選擇使用不安全的鏈接便可,此時咱們切換到Mbean視圖

這裏ReplicatedServer可複製的服務中,id1表明咱們設置的myid的值爲1

屬性中咱們看到一個數值QuorumSize表明的是集羣中有多少臺服務

此時它是做爲小弟follower,咱們也能夠嘗試打開另外的節點,好比個人第二個節點就是做爲leader節點

工具大概有一個瞭解就差很少了,咱們終於開始今天咱們的主題,集羣的leader選舉

內容二:zookeeper集羣的靈魂 --- leader

① 誰纔是leader

每臺服務器均可能成爲leader,那到底leader是如何被選舉出來的呢?

② 分佈式一致性算法---paxos算法

1.paxos是什麼---官方英文說明

這裏嘗試去翻譯了一下

P1a:提議者選擇一個提案編號n,給大多數接收者發送一個帶有編號n的提案預請求

P1b:若是接收者收到了編號n的預請求,n大於前面已經響應過的提案預請求編號,這時接收者作出響應,
承諾再也不接收編號比n小的提案預請求,而且在響應中會帶上本身曾接收過的最高編號提案

P2a:若是提議者接收到了大多數接收者對於n的提案響應,這時提議者會給這些接收者的每個服務發送接受請求,
此接受請求的內容爲編號n的提案並帶上一個value,這個value是如何取值的呢,
是取接收者響應中最高編號所對應的值,若是響應中根本不存在提議值,則能夠任意取值

P2b:若是接收者接收到一個編號爲n的接收請求,它接收該提案,但若是它將對大於編號n的預請求作出響應,則不接受n的提案
複製代碼

2.paxos的存在角色

ps:拜占庭將軍問題不存在於zookeeper的集羣中

3.paxos算法的流程簡析(結合官方流程說明)

首先咱們必須清楚paxos算法的兩個約束

1.最終只有一個提議會被選擇,只有被選擇的提議值纔會被learner去記錄
2.最終會有一個提議生效,paxos協議可以讓proposer發送的提議朝着能被大多數Acceptor接受的那個提議靠攏,
保證了最後結果的生成(少數服從多數原則)
複製代碼

咱們如今開始進行流程分析


① 預請求第一階段

提議者會發送的兩種消息:prepare是預請求,accept是接收請求

提議者的提議請求構成一個(n,v)結構,n爲序號,v爲提議值

簡單說明一下,這裏提議者A向接收者C,D,E發送了本身的一個預請求prepare request(n=2,v=5),C,D正常接收,可是E斷線,沒有第一時間接收到

提議者B向C,D,E發送本身的預請求prepare request(n=4,v=8),此時E已經恢復通信。


② 預請求第二階段

這裏咱們如何理解?首先咱們第一階段中C,D都是正常接收到了提議者A的預請求的,此時它們在先前沒有接收過任何的預請求(no previous),因此它們會對提議者A的請求做出應答(prepare response),這裏對應的概念就是P2b:若是接收者接收到一個編號爲n的接收請求,它接收該提案

因此此時它們對提議者A做出它們的應答,就是設置它們當前收到的提議就是(n=2,v=5),並向A做出承諾,再也不接收序號小於2的提議請求,這裏對應的就是P1b:若是接收者收到了編號n的預請求,n大於前面已經響應過的提案預請求編號,這裏由於先前根本就沒人請求過啊,因此前面響應過的預請求編號就是0,知足條件後,這時接收者C,D作出響應,承諾再也不接收編號比n小的提案預請求,因此而且在響應中會帶上本身曾接收過的最高編號提案,也就是它們接收到的(n=2,v=5)

此時接收者E是接收到了提議者B的提案,也是對應P1b和P2b,承諾再也不接收比4小的編號提案,並把本身的曾接收的最高編號提案設置爲(n=4,v=8)


③ 接受第一階段

隨後咱們的C,D接收者也會接收到提議者B的提議請求,先前C,D的提案爲(n=2,v=5),此時因爲B提議(n=4,v=8)了,4>2,因此它們會給提議者B也發送一個提議響應,表示它們曾經提案過(n=2,v=8),以後它們本身進行修改,把提案修改成(n=4,v=5),而且再承諾再也不接收n小於4的提案請求。這裏v爲何值爲5以後的階段會解釋

接收者E由於是先接收了提議者B的提案(n=4,v=8),因此以後再接收提議者A的提案(n=2,v=5)時,4>2,因此直接無視掉A的提案,也不對A做出回覆


④ 接受第二階段

此時proposerB收到了超過半數Acceptor所發的提案響應,便會對全部的接收者都發送一個接收請求

提議者A在預請求第二階段收到兩個提議響應以後會給C,D發送它的提案接受請求(n=2,v=5),可如今C,D明顯已經再也不是它的人了,因此C,D會無情拋棄它發送的接受請求

提議者B收到C,D的響應(n=2,v=5)的時候,會發送一個接收請求(n=4,v=5),爲何會是本身的編號4可是又取5做爲提案值呢,由於這裏的5是衆望所歸,是C,D它們響應回來的數字中最大的,請看下面咱們接收第一階段的圖,要明白,決定最終提案值的並非接收者接收到的你的提議值,而是做爲提議者自身所接收到的最大編號對應值,請注意P2a中對於paxos算法的描述,取接收者響應中最高編號所對應的值

⑤ 學習者參與流程

此時learner才真正地參與進來,在通過接受第二階段後,proposerB會把它得出來的(n=4,v=5)發送給全部的它能聯繫到的接收者,每一個人都接收完成以後,此時就算A再發起投票,由於它能接收回來的響應中,確定編號最大對應的v值爲5,因此5就是這個算法的最終結果,沒法再更改了。此時學習者就會去進行同步

③ zookeeper集羣的leader選舉要求

對選舉leader的要求:

選出的leader節點上要持有最高zxid
過半數節點贊成
複製代碼

內置實現的選舉算法

LeaderElection
FastLeaderElection(默認的)
AuthFastLeaderElection
複製代碼

④ zookeeper集羣leader選舉機制的內容及概念

服務器id---myid
事務id---服務器中存放的最大zxid
邏輯時鐘---發起的投票輪數計數
選舉狀態:
    LOOKING:競選狀態
    FOLLOWING:跟隨狀態,同步leader狀態,參與投票
    OBSERVING:管擦狀態,同步leader狀態,不參與投票
    LEADING:領導者狀態
複製代碼

⑤ zookeeper集羣leader選舉算法

選舉算法的步驟

1.每一個服務實例均發起選舉本身爲leader的投票
2.其餘服務實例收到投票邀請時,比較發起者的數據事務id是否比本身最新的事務ID大,大則給它投一票,小則不投票,相等則比較發起者的服務器ID,大則投票給它
3.發起者收到你們的投票反饋後,看投票數(包括本身的票數)是否大於集羣的半數,大於則成爲leader,未超過半數且leader未選出,則再次發起投票
複製代碼

如今咱們來說一下,剛剛咱們搭建好的集羣是如何選舉出節點2爲leader的

首先咱們節點一先被運行起來,由於沒人和它聯繫,因此當第二個節點啓動的時候,第二個節點也給本身發起一個投票,此時第一個節點把本身的信息廣播出去給其餘的節點,可是此時第三個節點並無啓動起來,因此它只能廣播給第二臺,而後第二個節點的廣播也發送給第一個節點,此時它們各自都持有節點1,2的信息,此時它們會先比較事務id,這時,可能初期這兩個節點的事務id都爲0,那都爲0的狀況下是怎樣的呢,就再比較服務器id,這時咱們的節點1的myid爲1,節點2的myid爲2,因此節點1就會投票給節點2,此時節點2就是兩票,節點1是本身的一票

此時咱們的節點3的服務也起來了,此時它會接收到節點1和節點2的廣播消息,它本身也會給節點1和節點2發送本身想要當leader的廣播,但是,它會發現,此時節點2已經有了兩票了,因此它就只能接受節點2爲leader的結果。

⑥ 經過啓動日誌來簡單分析

節點1的zkServer.cmd的啓動日誌

稍微往下到節點2啓動的狀態

最後渠道節點3啓動的狀態

節點2的zkServer.cmd的啓動日誌

節點3的zkServer.cmd的啓動日誌

finally

也是扯了很多東西,簡單再回顧一下

集羣搭建必定要注意myid的存放位置和編輯時候別手快帶上了一些沒法識別的字符,其他也沒什麼好說的

paxos算法比較晦澀難懂,也是不敢說本身有多深刻的研究,若是講的不穩當或者有另外想法的,你們能夠給我留言,會進行改進

zookeeper的選舉其實不算很難理解,若是要測試的話,只須要在搭建集羣那裏多加幾個集羣,再經過jconsole或者運行日誌來查看就能測試不一樣的結果,在網絡波動大的時候也會有其餘的改變

下一篇:從零開始的高併發(四)--- Zookeeper的經典應用場景

相關文章
相關標籤/搜索