一篇文章帶你瞭解 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 叢集