1. 程式人生 > >一篇文章帶你瞭解 ZooKeeper 架構

一篇文章帶你瞭解 ZooKeeper 架構

上一篇文章,我們講解了 ZooKeeper 入門知識,這篇文章主要講解下 ZooKeeper 的架構,理解 ZooKeeper 的架構可以幫助我們更好地設計協同服務。

首先我們來看下 ZooKeeper 的總體架構圖。

ZooKeeper 總體架構

應用使用 ZooKeeper 客戶端庫來使用 ZooKeeper 服務,ZooKeeper 客戶端會和叢集中某一個節點建立 session, ZooKeeper 客戶端負責和 ZooKeeper 叢集的互動。 ZooKeeper 叢集可以有兩種模式:standalone 模式和 quorum 模式。處於 standalone 模式的 ZooKeeper 叢集還有一個獨立執行的 ZooKeeper 節點,standalone 模式一般用來開發。實際生產環境 ZooKeeper 一般處於 quorum 模式,在 quorum 模式下 ZooKeeper 叢集包換多個 ZooKeeper 節點。

Session

Session 是 ZooKeeper 客戶端的一個重要概念,ZooKeeper 客戶端庫和 ZooKeeper 叢集中的節點建立一個 session。客戶端可以主動關閉 session。另外如果 ZooKeeper 節點沒有在 session 關聯的 timeout 時間內收到客戶端的資料的話,ZooKeeper 節點也會關閉 session。另外 ZooKeeper 客戶端庫如果發現連線的 ZooKeeper 出錯,會自動的和其他 ZooKeeper 節點建立連線。

下圖展示了 ZooKeeper 客戶端是如何進行重連的?

剛開始 ZooKeeper 客戶端和 ZooKeeper 叢集中的節點 1 建立的 session,在過了一段時間後,ZooKeeper 節點 1 失敗了,ZooKeeper 客戶端就自動和 ZooKeeper 叢集中的節點 3 重新建立了 session 連線。

Quorum 模式

處於 Quorum 模式的 ZooKeeper 叢集包含多個 ZooKeeper 節點。 下圖的 ZooKeeper 叢集有 3 個節點,其中節點 1 是 leader 節點,節點 2 和節點 3 是 follower 節點。叢集中只能有一個 leader 節點,可以有多個 follower 節點,leader 節點可以處理讀寫請求,follower 只可以處理讀請求。follower 在接到寫請求時會把寫請求轉發給 leader 來處理。

下面來說下 ZooKeeper 保證的資料一致性:

資料一致性

  • 可線性化(Linearizable)寫入:先到達 leader 的寫請求會被先處理,leader 決定寫請求的執行順序。
  • 客戶端 FIFO 順序:來自給定客戶端的請求按照發送順序執行。

為了讓大家更好地理解 Quorum 模式,下面會配置一個 3 節點的 Quorum 模式的 ZooKeeper 叢集。

搭建 3 節點 ZooKeeper 叢集

首先需要準備 3 個配置檔案,dataDir 和 clientPort 配置項要配置不同的值。3 個配置檔案的 server.n 部分都是一樣的。在 server.1=127.0.0.1:3333:3334,其中 3333 是 quorum 之間的通訊的埠號,3334 是用於 leader 選舉的埠號。

還需要在每個節點的 dataDir 目錄下建立 myid 檔案,裡面內容為一個數字,用來標識當前主機,配置檔案中配置的 server.n 中 n 為什麼數字,則 myid 檔案中就輸入這個數字。

如下是第 1 個節點的配置檔案,其中目錄是 node1,埠號用的是 2181,另外兩個節點的目錄分別是 node2 和 node3,埠號分別為 2182 和 2183,最下面的三行都是一樣的:

# 心跳檢查的時間 2秒
tickTime=2000
# 初始化時 連線到伺服器端的間隔次數,總時間10*2=20秒
initLimit=10
# ZK Leader 和follower 之間通訊的次數,總時間5*2=10秒
syncLimit=5
# 儲存記憶體中資料庫快照的位置,如果不設定引數,更新事務日誌將被儲存到預設位置。
dataDir=/data/zk/quorum/node1
# ZK 伺服器端的監聽埠  
clientPort=2181

server.1=127.0.0.1:3333:3334
server.2=127.0.0.1:4444:4445
server.3=127.0.0.1:5555:5556

下面來啟動這個叢集,首先啟動第一個節點,啟動命令如下:

zkServer.sh start-foreground /usr/local/apache-zookeeper-3.5.6-bin/conf/zoo-quorum-node1.cfg 

注:start-foreground 選項 zkServer.sh 在前臺執行,把日誌直接打到 console。如果把日誌打到檔案的話,這三個 zkServer.sh 會把日誌打到同一個檔案。

