假設有五台伺服器組成的zookeeper叢集,它們的id從1-5,同時它們都是最新啟動的,也就是沒有曆史資料,在存放資料量這一點上,都是一樣的。
假設這些伺服器依序啟動,來看看會發生什麼。

(1)伺服器1啟動,此時隻有它一台伺服器啟動了,它發出去的報沒有任何響應,是以它的選舉狀态一直是LOOKING狀态。
(2)伺服器2啟動,它與最開始啟動的伺服器1進行通信,互相交換自己的選舉結果,由于兩者都沒有曆史資料,是以id值較大的伺服器2勝出,
但是由于沒有達到超過半數以上的伺服器都同意選舉它(這個例子中的半數以上是3),是以伺服器1、2還是繼續保持LOOKING狀态。
(3)伺服器3啟動,根據前面的理論分析,伺服器3成為伺服器1、2、3中的Leader,而與上面不同的是,此時有三台伺服器選舉了它,
是以它成為了這次選舉的Leader。
(4)伺服器4啟動,根據前面的分析,理論上伺服器4應該是伺服器1、2、3、4中最大的,但是由于前面已經有半數以上的伺服器選舉了伺服器3,
是以它成為Follower。
(5)伺服器5啟動,同4一樣成為Follower。
注意,如果按照5,4,3,2,1的順序啟動,那麼5将成為Leader,因為在滿足半數條件後,ZooKeeper叢集啟動,5的Id最大,被選舉為Leader。
在ZooKeeper中,伺服器和用戶端之間維持的是一個長連接配接,在 SESSION_TIMEOUT 時間内,伺服器會确定用戶端是否正常連接配接(用戶端會定時向伺服器發送
heart_beat),伺服器重置下次SESSION_TIMEOUT時間。是以,在正常情況下,Session一直有效,并且zk叢集所有機器上都儲存這個Session資訊。在出現問題的情況下,
用戶端與伺服器之間連接配接斷了(用戶端所連接配接的那台zk機器挂了,或是其它原因的網絡閃斷),這個時候用戶端會主動在位址清單(初始化的時候傳入構造方法的那個
參數connectString)中選擇新的位址進行連接配接。
以上即為伺服器與用戶端之間維持長連接配接的過程,在這個過程中,使用者可能會看到兩類異常CONNECTIONLOSS(連接配接斷開) 和SESSIONEXPIRED(Session 過期)。
發生CONNECTIONLOSS後,此時使用者不需要關心我的會話是否可用,應用所要做的就是等待用戶端幫我們自動連接配接上新的zk機器,一旦成功連接配接上新的zk機器後,
确認之前的操作是否執行成功了。
ZooKeeper不能確定任何用戶端能夠擷取(即Read Request)到一樣的資料,除非用戶端自己要求,方法是用戶端在擷取資料之前調用
org.apache.zookeeper.AsyncCallbac k.VoidCallback, java.lang.Object) sync。
通常情況下(這裡所說的通常情況滿足:1. 對擷取的資料是否是最新版本不敏感,2. 一個用戶端修改了資料,其它用戶端是否需要立即能夠擷取最新資料),
可以不關心這點。
在其它情況下,最清晰的場景是這樣:ZK用戶端A對 /my_test 的内容從 v1->v2, 但是ZK用戶端B對 /my_test 的内容擷取,依然得到的是 v1. 請注意,
這個是實際存在的現象,當然延時很短。解決的方法是用戶端B先調用 sync(), 再調用 getData()。
不是。
官方聲明:一個Watch事件是一個一次性的觸發器,當被設定了Watch的資料發生了改變的時候,則伺服器将這個改變發送給設定了Watch的用戶端,
以便通知它們。
為什麼不是永久的,舉個例子,如果服務端變動頻繁,而監聽的用戶端很多情況下,每次變動都要通知到所有的用戶端,這太消耗性能了。
一般是用戶端執行getData(“/節點A”,true),如果節點A發生了變更或删除,用戶端會得到它的watch事件,但是在之後節點A又發生了變更,
而用戶端又沒有設定watch事件,就不再給用戶端發送。
在實際應用中,很多情況下,我們的用戶端不需要知道服務端的每一次變動,我隻要最新的資料即可。
使用watch需要注意的幾點:
1)Watches通知是一次性的,必須重複注冊。
2)發生CONNECTIONLOSS之後,隻要在session_timeout之内再次連接配接上(即不發生SESSIONEXPIRED),那麼這個連接配接注冊的watches依然在。
3)節點資料的版本變化會觸發NodeDataChanged,注意,這裡特意說明了是版本變化。存在這樣的情況,隻要成功執行了setData()方法,
無論内容是否和之前一緻,都會觸發NodeDataChanged。
4)對某個節點注冊了watch,但是節點被删除了,那麼注冊在這個節點上的watches都會被移除。
5)同一個zk用戶端對某一個節點注冊相同的watch,隻會收到一次通知。
6)Watcher對象隻會儲存在用戶端,不會傳遞到服務端。
如果節點資料的更新頻率很高的話,不能。
原因在于:當一次資料修改,通知用戶端,用戶端再次注冊watch,在這個過程中,可能資料已經發生了許多次資料修改,是以,
千萬不要做這樣的測試:”資料被修改了n次,一定會收到n次通知”來測試server是否正常工作。
ZooKeeper中不能為臨時節點建立子節點,如果需要建立子節點,應該将要建立子節點的節點建立為永久性節點。
如何實作?ZK本身不提供這樣的功能,它僅僅提供了對單個IP的連接配接數的限制。你可以通過修改iptables來實作對單個ip的限制。
延時是多少?連接配接斷了之後,ZK不會馬上移除臨時資料,隻有當SESSIONEXPIRED之後,才會把這個會話建立的臨時資料移除。是以,
使用者需要謹慎設定Session_TimeOut。
ZooKeeper中的動态擴容其實就是水準擴容,Zookeeper對這方面的支援不太好,目前有兩種方式:
全部重新開機:關閉所有Zookeeper服務,修改配置之後啟動,不影響之前用戶端的會話。
逐個重新開機:這是比較常用的方式。
Leader伺服器會和每一個Follower/Observer伺服器都建立TCP連接配接,同時為每個F/O都建立一個叫做LearnerHandler的實體。
LearnerHandler主要負責Leader和F/O之間的網絡通訊,包括資料同步,請求轉發和Proposal提議的投票等。Leader伺服器儲存了所有F/O的LearnerHandler。
如何進行日志清理?zk自己不會進行日志清理,需要運維人員進行日志清理。
Zookeeper 作為一個分布式的服務架構,主要用來解決分布式叢集中應用系統的一緻性問題。ZooKeeper提供的服務包括:分布式消息同步和協調機制、
伺服器節點動态上下線、統一配置管理、負載均衡、叢集管理等。
ZooKeeper提供基于類似于Linux檔案系統的目錄節點樹方式的資料存儲,即分層命名空間。Zookeeper 并不是用來專門存儲資料的,
它的作用主要是用來維護和監控你存儲的資料的狀态變化,通過監控這些資料狀态的變化,進而可以達到基于資料的叢集管理,ZooKeeper節點的資料上限是1MB。
我們可以認為Zookeeper=檔案系統+通知機制,對于ZooKeeper的資料結構,每個子目錄項如 NameService 都被稱作為 znode,這個 znode 是被它所在的
路徑唯一辨別,如 Server1 這個 znode 的辨別為 /NameService/Server1;
znode 可以有子節點目錄,并且每個 znode 可以存儲資料,注意 EPHEMERAL 類型的目錄節點不能有子節點目錄(因為它是臨時節點);
znode 是有版本的,每個 znode 中存儲的資料可以有多個版本,也就是一個通路路徑中可以存儲多份資料;
znode 可以是臨時節點,一旦建立這個 znode 的用戶端與伺服器失去聯系,這個 znode 也将自動删除,Zookeeper 的用戶端和伺服器通信采用長連接配接方式,
每個用戶端和伺服器通過心跳來保持連接配接,這個連接配接狀态稱為 session,如果 znode 是臨時節點,這個 session 失效,znode 也就删除了;
znode 的目錄名可以自動編号,如 App1 已經存在,再建立的話,将會自動命名為 App2;
znode 可以被監控,包括這個目錄節點中存儲的資料的修改,子節點目錄的變化等,一旦變化可以通知設定監控的用戶端,這個是 Zookeeper 的核心特性,
Zookeeper 的很多功能都是基于這個特性實作的,後面在典型的應用場景中會有執行個體介紹。
1)Znode有兩種類型:
短暫(ephemeral):用戶端和伺服器端斷開連接配接後,建立的節點自己删除。
持久(persistent):用戶端和伺服器端斷開連接配接後,建立的節點不删除。
2)Znode有四種形式的目錄節點(預設是persistent )
(1)持久化目錄節點(PERSISTENT) 用戶端與zookeeper斷開連接配接後,該節點依舊存在。
(2)持久化順序編号目錄節點(PERSISTENT_SEQUENTIAL) 用戶端與zookeeper斷開連接配接後,該節點依舊存在,
隻是Zookeeper給該節點名稱進行順序編号。
(3)臨時目錄節點(EPHEMERAL) 用戶端與zookeeper斷開連接配接後,該節點被删除。
(4)臨時順序編号目錄節點(EPHEMERAL_SEQUENTIAL)用戶端與zookeeper斷開連接配接後,該節點被删除,隻是Zookeeper給該節點名稱進行順序編号。
ZooKeeper選擇了基于通知(notification)的機制,即:用戶端向ZooKeeper注冊需要接受通知的znode,通過znode設定監控點(watch)來接受通知。
監視點是一個單次觸發的操作,意即監視點會觸發一個通知。為了接收多個通知,用戶端必須在每次通知後設定一個新的監視點。在下圖闡述的情況下,
當節點/task發生變化時,用戶端會受到一個通知,并從ZooKeeper讀取一個新值。
在應用程式中,mian()方法首先會建立zkClient,建立zkClient的同時就會産生兩個程序,即Listener程序(監聽程序)和
connect程序(網絡連接配接/傳輸程序),當zkClient調用getChildren()等方法注冊螢幕時,connect程序向ZooKeeper注冊監聽器,
注冊後的監聽器位于ZooKeeper的監聽器清單中,監聽器清單中記錄了zkClient的IP,端口号以及要監控的路徑,一旦目标檔案發生變化,
ZooKeeper就會把這條消息發送給對應的zkClient的Listener()程序,Listener程序接收到後,就會執行process()方法,
在process()方法中針對發生的事件進行處理。
2888:Follower與Leader交換資訊的端口。
3888:萬一叢集中的Leader伺服器挂了,需要一個端口來重新進行選舉,選出一個新的Leader,而這個端口就是用來執行選舉時伺服器互相通信的端口。
ZooKeeper的部署方式有單機模式和叢集模式,叢集中的角色有Leader和Follower,叢集最少3(2N+1)台,根據選舉算法,應保證奇數。
對于ZooKeeper叢集,過半存活即可使用。
Paxos算法是分布式選舉算法,Zookeeper使用的 ZAB協定(Zookeeper原子廣播),兩者的異同如下:
1)相同之處:
比如都有一個Leader,用來協調N個Follower的運作;Leader要等待超半數的Follower做出正确回報之後才進行提案;
二者都有一個值來代表Leader的周期。
2)不同之處:
ZAB用來建構高可用的分布式資料主備系統(Zookeeper),Paxos是用來建構分布式一緻性狀态機系統。
ZooKeeper對于事務性的支援主要依賴于四個函數,zoo_create_op_init, zoo_delete_op_init, zoo_set_op_init以及zoo_check_op_init。
每一個函數都會在用戶端初始化一個operation,用戶端程式有義務保留這些operations。當準備好一個事務中的所有操作後,可以使用zoo_multi來送出所有的操作,
由zookeeper服務來保證這一系列操作的原子性。也就是說隻要其中有一個操作失敗了,相當于此次送出的任何一個操作都沒有對服務端的資料造成影響。
Zoo_multi的傳回值是第一個失敗操作的狀态信号。