架構師進階:02---集羣專題之(ZooKeeper集羣)

前言:

  • 本文有些概念可能有錯誤,因爲ZooKeeper本人還不太熟悉,等學習熟悉之後再回來補充

一、ZooKeeper集羣結構與原理

  • 最典型集羣模式: Master/Slave模式(主備模式)。在這種模式中,通常Master服務器作爲主服務器提供寫服務,其他的 Slave 服務器作爲從服務器通過異步複製的方式獲取Master服務器最新的數據提供讀服務
  • 但是,在 ZooKeeper 中沒有選擇傳統的 Master/Slave 概念,而是引入了 Leader、Follower 和 Observer 三種角色。如下圖所示:

  • 角色介紹:
    • Leader:ZooKeeper集羣中的所有機器通過一個Leader選舉過程選出一個Leader機器作爲領導者,LEADER既可以爲客戶端提供寫服務器又能提供讀服務
    • Follower:只能提供讀服務。Follower有選舉權
    • Observer:只能提供讀服務。Observer無選舉權;並且Observer也不參與寫操作的“過半寫成功”策略,因此Observer機器可以在不影響寫性能的情況下提升集羣的讀性能
  • 上面角色的總結爲:

二、ZooKeeper集羣的搭建

  • 集羣搭建前的提示與建議:
    • 爲了獲得可靠的 ZooKeeper 服務,用戶應該在一個集羣上部署 ZooKeeper
    • 只要集羣上大多數的ZooKeeper服務啓動了,那麼總的 ZooKeeper 服務將是可用的
    • 另外,最好使用奇數臺機器, 因爲zookeeper集羣是以宕機個數過半纔會讓整個集羣宕機的。 如果 zookeeper擁有 5 臺機器,那麼它就能處理 2 臺機器的故障了
    • 搭建zookeeper集羣時,一定要先停止已經啓動的zookeeper節點
  • 下面我們搭建的集羣中有5臺機器,其中1臺Leader、2臺Follower、2臺Observer

三、本機集羣搭建

第一步(創建環境)

  • 在根目錄下建立一個zookeeper目錄用來搭建集羣,然後進入目錄創建5個目錄,用來存放5個節點
mkdir zookeeper

cd zookeeper

mkdir node1 node2 node3 node4 node5

  • 分別在5個目錄下再分別創建conf/目錄、log/目錄、data/目錄,分別用來存儲配置文件、日誌文件、數據文件
mkdir node1/conf/ node1/log/ node1/data/
mkdir node2/conf/ node2/log/ node2/data/
mkdir node3/conf/ node3/log/ node3/data/
mkdir node4/conf/ node4/log/ node4/data/
mkdir node5/conf/ node5/log/ node5/data/

 

  • 再分別在每個節點的log/目錄下創建zoo.cfg文件,用來存儲配置信息
touch node1/conf/zoo.cfg node2/conf/zoo.cfg node3/conf/zoo.cfg node4/conf/zoo.cfg node5/conf/zoo.cfg

  • 創建完成之後查看一下
ls node1/ node2/ node3/ node4/ node5/

第二步(編寫配置文件)

  • node1節點的配置文件如下:
tickTime=2000

initLimit=10

syncLimit=5

dataDir=/home/ubuntu/zookeeper/node1/data/
dataLogDir=/home/ubuntu/zookeeper/node1/log/

clientPort=2181

maxClientCnxns=60

autopurge.snapRetainCount=3
autopurge.purgeInterval=1

server.1=0.0.0.0:2888:3888
server.2=0.0.0.0:2889:3889
server.3=0.0.0.0:2890:3890
server.4=0.0.0.0:2891:3891:observer
server.5=0.0.0.0:2892:3892:observer
  • node2節點的配置文件如下:
tickTime=2000

initLimit=10

syncLimit=5

dataDir=/home/ubuntu/zookeeper/node2/data/
dataLogDir=/home/ubuntu/zookeeper/node2/log/

clientPort=2182

maxClientCnxns=60

autopurge.snapRetainCount=3
autopurge.purgeInterval=1