在啟動第一個節點後日志中會出現如下:

2019-12-29 13:14:35,758 [myid:1] - WARN  [WorkerSender[myid=1]:QuorumCnxManager@679] - Cannot open channel to 2 at election address /127.0.0.1:4445
java.net.ConnectException: Connection refused (Connection refused)
    at java.net.PlainSocketImpl.socketConnect(Native Method)
    at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
    at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
    at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
    at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
    at java.net.Socket.connect(Socket.java:589)
    at org.apache.zookeeper.server.quorum.QuorumCnxManager.connectOne(QuorumCnxManager.java:650)
    at org.apache.zookeeper.server.quorum.QuorumCnxManager.connectOne(QuorumCnxManager.java:707)
    at org.apache.zookeeper.server.quorum.QuorumCnxManager.toSend(QuorumCnxManager.java:620)
    at org.apache.zookeeper.server.quorum.FastLeaderElection$Messenger$WorkerSender.process(FastLeaderElection.java:477)
    at org.apache.zookeeper.server.quorum.FastLeaderElection$Messenger$WorkerSender.run(FastLeaderElection.java:456)
    at java.lang.Thread.run(Thread.java:748)

原因是配置檔案中配置的為 3 個節點,但是隻啟動了 1 個節點,目前他和其他另外兩個節點建立不了連線,所以報這個問題。

接下來啟動第 2 個節點,執行命令如下:

zkServer.sh start-foreground /usr/local/apache-zookeeper-3.5.6-bin/conf/zoo-quorum-node2.cfg 

啟動後,我們可以在節點 2 的日誌中發現這麼一行:

2019-12-29 13:15:13,699 [myid:2] - INFO  [QuorumPeer[myid=2](plain=/0.0.0.0:2182)(secure=disabled):Leader@464] - LEADING - LEADER ELECTION TOOK - 41 MS

這說明節點 2 成為了 leader 節點,同樣可以在節點 1 的日誌中發現如下一行日誌,說明了節點 1 成為了 follower 節點。

2019-12-29 13:15:13,713 [myid:1] - INFO  [QuorumPeer[myid=1](plain=/0.0.0.0:2181)(secure=disabled):Follower@69] - FOLLOWING - LEADER ELECTION TOOK - 61 MS

因為對於一個三節點叢集來說兩個就代表了多數,就形成了 Quorum 模式,接下來啟動第 3 個節點,執行命令如下:

zkServer.sh start-foreground /usr/local/apache-zookeeper-3.5.6-bin/conf/zoo-quorum-node3.cfg

啟動後在日誌中也有如下一行,說明第 3 個節點也加入這個叢集,並且是作為 follower 節點加入的。

2019-12-29 13:15:52,440 [myid:3] - INFO  [QuorumPeer[myid=3](plain=/0.0.0.0:2183)(secure=disabled):Follower@69] - FOLLOWING - LEADER ELECTION TOOK - 15 MS

下面來啟動客戶端來使用這個三節點叢集,在命令中加了 -server 選項,後面指定的是三個節點的主機名和埠號,命令如下:

zkCli.sh -server 127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183
2019-12-29 13:45:44,982 [myid:127.0.0.1:2181] - INFO  [main-SendThread(127.0.0.1:2181):ClientCnxn$SendThread@1394] - Session establishment complete on server localhost/127.0.0.1:2181, sessionid = 0x101fff740830000, negotiated timeout = 30000

通過啟動日誌可以看到客戶端和埠號為 2181 的節點建立了連線,也就是和第 1 個節點建立了連線。

我們執行 ls -R / 命令看下這個叢集中的 znode 資料。

[zk: 127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183(CONNECTED) 1] ls -R /
/
/zookeeper
/zookeeper/config
/zookeeper/quota

下面我們殺掉一個 ZooKeeper 節點,看客戶端是否能進行重連。現在我們連的節點 1,我們來把節點 1 殺掉,可以在客戶端的日誌中發現客戶端和埠號為 2183 的節點重新建立了連線,也就是和節點 3 建立了連線。

2019-12-29 14:03:31,392 [myid:127.0.0.1:2183] - INFO  [main-SendThread(127.0.0.1:2183):ClientCnxn$SendThread@1394] - Session establishment complete on server localhost/127.0.0.1:2183, sessionid = 0x101fff740830000, negotiated timeout = 30000

然後我們再看下客戶端能否正常使用,執行 ls -R / ,可以發現能夠正常返回資料,說明客戶端是能夠正常使用的。

[zk: 127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183(CONNECTED) 4] ls -R /
/
/zookeeper
/zookeeper/config
/zookeeper/quota

總結

這篇文章主要講解了 ZooKeeper 架構,以及怎樣配置一個三節點的 ZooKeeper 叢集