zookeeper動物管理員,是一個很形象的名字,是一個分布式協調服務。它可以用來做分布式配置管理,服務注冊及發現,分布式鎖。在CAP中,屬于CP型。
下圖是zookeeper的架構圖:
圖中,綠色的是client,紅色的是一個leader,藍色的是其follower,紅色和藍色合稱為Ensemble(合奏團)。
- Ensemble:zookeeper集群所有server構成一個Ensemble,要求必須至少3臺server。
- Leader:負責改變zookeeper的狀態ZooKeeper state: create, setData, and delete.
- Follower:負責執行leader的命令。
- Client:client和server之間有心跳保持連接,如果server掛了zookeeper會自動failover到別的server上去。Client連接后zookeeper都會有相應的session,session就是一次活動的時間段,session中可以有這次活動的變量,client的session從client建立連接開始一直到client斷開連接為止。
- Quorum:集群中過半數的server的數量。比如集群中共有5臺機器,那么quorum是5/2+1==3臺。
一個zookeeper集群有一個leader,負責讀寫并把數據復制給follower,follower只負責讀。是主從結構。當leader掛掉后,zookeeper可以在200ms內重新選出新leader,可用性比較強。Read操作會從當前連接的server讀(但是因為write只需要quorum數量滿足即可生效,所以有可能client讀的是一臺沒同步的機器,所以這臺機器需要先從leader sync一下再返回),write寫操作需要集群里面大多數server都確認后才算ok,大多數指的是過半數。所以寫性能隨集群數量的增大而下降,讀性能隨集群數量增大而增加。所以,zookeeper有了observer server,他們不參與選舉,不算大多數的一部分,但是可以同步leader的數據擴展讀能力且不降低寫能力,這樣集群增大時不至于降低太多寫能力。可見,read和write都需要leader,所以leader掛了整個cluster不能對外提供任何服務。如果集群中不夠quorum量,整個cluster也不能對外提供任何服務。
zookeeper由leader把數據復制到follower上,同傳統數據庫類似,zookeeper的每項操作也是有事務的,有事務id,叫做Zxid(zookeeper transaction id,是個遞增的數值)。先來看看zookeeper的數據模型,是kv型,其中key是類似于linux的目錄樹:
zookeeper數據模型遵循分層命名空間,其中每個節點稱為Znode,這是群集運行的系統的一部分。和目錄不同的是即使中間的znode也能存數據,數據大小不超過1M,同redis一樣,存放的數據也是二進制安全的。如下圖:
zookeeper的znode節點有3種類型:
- 持久性Znode:
集合中的所有節點都將自己視為持久性Znode。即使在客戶端斷開連接后,這些節點仍會保持活動狀態。
- 臨時Znode:
這些類型的節點將保持活動狀態,直到客戶端連接到它們為止。當客戶端斷開連接時,他們會死亡。這些類型的節點不允許有子節點。
- 順序Znode:
它可以是持久性Znode或臨時Znode。將節點創建為順序Znode時,可以通過在原始名稱上附加10位序列號來分配Znode的路徑。
zookeeper如何選擇leader呢?每個server啟動后都是looking狀態,它要么選舉一個leader要么找到一個leader。有如下兩種情況:
- 如果leader已經存在了,其他server就會通知它哪臺server是leader,然后這臺機必須和leader進行溝通以使得自己的state和leader的state保持一致。
- 如果leader不存在,也就是所有的server都是looking狀態,那server必須相互溝通以選舉出一個leader。一開始每臺server都投票給自己,然后根據規則(voteZxid > myZxid) or (voteZxid = myZxid and voteSid > mySid)決定是否改變自己的投票(比如別人比自己更up to date那就投別人),當收到過半數投票后就會選一個勝出的,簡單來說就是most up to date的那臺server勝出。
zookeeper使用兩步法來進行數據的寫入。叫做zab協議,zookeeper atomic broadcast,即zookeeper原子廣播協議。
具體步驟是這樣:
- 客戶端發起寫請求給某臺server,server如果不是leader再轉給leader。
- Leader將客戶端請求信息轉化為事務Proposal,同時為每個Proposal分配一個事務ID(Zxid)。
- Leader為每個Follower單獨分配一個FIFO的隊列,將需要廣播的Proposal依次放入到隊列中。
- Follower接收到Proposal后,首先將其以事務日志log的方式寫入到本地磁盤中,寫入成功后給Leader反饋一個ACK響應。
- Leader接收到半數以上Follower的ACK響應后,即認為消息發送成功,可以發送Commit消息。
- Leader向所有Follower廣播Commit消息,同時自身也會完成事務提交。Follower接收到Commit消息后也會完成事務的提交,寫入內存,commit后follower自身才有這時的zxid可用于leader掛掉后的leader選舉,zookeeper同redis也是內存型的。
之所以分為兩個階段,是為了原子性。如果第一個步驟有過半的成功響應,則commit,否則不commit,不會存在中間狀態。
另外,之所以要求過半的成功響應,是為了當集群掛掉后數據不會丟失。因為集群再次可用需要過半的server ok狀態,因為之前有過半的成功響應,所以此時過半的server ok中必然存在log中保存有commit的數據,數據不會丟失。