zookeeper 動物園管理員,可以用來管理 hadoop(大象)、hive(蜜蜂)、pig(小 豬)、tomcat(貓)等等。
Apache Hbase 和 Apache Solr 的分布式集群都用到了 zookeeper,Dubbo中也要用到zookeeper。
Zookeeper是一個高性能、高可用的分布式管理與協調框架,主要功能包括:配置管理、命名服務、分布式鎖、集群管理、發布/訂閱、隊列管理。
ZooKeeper 的作用(應用場景)
1、配置管理
項目中有各種配置,比如數據庫連接配置,springboot、springcloud的yml配置,集群各節點的配置,等等。
當我們只使用少數幾台服務器、配置文件很少的時候,配置文件可以直接放在這些服務器上。
但做分布式項目的時候,往往有很多集群,集群節點很多,各種配置文件一大堆,這時候就需要使用zk來集中管理配置。
常見的有,使用zk來管理HBase集群的配置信息,管理Kafka的broker信息,Dubbo中使用zk來管理各服務節點的信息、實現服務治理(相當於SpringCloud中的Eureka)。
2、命名服務
訪問服務時,直接使用服務所在機器的ip地址很麻煩,全是數字、機器又多,那么多ip,不好記。
可以把集群各節點提供的服務的名稱、機器ip注冊到ZK上,在項目中直接使用服務名,向ZK請求某個服務時,ZK會選擇合適的節點(ip)來提供服務。
3、分布式鎖
除了redis可以實現分布式鎖,zk也可以。
4、 集群管理
在分布式的集群中,由於各種原因,比如硬件故障、網絡故障、並發量劇增等,經常要增減節點數量。
添加新的節點、或者舊的節點下線,集群中的各節點需要感知集群節點的變化。zk server可以存儲集群各節點(client)的信息,集群各節點再從zk server上獲取其它集群的信息。
Dubbo的服務治理就是用ZK實現的。
5、發布/訂閱
利用zk的觀察/通知機制。zk client訂閱某個znode,這個znode變化時,zk server會通知對應的zk client獲取znode的最新數據。
6、實現隊列
利用zk執行寫請求的順序一致性來實現(paxos算法)。
Zookeeper的數據模型

樹結構,/是根節點,節點叫做znode,一個znode對應一個文件目錄。
一個znode都代表1個zk client,用來存儲、管理一個zk client的數據。
每個znode都可以有子節點,即znode可以嵌套。
有四種類型的znode
-
PERSISTENT 持久化。一個節點斷開與zk server的連接后,zk server會保留該節點對應的znode
-
PERSISTENT_SEQUENTIAL 持久化順序編號。客戶端與zookeeper斷開連接后,zk server會保留該節點對應的znode
-
EPHEMERAL 臨時。一個節點斷開與zk server的連接后,zk server會刪除該節點對應的znode
-
EPHEMERAL_SEQUENTIAL 臨時順序編號。一個節點斷開與zk server的連接后,zk server會刪除該節點對應的znode
SEQUENTIAL 順序編號指的是,創建一個znode時會自動在節點名上加一個整數作為后綴。
這個整數由父節點維護,比如最新的一個SEQUENTIAL znode到5了,那下一個創建SEQUENTIAL znode就是6。
臨時節點常用於實現分布式鎖
服務節點先檢測是否有某個臨時節點,比如/ticket_lock,沒有此節點,接設置此節點,持有鎖;如過此節點已存在,就說明鎖已經被某個節點獲取、持有。
如果獲取到鎖的服務節點故障,斷開連接后會自動刪除此臨時節點,釋放鎖。
zkServer的集群模式
zk server一般都要集群,保證zk server的高可用。zk server的集群一般要讀寫分離,搞成分布式讀寫的形式。

