顧名思義 zookeeper 就是動物園管理員,他是用來管 hadoop(大象)、Hive(蜜蜂)、pig(小 豬)的管理員, Apache Hbase 和 Apache Solr 的分散式集群都用到了 zookeeper;Zookeeper: 是一個分散式的、開源的程式協調服務,是 hadoop ...
顧名思義 zookeeper 就是動物園管理員,他是用來管 hadoop(大象)、Hive(蜜蜂)、pig(小 豬)的管理員, Apache Hbase 和 Apache Solr 的分散式集群都用到了 zookeeper;Zookeeper: 是一個分散式的、開源的程式協調服務,是 hadoop 項目下的一個子項目。他提供的主要功 能包括:配置管理、名稱服務、分散式鎖、集群管理。
功能特性
- 最終一致性:client 不論連接到哪個 Server,展示給它都是同一個視圖,這是 zookeeper 最重要的性能。
- 可靠性:具有簡單、健壯、良好的性能,如果消息 m 被到一臺伺服器接受,那麼它 將被所有的伺服器接受。
- 實時性:Zookeeper 保證客戶端將在一個時間間隔範圍內獲得伺服器的更新信息,或 者伺服器失效的信息。但由於網路延時等原因,Zookeeper 不能保證兩個客戶端能同時得到 剛更新的數據,如果需要最新數據,應該在讀數據之前調用 sync()介面。
- 等待無關(wait-free):慢的或者失效的 client 不得干預快速的 client 的請求,使得每 個 client 都能有效的等待。
- 原子性:更新只能成功或者失敗,沒有中間狀態。
順序性:包括全局有序和偏序兩種:全局有序是指如果在一臺伺服器上消息 a 在消息 b 前發佈,則在所有 Server 上消息 a 都將在消息 b 前被髮布;偏序是指如果一個消息 b 在消 息 a 後被同一個發送者發佈,a 必將排在 b 前面。
進程角色
- leader:由集群成員投票選舉出來的領導者,負責處理外部發到集群的讀寫請求,處理寫請求時會發起投票,只有集群內超過半數節點通過後寫操作才會被通過。
- follower:負責處理都請求並返回結果,如果接收到寫請求則將之轉發給leader,還要負責leader選舉時的投票。
observer:可以理解為沒有選舉權的follower,只負責處理業務,時為了提高集群吞吐率,同時又能保證集群快速完成選舉而引進的機制。
機制
- 集群的兩種模式
- 恢復模式:集群的一種非穩定狀態,集群不能處理外部請求;集群啟動或遇到leader崩潰時,集群進入恢復模式,在本模式中選舉leader,leader選舉完成後其他節點與leader進行數據同步,當過半節點完成同步後恢復模式結束,進入廣播模式。
- 廣播模式:集群的穩定狀態,集群能正常的處理外部請求;此時若有新節點加入,新節點會自動從leader同步數據。
- 集群啟動過程:
- leader選舉原則
- 集群中只有超過半數的節點處於正常狀態,集群才能穩定,才能處理外部請求。
- 集群正常工作之前myid小的節點會優先給myid大的節點投票,直到選出leader為止。
- 選出leader之前,集群所有節點都處於looking狀態,選舉成功後,除leader節點外,其餘節點的狀態由looking變為following,角色也成為了follower。
- leader選舉過程
- 假設集群有5個節點,myid分別為1~5,假設集群第一次啟動,所有節點都沒有歷史數據,啟動順序1~5。由集群節點數量可知,至少要有3個節點正常,集群才能穩定工作。
- 節點1啟動,其初始狀態為looking,發起一輪選舉,節點1投自己一票,由於不過半,本輪選舉無法完成。節點1仍然保持looking狀態。
- 節點2啟動,其初始狀態為looking,它也發起一輪選舉,節點2投自己一票;節點1也參與進本輪投票,打算給自己投一票,但是發現節點2的myid比自己的大,就改投節點2一票;本輪投票過後節點1得0票,節點2得2票,由於節點2的得票數不過半,所以本輪選舉未能完成;節點1、2都保持looking狀態。
- 節點3啟動,其初始狀態為looking,它也發起一輪選舉,且節點3先投自己一票;節點1、2也都參與進本輪投票中來,打算投自己一票,發現本輪中節點3的myid大於自己的,所以節點1、2都轉投節點3一票;此時節點3就收穫了3票,超過了集群節點的半數,節點3率先當選,並從looking狀態變為leading狀態。節點1、2的狀態變為following。
- 節點4啟動,其初始狀態為looking,它也發起一輪選舉;此時由於節點1、2處於following狀態,這兩個節點就不參與本輪選舉。節點4本打算投自己一票,但是發現節點3已進入leading狀態,且票數已經過半,此時節點4就會將自己的一票轉投給節點3。節點4未收到投票,狀態由looking變為following。
- 節點5的啟動過程與節點4一樣,最終未獲得投票,也處於following狀態。
- 最終節點3成為leader,節點1、2、4、5成為follower。
- leader選舉原則
- 崩潰恢復過程:當leader崩潰後,集群中的其他follower節點會重新變為looking狀態,重新進行leader選舉。選舉過程同啟動時的leader選舉一樣。
- 消息廣播演算法:
- leader接收到一個寫請求後,leader會給此請求標記一個全局自增的64位事務id(zxid)。
- leader以隊列未載體將每個事務依此發送給follower,follower讀取也嚴格遵循隊列的順序,這就避免了paxos演算法的全序問題。
- follower在本地緩存了它最新執行的事務的zxid,當接收到新事務後,會取出zxid與本地的zxid做比較,如果接收到的zxid大於本地的就執行此事務並給leader返回確認消息,否則拒絕執行。
當leader接收到過半數量的follower確認消息後,代表著事務已在整個集群中執行,leader就給所有follower發送事務提交指令。
zxid:是一個32+32位的數字;前32位稱為epochId,是當前leader的全局自增編號,如果把leader比作皇帝,那epochId則是皇帝的年號。後32位是每個事務特定的標識,相當於皇帝發佈的號令,對一個皇帝來說這個編號也是全局自增的。數據結構
- Znode
在 Zookeeper 中,znode 是一個跟 Unix 文件系統路徑相似的節點,可以往這個節點存儲 或獲取數據。 Zookeeper 底層是一套數據結構。這個存儲結構是一個樹形結構,其上的每一個節點, 我們稱之為“znode” zookeeper 中的數據是按照“樹”結構進行存儲的。而且 znode 節點還分為 4 中不同的類 型。 每一個 znode 預設能夠存儲 1MB 的數據(對於記錄狀態性質的數據來說,夠了) 可以使用 zkCli 命令,登錄到 zookeeper 上,並通過 ls、create、delete、get、set 等命令 操作這些 znode 節點 - Znode 節點類型
- PERSISTENT 持久化節點: 所謂持久節點,是指在節點創建後,就一直存在,直到 有刪除操作來主動清除這個節點。否則不會因為創建該節點的客戶端會話失效而消失。
- PERSISTENT_SEQUENTIAL 持久順序節點:這類節點的基本特性和上面的節點類 型是一致的。額外的特性是,在 ZK 中,每個父節點會為他的第一級子節點維護一份時序, 會記錄每個子節點創建的先後順序。基於這個特性,在創建子節點的時候,可以設置這個屬 性,那麼在創建節點過程中,ZK 會自動為給定節點名加上一個數字尾碼,作為新的節點名。 這個數字尾碼的範圍是整型的最大值。 在創建節點的時候只需要傳入節點 “/test_”,這樣 之後,zookeeper 自動會給”test_”後面補充數字。
- EPHEMERAL 臨時節點:和持久節點不同的是,臨時節點的生命周期和客戶端會 話綁定。也就是說,如果客戶端會話失效,那麼這個節點就會自動被清除掉。註意,這裡提 到的是會話失效,而非連接斷開。另外,在臨時節點下麵不能創建子節點。 這裡還要註意一件事,就是當你客戶端會話失效後,所產生的節點也不是一下子就消失 了,也要過一段時間,大概是 10 秒以內,可以試一下,本機操作生成節點,在伺服器端用 命令來查看當前的節點數目,你會發現客戶端已經 stop,但是產生的節點還在。
EPHEMERAL_SEQUENTIAL 臨時自動編號節點:此節點是屬於臨時節點,不過帶 有順序,客戶端會話結束節點就消失。
目錄結構
- bin:放置運行腳本和工具腳本,如果是 Linux 環境還會有有 zookeeper 的運 行日誌 zookeeper.out
- conf:zookeeper 預設讀取配置的目錄,裡面會有預設的配置文件
- contrib:zookeeper 的拓展功能
- dist-maven:zookeeper的 maven 打包目錄
- docs:zookeeper 相關的文檔
- lib:zookeeper 核心的 jar
- recipes:zookeeper 分散式相關的 jar 包
src:zookeeper 源碼
單機部署
Zookeeper 在啟動時預設的去 conf 目錄下查找一個名稱為 zoo.cfg 的配置文件。 在 zookeeper 應用目錄中有子目錄 conf。其中有配置文件模板,手動拷貝重命名:zoo_sample.cfg cp zoo_sample.cfg zoo.cfg。zookeeper 應用中的配置文件為 conf/zoo.cfg。 修改配置文件 zoo.cfg - 設置數據緩存路徑
安裝jdk,配置相關環境變數,上傳zookeeper壓縮包
[zk_hom]# tar -zxvf apache-zookeeper-3.5.5-bin.tar.gz //解壓
[zk_hom]# mkdir zkdata //新建一個數據持久化目錄
[zk_hom]# cd conf //進入配置目錄
[zk_hom/confg]# cp zoo_example.cfg zoo.cfg //複製配置文件樣本,並重命名未zoo.cfg
編解zoo.cfg,將其中的dataDir = zk_home/zkdata
[zk_hom/bin]# sh ./zkServer.sh start //啟動節點
[zk_hom/bin]# sh ./zkServer.sh status //查看節點狀態集群部署
- 各個節點上的準備工作同單機的一樣,都需要jdk,zookeeper壓縮包,同時要拷貝配置並配置數據持久化目錄,同時為各節點新建持久化目錄。
- 不同的是需要在各節點的zookeeper持久化目錄中新建一個名為“myid”的文件,文件中各自寫上節點編號1~5。
- 配置文件中需要追加集群中其他節點的訪問地址:
【server.myid = ip:通信埠:選舉埠】
server.1 = 192.168.50.1:2181:3181
server.2 = 192.168.50.2:2181:3181
server.3 = 192.168.50.3:2181:3181
server.4 = 192.168.50.4:2181:3181
server.5 = 192.168.50.5:2181:3181 啟動各個節點
應用管理
bin/zkServer.sh start //開啟服務
bin/zkServer.sh status //查看服務狀態
bin/zkServer.sh stop //停止服務端
bin/zkCli.sh -server 192.168.199.175:2181 //使用客戶端連接服務端客戶端命令
應用場景
- 配置管理
在我們的應用中除了代碼外,還有一些就是各種配置。比如資料庫連接等。一般我們都 是使用配置文件的方式,在代碼中引入這些配置文件。當我們只有一種配置,只有一臺服務 器,並且不經常修改的時候,使用配置文件是一個很好的做法,但是如果我們配置非常多, 有很多伺服器都需要這個配置,這時使用配置文件就不是個好主意了。這個時候往往需要尋 找一種集中管理配置的方法,我們在這個集中的地方修改了配置,所有對這個配置感興趣的 都可以獲得變更。Zookeeper 就是這種服務,它使用 Zab 這種一致性協議來提供一致性。現 在有很多開源項目使用 Zookeeper 來維護配置,比如在 HBase 中,客戶端就是連接一個 Zookeeper,獲得必要的 HBase 集群的配置信息,然後才可以進一步操作。還有在開源的消 息隊列 Kafka 中,也使用 Zookeeper來維護broker的信息。在 Alibaba開源的 SOA 框架Dubbo 中也廣泛的使用 Zookeeper 管理一些配置來實現服務治理。 - 名稱服務
名稱服務這個就很好理解了。比如為了通過網路訪問一個系統,我們得知道對方的 IP 地址,但是 IP 地址對人非常不友好,這個時候我們就需要使用功能變數名稱來訪問。但是電腦是 不能是功能變數名稱的。怎麼辦呢?如果我們每台機器里都備有一份功能變數名稱到 IP 地址的映射,這個倒 是能解決一部分問題,但是如果功能變數名稱對應的 IP 發生變化了又該怎麼辦呢?於是我們有了 DNS 這個東西。我們只需要訪問一個大家熟知的(known)的點,它就會告訴你這個功能變數名稱對應 的 IP 是什麼。在我們的應用中也會存在很多這類問題,特別是在我們的服務特別多的時候, 如果我們在本地保存服務的地址的時候將非常不方便,但是如果我們只需要訪問一個大家都 熟知的訪問點,這裡提供統一的入口,那麼維護起來將方便得多了。 - 分散式鎖
其實在第一篇文章中已經介紹了 Zookeeper 是一個分散式協調服務。這樣我們就可以利 用 Zookeeper 來協調多個分散式進程之間的活動。比如在一個分散式環境中,為了提高可靠 性,我們的集群的每台伺服器上都部署著同樣的服務。但是,一件事情如果集群中的每個服 務器都進行的話,那相互之間就要協調,編程起來將非常複雜。而如果我們只讓一個服務進 行操作,那又存在單點。通常還有一種做法就是使用分散式鎖,在某個時刻只讓一個服務去幹活,當這台服務出問題的時候鎖釋放,立即 fail over 到另外的服務。這在很多分散式系統 中都是這麼做,這種設計有一個更好聽的名字叫 Leader Election(leader 選舉)。比如 HBase 的 Master 就是採用這種機制。但要註意的是分散式鎖跟同一個進程的鎖還是有區別的,所 以使用的時候要比同一個進程里的鎖更謹慎的使用。 - 集群管理
在分散式的集群中,經常會由於各種原因,比如硬體故障,軟體故障,網路問題,有些 節點會進進出出。有新的節點加入進來,也有老的節點退出集群。這個時候,集群中其他機 器需要感知到這種變化,然後根據這種變化做出對應的決策。比如我們是一個分散式存儲系 統,有一個中央控制節點負責存儲的分配,當有新的存儲進來的時候我們要根據現在集群目 前的狀態來分配存儲節點。這個時候我們就需要動態感知到集群目前的狀態。還有,比如一 個分散式的 SOA 架構中,服務是一個集群提供的,當消費者訪問某個服務時,就需要採用 某種機制發現現在有哪些節點可以提供該服務(這也稱之為服務發現,比如 Alibaba 開源的 SOA 框架 Dubbo 就採用了 Zookeeper 作為服務發現的底層機制)。還有開源的 Kafka 隊列就 採用了 Zookeeper 作為 Cosnumer 的上下線管理。 - 負載均衡的集群管理