server.1=0.0.0.0:2888:3888
server.2=0.0.0.0:2889:3889
server.3=0.0.0.0:2890:3890
server.4=0.0.0.0:2891:3891:observer
server.5=0.0.0.0:2892:3892:observer
  • node3節點的配置文件如下: 
tickTime=2000

initLimit=10

syncLimit=5

dataDir=/home/ubuntu/zookeeper/node3/data/
dataLogDir=/home/ubuntu/zookeeper/node3/log/

clientPort=2183

maxClientCnxns=60

autopurge.snapRetainCount=3
autopurge.purgeInterval=1

server.1=0.0.0.0:2888:3888
server.2=0.0.0.0:2889:3889
server.3=0.0.0.0:2890:3890
server.4=0.0.0.0:2891:3891:observer
server.5=0.0.0.0:2892:3892:observer
  • node4節點的配置文件如下: 
tickTime=2000

initLimit=10

syncLimit=5

dataDir=/home/ubuntu/zookeeper/node4/data/
dataLogDir=/home/ubuntu/zookeeper/node4/log/

clientPort=2184

maxClientCnxns=60

autopurge.snapRetainCount=3
autopurge.purgeInterval=1

server.1=0.0.0.0:2888:3888
server.2=0.0.0.0:2889:3889
server.3=0.0.0.0:2890:3890
server.4=0.0.0.0:2891:3891:observer
server.5=0.0.0.0:2892:3892:observer
  • node5節點的配置文件如下: 
tickTime=2000

initLimit=10

syncLimit=5

dataDir=/home/ubuntu/zookeeper/node5/data/
dataLogDir=/home/ubuntu/zookeeper/node5/log/

clientPort=2185

maxClientCnxns=60

autopurge.snapRetainCount=3
autopurge.purgeInterval=1

server.1=0.0.0.0:2888:3888
server.2=0.0.0.0:2889:3889
server.3=0.0.0.0:2890:3890
server.4=0.0.0.0:2891:3891:observer
server.5=0.0.0.0:2892:3892:observer
  • autopurge配置:
    • 客戶端在與zookeeper交互過程中會產生非常多的日誌,而且zookeeper也會將內存 中的數據作爲snapshot保存下來,這些數據是不會被自動刪除的,這樣磁盤中這樣的 數據就會越來越多。不過可以通過這兩個參數來設置,讓zookeeper自動刪除數據
    • autopurge.purgeInterval:指定自動清理快照文件和事務日誌文件的時間,單位爲 h,默認爲0表示不自動清理,這個時候可以使用腳本zkCleanup.sh手動清理。如果不 清理則磁盤空間佔用越來越大 
    • autopurge.snapRetainCount:用於指定保留快照文件和事務日誌文件的個數,默認 爲3
    • 不過如果你的集羣是一個非常繁忙的集羣,然後又碰上這個刪除操作,可能會影響 zookeeper集羣的性能,所以一般會讓這個過程在訪問低谷的時候進行,但是遺憾的 是zookeeper並沒有設置在哪個時間點運行的設置,所以有的時候我們會禁用這個自 動刪除的功能,而做一些其他手段,比如手動或者自動地在凌晨做清理工作。
  • Server.ID配置:server.id=IP/Host : port1 : port2
    • id:用來配置ZK集羣中的各節點,並建議id的值和myid保持一致,【注意】下文會講 myid的配置
    • IP/Host: 服務器的 IP 或者是與 IP 地址做了映射的主機名
    • port1:Leader和Follower或Observer交換數據使用
    • port2:用於Leader選舉
  • minSessionTimeout、maxSessionTimeout:
    • 一般,客戶端連接zookeeper的時候,都會設置一個session timeout,如果超過這個 時間client沒有與zookeeper server有聯繫,則這個session會被設置爲過期(如果這個 session上有臨時節點,則會被全部刪除,這就是實現集羣感知的基礎)。但是這個時間 不是客戶端可以無限制設置的,服務器可以設置這兩個參數來限制客戶端設置的範 圍。