zkServer有3種角色
- leader 負責處理zk client的寫請求(更新znode),一個zk集群只有一個leader
- follower 同步leader的數據,負責處理zk client的讀請求(獲取znode中的數據),並參與leader的選舉
- observer 處理zk client的讀請求,但不能參與leader的選舉,相當於臨時工,沒有編制
有follower就夠了,為什么還要使用observer?
- 減少選舉的時間花銷。
假設follower5台、observer6台,選舉新的leader時只需要等待3台機器同意;如果10台全是follower,需要等待6台機器同意,大大增加了選舉的時間花銷。
- 提高可用性。observer只是臨時工,宕掉一半集群就不可用,指的是follower宕掉一半,observer不參與數量統計,就算observer全部宕掉,也沒關系。
假設follower5台、observer8台,宕掉所有的observer、2台follower總共10台機器,集群仍然可用;如果13台全是follower,宕掉7台機器集群就不可用;顯然使用observer可用性更高。
observer可以看做無選舉、投票權的follower,主要是為了協助 follower 處理更多的讀請求。如果zkCli非常多、zkServer集群的讀請求負載很高,可以設置一些 Observer,提高讀請求的吞吐量。
zkServer集群至少需要3台機器,為何官方推薦集群的zkServer數量為單數?
比如zk server數量為5,掛掉3個,集群就不可用;數量為6,還是掛掉3個,集群就不可用。
5台、6台的容災能力是一樣的,少用1台機器還可以減少點成本。
zkServer如何處理zkCli的請求?
讀請求
zk集群的所有節點都可以處理讀請求,自己收到讀請求直接就處理了。
寫請求
由leader處理寫請求,follower、observer收到寫請求時轉交給leader處理。leader使用paxos算法來處理寫請求:
leader將寫請求都放到一個隊列中,並給寫請求分配一個唯一的編號,按照先進先出的順序處理。執行某個寫請求時,leader先向follower發起投票,是否要執行這個寫請求,如果超過半數同意,leader就會執行這個寫請求。同一時刻,leader只會執行一個寫請求,來保證數據的一致性。
如果某個follower、observer同步數據時,比如一個follower同步編號為99的寫操作時,發現編號為100的寫操作之前已經同步了,意識到自己的數據不一致,馬上停止對外服務,並從leader同步全量數據。
准確來說不算是投票。因為follower、observer要從leader同步數據,leader先執行一個寫請求,follower、observer再同步這個寫操作引起的數據更新,這2步作為一個事務來處理。
如果有一半以上的節點同步成功,就認為這個寫操作執行成功,提交事務。至於其他尚未同步成功的,如果機器故障下線了,那也不用去管它是否同步了;如果是網絡延遲等原因,過會兒會自動從leader同步,也不用去管。
如果同步成功的節點數量沒達到一半,就認為同步失敗,在已執行這個寫操作的機器上進行回滾。
其實leader處理讀、寫請求都是放在一個隊列中的,只不過讀請求不發起投票。
Zookeeper的特點
- 順序一致性:zkServer使用paxos算法來處理請求,將請求放在隊列中,先進先出,請求的執行順序與發送順序一致。寫代碼時可以利用這個特性來實現更高層次的同步。
- 數據更新原子性:一次寫操作即一個事務,要么成功(都應用、同步到所有節點),要么失敗(所有節點都不使用這次數據更新)。
- 單一視圖: zk client無論連接到哪個zk server,讀到的數據都是一樣的
- 可靠性(高可用性):zk server往往要集群,只要半數以上的節點可以正常工作,zk server集群就可以對外提供服務。(observer不參與數量統計)
- 實時性: zkCli讀取到的是zkServer上最新的數據
- 高性能:zk server將全量數量存儲在內存中,性能極高,尤其是在讀的時候。寫(更新znode)的時候,要把更新從內存同步到文件,性能稍低。
Zookeeper的工作原理
zk的核心機制是原子廣播,這個機制保證了zk server之間的數據同步。
實現原子廣播的協議是Zab協議,Zab協議有3種模式
- 恢復模式。leader故障后自動進入恢復模式,將集群恢復到正常工作狀態。先從follower中選出一個新的leader,其它follower從新的leader處同步數據,大多數follower同步完后恢復模式就結束了。
- 同步模式。follower從leader處同步數據。同步模式也是恢復模式的一部分。
- 廣播模式。leader處理寫請求時,廣播通知follower發起投票,半數以上的follower同意后,leader執行寫請求(修改自身的znode),執行后將修改、更新廣播給follower、observer,完成同步。
leader選舉
zkServer在選舉leader過程中的狀態
- LOOKING:leader宕掉了,正在選舉新的leader
- LEADING:當前zkServer成為leader
- FOLLOWING:當前zkServer是follower,同步leader的數據
- OBSERVING:當前zkServer是observer,同步leader的數據
myid
唯一標識集群中的一個zkServer
zxid
zxid其實是一個ReentranReadWriteLock,為保證數據一致性,一個節點同一時刻只執行一個讀寫操作,自然要加鎖。follower、observer只是對外不處理寫請求,同步leader數據時依然要同步寫操作。
zxid的前32位表示epoch(紀元、時代),一個leader對應一個epoch(時代),換了新的leader,epoch會自動使用新值。
zxid的后32位表示xid,每一個寫操作都是一個事務,xid即事務id。
myid標識的是zkServer節點,zxid標識的是數據版本。
Logicalclock 邏輯時鍾
邏輯時鍾是選舉時的一個概念,其值就是zxid中epoch的值。選舉出新的leader,開啟了新leader的統治時代,新紀元|時代就此誕生。
leader的選舉時機
- 集群啟動還沒有leader時
- leader宕機時
leader的選舉機制|算法
集群沒有leader時,所有節點都是LOOKING狀態。
所有follower都參與投票,投的都是自己,寫上自己的myid、zxid,廣播出去。
比較zxid,先選epoch大的,再從里面選xid大的。epoch越大、xid越大,說明數據越新。如果zxid相同,選擇myid大的。
選出新的leader后,leader變為LEADING狀態,follower變為FOLLOWING狀態,observer變為OBSERVING狀態。
Zookeeper的觀察/通知機制
一個zkCli可以觀察(watch)某個znode,一旦指定的znode發生改變,zkServer會通知觀察此znode的zkCli。
不使用zkCli輪詢zkServer的方式,一者開銷大,二者輪詢大多數時候都是無效的,znode發生變化時由zkServer通知對應的zkCli。
