zookeeper的基本原理和部署
一、Zookeeper 概述(官網翻譯)
ZooKeeper 通過一個可共享的分層資料註冊(稱之為znode)名稱空間來協調分佈部署的各個程序,這與檔案系統很相像。與一般的檔案不同,ZooKeeper提供給客戶端的服務是高吞吐、低延遲、高可用和嚴格有序的。效能上的特點使得ZooKeeper常被用於大規模分散式叢集。它的高可靠性使得它能夠避免大型系統中常見的單點故障。嚴格有序的特點使得客戶端可以實現複雜的同步邏輯。
ZooKeeper的名稱空間非常像一個標準檔案系統。每個名稱都是用“/”分隔的一系列路徑。每個znode都被用一個路徑標示,每個路徑都以“/”也就是根路徑開始。與標準檔案系統很相似,當znode還有子路徑的時候不能夠被刪除。
ZooKeeper與標準檔案系統最主要的不同是資料儲存在多個znode節點上,每個znode節點上存放的資料是有限的。設計ZooKeeper的目的是儲存元資料,例如狀態資訊,配置,位置資訊等等。這些元資料通常是Kbytes級別的。ZooKeeper有一個檢查機制來避免儲存大於1m的檔案,不過一般儲存的資料大小都小於這個值。
ZooKeeper的服務可以由多臺機器提供。這些機器共同維護著一個存在記憶體中的資料樹形結構,還有事務日誌以及持久化的快照。因為資料儲存在記憶體中,所以ZooKeeper才能做到高吞吐和低延遲。但是這也有一個缺點就是它能管理的資料大小受制於記憶體。這也就是為什麼要限制znode上儲存的資料量的深層次原因。
組成ZooKeeper的每個節點都要知道叢集中其他節點的資訊。只要大多數節點是可用的,整個服務就可用。客戶端也需要保留一份節點列表,並用這個來使用ZooKeeper服務。
客戶端在一個時間只會連線一個節點。它通過建立一個TCP連線來發送請求,獲取響應,得到事件通知和傳送心跳資訊。假如這個TCP連線中斷了,客戶端會重新連線另一個節點。ZooKeeper服務會在客戶端第一次連線到它的時候在客戶端所連線到的節點上開啟一個會話。如果客戶端需要連線另一個節點,這個會話會被新節點重置。
客戶端發出的讀請求由它所連線的那個節點處理。假如讀請求在某個znode註冊了一個監視事件,這個監視也由這個節點來負責。寫請求會被髮給多個節點,在所有節點都完成之後才會返回響應,這是為了保證一致性。同步請求也會被髮送給多個節點,但是不保證一致性。因此讀操作的吞吐能力會隨著節點的數目增多而增加,但是寫吞吐能力卻會下降。
對於ZooKeeper順序很重要。因此它用了強制性措施來保證有序。所有的更新都是統一有序的。ZooKeeper會給每個更新一個序號,稱之為zxid(ZooKeeper Transaction Id)。每次更新的zxid都是唯一的。讀操作是相對於寫有序的。讀請求的響應會被所請求的伺服器標記上它(這臺伺服器)所處理的最後一個zxid。
二、zookeeper的資料模型
Zookeeper 會維護一個具有層次關係的資料結構,它非常類似於一個標準的檔案系統,如圖所示:
zookeeper的資料結構具有如下特點:
- 每個子目錄項如 NameService 都被稱作為 znode,這個 znode 是被它所在的路徑唯一標識,如 Server1 這個 znode 的標識為 /NameService/Server1。如果建立與一個已經存在的路徑標示相同標示的節點,則會報錯。
- znode 可以有子節點目錄,並且每個 znode 可以儲存資料,儲存的資料需要小於1M。
- znode 是有版本的,每個 znode 中儲存的資料可以有多個版本,也就是一個訪問路徑中可以儲存多份資料
- znode 可以是臨時節點,一旦建立這個 znode 的客戶端與伺服器失去聯絡,這個 znode 也將自動刪除,Zookeeper 的客戶端和伺服器通訊採用長連線方式,每個客戶端和伺服器通過心跳來保持連線,這個連線狀態稱為 session,如果 znode 是臨時節點,這個 session 失效,znode 也就刪除了
- znode 的目錄名可以自動編號,如 App1 已經存在,再建立的話,將會自動命名為 App2
- znode 可以被監控,包括這個目錄節點中儲存的資料的修改,子節點目錄的變化等,一旦變化可以通知設定監控的客戶端,這個是 Zookeeper 的核心特性,Zookeeper 的很多功能都是基於這個特性實現的。
- 一個znode存在子節點,那麼對這個znode刪除操作會不成功,及每次刪除只能刪除葉子節點。
三、如何保證zookeeper叢集每臺機器上的資料的一致性
zookeeper的資料同步主要是通過leader機制來實現的。zookeeper的叢集中會選舉出來一個服務作為leader,leader的只要功能是對其他伺服器上面的資料進行恢復和同步。 如果客戶端發過來一個讀請求,那麼與這個請求連線的那臺伺服器可以對讀請求進行處理,然後返回資料,應為有leader機制保證了資料的一致性,因此不比擔心讀到的資料回不正確。 如果客戶端發過來一個寫請求,那麼與這個請求連線的那臺伺服器不會直接對寫請求進行處理,而是先判斷當前服務是否是leader,如果不是leader服務,該伺服器會把這個寫請求轉發給leader服務,由leader服務先對這個請求進行處理,進行資料的寫操作,完成後再跟其他的伺服器進行資料同步,來保證資料的一致性。因此當客戶端下載在傳送讀請求的時候,就算連的不是leader服務,讀到的資料也是上個寫請求修改過後的資料。四、zookeeper的部署以及客戶端程式碼的編寫
1. 單機模式:單機安裝非常簡單,只要獲取到 Zookeeper 的壓縮包並解壓到某個目錄如:/home/zookeeper-3.2.2 下,Zookeeper 的啟動指令碼在 bin 目錄下,Linux 下的啟動指令碼是 zkServer.sh。在你執行啟動指令碼之前,還有幾個基本的配置項需要配置一下,Zookeeper 的配置檔案在 conf 目錄下,這個目錄下有 zoo_sample.cfg 和 log4j.properties,你需要做的就是將 zoo_sample.cfg 改名為 zoo.cfg,因為 Zookeeper 在啟動時會找這個檔案作為預設配置檔案。下面詳細介紹一下,這個配置檔案中各個配置項的意義。tickTime=2000 dataDir=../devtools/zookeeper-3.2.2/build clientPort=2181
- tickTime:這個時間是作為 Zookeeper 伺服器之間或客戶端與伺服器之間維持心跳的時間間隔,也就是每個 tickTime 時間就會發送一個心跳
- dataDir:顧名思義就是 Zookeeper 儲存資料的目錄,預設情況下,Zookeeper 將寫資料的日誌檔案也儲存在這個目錄裡。
- clientPort:這個埠就是客戶端連線 Zookeeper 伺服器的埠,Zookeeper 會監聽這個埠,接受客戶端的訪問請求。
當這些配置項配置好後,你現在就可以啟動 Zookeeper 了,啟動後要檢查 Zookeeper 是否已經在服務,可以通過 netstat – ano 命令檢視是否有你配置的 clientPort 埠號在監聽服務。
2. 叢集模式:Zookeeper 的叢集模式的安裝和配置也不是很複雜,所要做的就是增加幾個配置項。叢集模式除了上面的三個配置項還要增加下面幾個配置項:
initLimit=5 syncLimit=2 server.1=192.168.211.1:2888:3888 server.2=192.168.211.2:2888:3888
- initLimit:這個配置項是用來配置 Zookeeper 接受客戶端(這裡所說的客戶端不是使用者連線 Zookeeper 伺服器的客戶端,而是 Zookeeper 伺服器叢集中連線到 Leader 的 Follower 伺服器)初始化連線時最長能忍受多少個心跳時間間隔數。當已經超過 10 個心跳的時間(也就是 tickTime)長度後 Zookeeper 伺服器還沒有收到客戶端的返回資訊,那麼表明這個客戶端連線失敗。總的時間長度就是 5*2000=10 秒
- syncLimit:這個配置項標識 Leader 與 Follower 之間傳送訊息,請求和應答時間長度,最長不能超過多少個 tickTime 的時間長度,總的時間長度就是 2*2000=4 秒
- server.A=B:C:D:其中 A 是一個數字,表示這個是第幾號伺服器;B 是這個伺服器的 ip 地址;C 表示的是這個伺服器與叢集中的 Leader 伺服器交換資訊的埠;D 表示的是萬一叢集中的 Leader 伺服器掛了,需要一個埠來重新進行選舉,選出一個新的 Leader,而這個埠就是用來執行選舉時伺服器相互通訊的埠。如果是偽叢集的配置方式,由於 B 都是一樣,所以不同的 Zookeeper 例項通訊埠號不能一樣,所以要給它們分配不同的埠號。
除了修改 zoo.cfg 配置檔案,叢集模式下還要配置一個檔案 myid,這個檔案在 dataDir 目錄下,這個檔案裡面就有一個數據就是 A 的值,Zookeeper 啟動時會讀取這個檔案,拿到裡面的資料與 zoo.cfg 裡面的配置資訊比較從而判斷到底是那個 server。