第三步(編寫myid文件)

  • 例如在上面的日誌文件中,最後幾行的最前面顯示的是“server.id”,這個id就是指zookeeper服務器在集羣中的編號,我們需要把這個id寫入到 myid文件中,這個myid值在zookeeper集羣中的選舉過程中會做一個非常大的作用

  • myid文件存儲在dataDir參數所指定的目錄下,因此我們輸入下面的命令創建5個myid文件,並將各自的id寫入進去
echo "1" > node1/data/myid
echo "2" > node2/data/myid
echo "3" > node3/data/myid
echo "4" > node4/data/myid
echo "5" > node5/data/myid

  • 查看是否寫入成功
cat node1/data/myid node2/data/myid node3/data/myid node4/data/myid node5/data/myid

第四步(啓動所有服務器)

  • 啓動服務器1:
# 我們的zkServer.sh位於該目錄下, 使用時換上你們的路徑
~/build/apache-zookeeper-3.6.1-bin/bin/zkServer.sh --config ./node1/conf start

  • 啓動服務器2:
# 我們的zkServer.sh位於該目錄下, 使用時換上你們的路徑
~/build/apache-zookeeper-3.6.1-bin/bin/zkServer.sh --config ./node1/conf start

  • 啓動服務器3:
# 我們的zkServer.sh位於該目錄下, 使用時換上你們的路徑
~/build/apache-zookeeper-3.6.1-bin/bin/zkServer.sh --config ./node1/conf start

  • 啓動服務器4:
# 我們的zkServer.sh位於該目錄下, 使用時換上你們的路徑
~/build/apache-zookeeper-3.6.1-bin/bin/zkServer.sh --config ./node1/conf start

  • 啓動服務器5:
# 我們的zkServer.sh位於該目錄下, 使用時換上你們的路徑
~/build/apache-zookeeper-3.6.1-bin/bin/zkServer.sh --config ./node1/conf start

  • 全部啓動之後查看它們的狀態:可以看到其中node2爲leader節點,node1、node3爲follower節點,node4、node5爲observer節點

第五步(測試)

  • 進入2181節點(follpower節點)

  • 創建一個nodeservice節點,數據爲“xxx000”

  • 輸入下面的命令從2181節點進入2182節點
connect 0.0.0.0:2182

  • 在2182節點中也可以成功的看到內容

  • 同理,其他節點的數據也是一致的

四、多臺主機之間搭建集羣

  • 由於資源環境有限,無法用多臺機器演示

五、ZooKeeper數據一致性的實現

實際生活中數據一致性的案例

  • 車票搶購:例如下面一等座只有4張,多個用戶訂購時需要持證數據一致性

  • 限時搶購:例如下面圖片中顯示商品“xx件已售”,這個是靜態的,其變化在頁面刷新之後會改變,當用戶真正下單的時候需要與後臺保持數據一致性

  • 銀行轉賬:最經典的問題,董某向張某轉賬,需要確保董某的資金減少,張某的資金增加

一致性的三個級別

  • 總得來說,我們無法找到一種能夠滿足分佈式系統所有系統屬性的分佈式一致性解決方案。因此,如何既保證數據的一致性,同時又不影響系統運行的性能,是每一個分佈式系統都需要重點考慮和權衡 的
  • 於是,一致性級別由此誕生:
    • 1.強一致性:這種一致性級別是最符合用戶直覺的,它要求系統寫入什麼,讀出來的也會是什麼,用戶體驗好,但 實現起來往往對系統的性能影響大
    • 2.弱一致性:這種一致性級別約束了系統在寫入成功後,不承諾立即可以讀到寫入的值,也不久承諾多久之後數據 能夠達到一致,但會盡可能地保證到某個時間級別(比如秒級別)後,數據能夠達到一致狀態。例如支付寶體現之後提示幾分鐘之內到賬
    • 3.最終一致性:最終一致性是弱一致性的一個特例,系統會保證在一定時間內,能夠達到一個數據一致的狀態。這裏 之所以將最終一致性單獨提出來,是因爲它是弱一致性中非常推崇的一種一致性模型,也是業界在大 型分佈式系統的數據一致性上比較推崇的模型
  • ZooKeeper是強一致性的

常用的一致性算法

  • Paxos:比較複雜,一般不使用
  • Raft:Redis、ectd使用
  • Zab:zookeeper的算法

Paxos算法

  • 階段一:
    • (a) Proposer選擇一個提案編號N,然後向半數以上的Acceptor發送編號爲N的 Prepare請求
    • (b) 如果一個Acceptor收到一個編號爲N的Prepare請求,且N大於該Acceptor 已經響應過的所有Prepare請求的編號,那麼它就會將它已經 接受過的編號最大 的提案(如果有的話)作爲響應反饋給Proposer,同時該Acceptor承諾不再接 受任何編號小於N的提案。
  • 階段二:
    • (a) 如果Proposer收到半數以上Acceptor對其發出的編號爲N的Prepare請求的 響應,那麼它就會發送一個針對[N,V]提案的 Accept請求給半數以上的 Acceptor。注意:V就是收到的響應中編號最大的提案的value,如果響應中 不 包含任何提案,那麼V就由Proposer自己決定。
    • (b) 如果Acceptor收到一個針對編號爲N的提案的Accept請求,只要該 Acceptor沒有對編號大於N的Prepare請求做出過 響應,它就接受該提案
  • 如下圖所示:

ZooKeeper的數據一致性方案

  • 1. Leader服務器會爲事務請求生成一個全局的的遞增事務ID(即ZXID),保證每個消息的因果關係的順序
  • 2. Leader服務器會爲該事務生成對應的Proposal,進行廣播
  • 3. Leader服務器會爲每一個Follower服務器都各自分配一個單獨的隊列,然後將需要廣播的事務Proposal依次放入這些隊列中去,並根據FIFO策略進行消息發送
  • 4. 每一個Follower服務器在接收到這個事務Proposal之後,首先以日誌形式寫入本地磁盤,並且成功寫入後反饋給Leader服務器一個Ack響應,ZNODE樹
  • 5. 當Leader服務器接收超過半數的Follower的Ack響應,Leader自身也會完成對事務的提交。同時就會廣播一個Commit消息給所有的Follower服務器以通知進行事務提交。每一個Follower服務器在 接收到Commit消息後,從日誌寫入到DataTree

Leader服務器崩潰重啓

  • 當Leader服務器掛掉之後會出現什麼問題?
    • 第一種:Leader接收的proposal還沒有廣播給其它的服務器,即ZXID相同
    • 第二種:Leader接收的proposal已部分發送給其他的服務器,即ZXID不同
  • 總的來說就是:Leader寫的數據還沒有生效。那麼如何處理呢?
  • 第一步:選舉新的Leader
    • 1)舊Leader宕機後,選舉新Leader中,舊的Leader重啓後不可能再次成爲這次選 舉的新Leader
    • 2)舊Leader宕機後,在剩下的Follower服務器選取新Leader的標準,一定是 (ZXID,myid)最大的那個Follower成爲新Leader。(即數據同步最新的那臺 Follower服務器)
      • 如果你的zxid比我大,那麼就不投票給你
      • 如果你的zxid比我大,那麼我就投票給你
      • 如果你的zxid跟我一樣,但是myid比我大,那麼就投票給你
    • 3)ZXID是64位的數字。其中低32位可以靠做是一個簡單的單調遞增的計數器,高 32位則代表一個Leader從生到死的epoch編號
    • 4)新Leader選舉出來,從proposal中分析出舊Leader的epoch編號,並遞增1, 作爲新的ZXID的高32位,然後新ZXID的低32位從0位重新開始計數
    • 5)新Leader通過ZXID和所有的Follower機器上的ZXID進行對比,確保數據同步。 保證數據在所有的Follower上與之達成同步。舊Leader上新被提出的事務被拋棄。 當數據達到同步,纔將Follower服務器加入可用的Follower服務器列表。然後開 始消息廣播
  • 第二步:選舉成功後數據同步
    • 1)Leader等待Follower和Observer連接
    • 2)Follower連接leader,將最大的zxid發送給leader
    • 3)Leader根據follower的zxid確定同步點
    • 4)完成同步後通知follower 已經成爲uptodate狀態
    • 5)Follower收到uptodate消息後,又可以重新接受client的請求進行服務了
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章