個(gè)人專題目錄
3.1 選舉機(jī)制(重點(diǎn))
半數(shù)機(jī)制:集群中半數(shù)以上機(jī)器存活场躯,集群可用谈为。所以Zookeeper適合安裝奇數(shù)臺(tái)服務(wù)器。
Zookeeper雖然在配置文件中并沒有指定
Master和Slave
踢关。但是伞鲫,Zookeeper工作時(shí),是有一個(gè)節(jié)點(diǎn)為L(zhǎng)eader签舞,其他則為Follower榔昔,Leader是通過內(nèi)部的選舉機(jī)制臨時(shí)產(chǎn)生的驹闰。以一個(gè)簡(jiǎn)單的例子來(lái)說(shuō)明整個(gè)選舉的過程。
假設(shè)有五臺(tái)服務(wù)器組成的Zookeeper集群撒会,它們的id從1-5,同時(shí)它們都是最新啟動(dòng)的师妙,也就是沒有歷史數(shù)據(jù)诵肛,在存放數(shù)據(jù)量這一點(diǎn)上,都是一樣的默穴。假設(shè)這些服務(wù)器依序啟動(dòng)怔檩,來(lái)看看會(huì)發(fā)生什么,如圖所示蓄诽。
- 服務(wù)器1啟動(dòng)薛训,此時(shí)只有它一臺(tái)服務(wù)器啟動(dòng)了,它發(fā)出去的報(bào)文沒有任何響應(yīng)仑氛,所以它的選舉狀態(tài)一直是LOOKING狀態(tài)乙埃。
- 服務(wù)器2啟動(dòng),它與最開始啟動(dòng)的服務(wù)器1進(jìn)行通信锯岖,互相交換自己的選舉結(jié)果介袜,由于兩者都沒有歷史數(shù)據(jù),所以id值較大的服務(wù)器2勝出出吹,但是由于沒有達(dá)到超過半數(shù)以上的服務(wù)器都同意選舉它(這個(gè)例子中的半數(shù)以上是3)遇伞,所以服務(wù)器1、2還是繼續(xù)保持LOOKING狀態(tài)捶牢。
- 服務(wù)器3啟動(dòng)鸠珠,根據(jù)前面的理論分析,服務(wù)器3成為服務(wù)器1秋麸、2渐排、3中的老大,而與上面不同的是竹勉,此時(shí)有三臺(tái)服務(wù)器選舉了它飞盆,所以它成為了這次選舉的Leader。
- 服務(wù)器4啟動(dòng)次乓,根據(jù)前面的分析吓歇,理論上服務(wù)器4應(yīng)該是服務(wù)器1、2票腰、3城看、4中最大的,但是由于前面已經(jīng)有半數(shù)以上的服務(wù)器選舉了服務(wù)器3杏慰,所以它只能接收當(dāng)小弟的命了测柠。
- 服務(wù)器5啟動(dòng)炼鞠,同4一樣當(dāng)小弟。
3.2 節(jié)點(diǎn)類型
- Znode有兩種類型:
短暫(ephemeral):客戶端和服務(wù)器端斷開連接后轰胁,創(chuàng)建的節(jié)點(diǎn)自動(dòng)刪除
持久(persistent):客戶端和服務(wù)器端斷開連接后谒主,創(chuàng)建的節(jié)點(diǎn)不刪除
- Znode有四種形式的目錄節(jié)點(diǎn)(默認(rèn)是persistent )
(1)持久化目錄節(jié)點(diǎn)(PERSISTENT)
客戶端與zookeeper斷開連接后,該節(jié)點(diǎn)依舊存在
(2)持久化順序編號(hào)目錄節(jié)點(diǎn)(PERSISTENT_SEQUENTIAL)
客戶端與zookeeper斷開連接后赃阀,該節(jié)點(diǎn)依舊存在霎肯,只是Zookeeper給該節(jié)點(diǎn)名稱進(jìn)行順序編號(hào)
(3)臨時(shí)目錄節(jié)點(diǎn)(EPHEMERAL)
客戶端與zookeeper斷開連接后,該節(jié)點(diǎn)被刪除
(4)臨時(shí)順序編號(hào)目錄節(jié)點(diǎn)(EPHEMERAL_SEQUENTIAL)
客戶端與zookeeper斷開連接后榛斯,該節(jié)點(diǎn)被刪除观游,只是Zookeeper給該節(jié)點(diǎn)名稱進(jìn)行順序編號(hào)
- 創(chuàng)建znode時(shí)設(shè)置順序標(biāo)識(shí),znode名稱后會(huì)附加一個(gè)值驮俗,順序號(hào)是一個(gè)單調(diào)遞增的計(jì)數(shù)器懂缕,由父節(jié)點(diǎn)維護(hù)
- 在分布式系統(tǒng)中,順序號(hào)可以被用于為所有的事件進(jìn)行全局排序王凑,這樣客戶端可以通過順序號(hào)推斷事件的順序
Znode維護(hù)了一個(gè)stat結(jié)構(gòu)搪柑,這個(gè)stat包含數(shù)據(jù)變化的版本號(hào)、訪問控制列表變化荤崇、還有時(shí)間戳拌屏。版本號(hào)和時(shí)間戳一起,可讓Zookeeper驗(yàn)證緩存和協(xié)調(diào)更新术荤。每次znode的數(shù)據(jù)發(fā)生了變化倚喂,版本號(hào)就增加。
例如瓣戚,無(wú)論何時(shí)客戶端檢索數(shù)據(jù)端圈,它也一起檢索數(shù)據(jù)的版本號(hào)。并且當(dāng)客戶端執(zhí)行更新或刪除時(shí)子库,客戶端必須提供他正在改變的znode的版本號(hào)舱权。如果它提供的版本號(hào)和真實(shí)的數(shù)據(jù)版本號(hào)不一致,更新將會(huì)失敗仑嗅。
zookeeper內(nèi)部維護(hù)了一套類似UNIX的樹形數(shù)據(jù)結(jié)構(gòu):由znode構(gòu)成的集合宴倍,
znode的集合又是一個(gè)樹形結(jié)構(gòu),
每一個(gè)znode又有很多屬性進(jìn)行描述仓技。 Znode = path + data + Stat
znode是由客戶端創(chuàng)建的鸵贬,它和創(chuàng)建它的客戶端的內(nèi)在聯(lián)系,決定了它的存在性:
PERSISTENT-持久化節(jié)點(diǎn):創(chuàng)建這個(gè)節(jié)點(diǎn)的客戶端在與zookeeper服務(wù)的連接斷開后脖捻,這個(gè)節(jié)點(diǎn)也不會(huì)被刪除(除非您使用API強(qiáng)制刪除)阔逼。
PERSISTENT_SEQUENTIAL-持久化順序編號(hào)節(jié)點(diǎn):當(dāng)客戶端請(qǐng)求創(chuàng)建這個(gè)節(jié)點(diǎn)A后,zookeeper會(huì)根據(jù)parent-znode的zxid狀態(tài)地沮,為這個(gè)A節(jié)點(diǎn)編寫一個(gè)全目錄唯一的編號(hào)(這個(gè)編號(hào)只會(huì)一直增長(zhǎng))嗜浮。當(dāng)客戶端與zookeeper服務(wù)的連接斷開后羡亩,這個(gè)節(jié)點(diǎn)也不會(huì)被刪除。
EPHEMERAL-臨時(shí)目錄節(jié)點(diǎn):創(chuàng)建這個(gè)節(jié)點(diǎn)的客戶端在與zookeeper服務(wù)的連接斷開后危融,這個(gè)節(jié)點(diǎn)(還有涉及到的子節(jié)點(diǎn))就會(huì)被刪除畏铆。
EPHEMERAL_SEQUENTIAL-臨時(shí)順序編號(hào)目錄節(jié)點(diǎn):當(dāng)客戶端請(qǐng)求創(chuàng)建這個(gè)節(jié)點(diǎn)A后,zookeeper會(huì)根據(jù)parent-znode的zxid狀態(tài)专挪,為這個(gè)A節(jié)點(diǎn)編寫一個(gè)全目錄唯一的編號(hào)(這個(gè)編號(hào)只會(huì)一直增長(zhǎng))及志。當(dāng)創(chuàng)建這個(gè)節(jié)點(diǎn)的客戶端與zookeeper服務(wù)的連接斷開后,這個(gè)節(jié)點(diǎn)被刪除寨腔。
另外,無(wú)論是EPHEMERAL還是EPHEMERAL_SEQUENTIAL節(jié)點(diǎn)類型率寡,在zookeeper的client異常終止后迫卢,節(jié)點(diǎn)也會(huì)被刪除
3.3 Stat結(jié)構(gòu)體
czxid-創(chuàng)建節(jié)點(diǎn)的事務(wù)zxid
每次修改ZooKeeper狀態(tài)都會(huì)收到一個(gè)zxid形式的時(shí)間戳,也就是ZooKeeper事務(wù)ID冶共。
事務(wù)ID是ZooKeeper中所有修改總的次序乾蛤。每個(gè)修改都有唯一的zxid,如果zxid1小于zxid2捅僵,那么zxid1在zxid2之前發(fā)生家卖。
ctime - znode被創(chuàng)建的毫秒數(shù)(從1970年開始)
mzxid - znode最后更新的事務(wù)zxid
mtime - znode最后修改的毫秒數(shù)(從1970年開始)
pZxid-znode最后更新的子節(jié)點(diǎn)zxid
cversion - znode子節(jié)點(diǎn)變化號(hào),znode子節(jié)點(diǎn)修改次數(shù)
dataversion - znode數(shù)據(jù)變化號(hào)
aclVersion - znode訪問控制列表的變化號(hào)
ephemeralOwner- 如果是臨時(shí)節(jié)點(diǎn)庙楚,這個(gè)是znode擁有者的session id上荡。如果不是臨時(shí)節(jié)點(diǎn)則是0。
dataLength- znode的數(shù)據(jù)長(zhǎng)度
numChildren - znode子節(jié)點(diǎn)數(shù)量
3.4 監(jiān)聽器原理(重點(diǎn))
客戶端注冊(cè)監(jiān)聽它關(guān)心的目錄節(jié)點(diǎn)馒闷,
當(dāng)目錄節(jié)點(diǎn)發(fā)生變化(數(shù)據(jù)改變酪捡、被刪除、子目錄節(jié)點(diǎn)增加刪除)時(shí)纳账,
zookeeper會(huì)通知客戶端逛薇。
ZooKeeper 支持watch(觀察)的概念∈璩妫客戶端可以在每個(gè)znode結(jié)點(diǎn)上設(shè)置一個(gè)觀察永罚。如果被觀察服務(wù)端的znode結(jié)點(diǎn)有變更,那么watch就會(huì)被觸發(fā)卧秘,這個(gè)watch所屬的客戶端將接收到一個(gè)通知包被告知結(jié)點(diǎn)已經(jīng)發(fā)生變化呢袱,把相應(yīng)的事件通知給設(shè)置過Watcher的Client端。
Zookeeper里的所有讀取操作:getData(),getChildren()和exists()都有設(shè)置watch的選項(xiàng)
一次觸發(fā)
當(dāng)數(shù)據(jù)有了變化時(shí)zkserver向客戶端發(fā)送一個(gè)watch,它是一次性的動(dòng)作斯议,即觸發(fā)一次就不再有效产捞,類似一次性紙杯。
只監(jiān)控一次
如果想繼續(xù)Watch的話哼御,需要客戶端重新設(shè)置Watcher坯临。因此如果你得到一個(gè)watch事件且想在將來(lái)的變化得到通知焊唬,必須新設(shè)置另一個(gè)watch。
發(fā)往客戶端
Watches是異步發(fā)往客戶端的看靠,Zookeeper提供一個(gè)順序保證:在看到watch事件之前絕不會(huì)看到變化赶促,這樣不同客戶端看到的是一致性的順序。
在(導(dǎo)致觀察事件被觸發(fā)的)修改操作的成功返回碼到達(dá)客戶端之前挟炬,事件可能在去往客戶端的路上鸥滨,但是可能不會(huì)到達(dá)客戶端。觀察事件是異步地發(fā)送給觀察者(客戶端)的谤祖。ZooKeeper會(huì)保證次序:在收到觀察事件之前婿滓,客戶端不會(huì)看到已經(jīng)為之設(shè)置觀察的節(jié)點(diǎn)的改動(dòng)。網(wǎng)絡(luò)延遲或者其他因素可能會(huì)讓不同的客戶端在不同的時(shí)間收到觀察事件和更新操作的返回碼粥喜。這里的要點(diǎn)是:不同客戶端看到的事情都有一致的次序凸主。
為數(shù)據(jù)設(shè)置watch
節(jié)點(diǎn)有不同的改動(dòng)方式《钕妫可以認(rèn)為ZooKeeper維護(hù)兩個(gè)觀察列表:數(shù)據(jù)觀察和子節(jié)點(diǎn)觀察
卿吐。getData()和exists()設(shè)置數(shù)據(jù)觀察。getChildren()設(shè)置子節(jié)點(diǎn)觀察锋华。此外嗡官,還可以認(rèn)為不同的返回?cái)?shù)據(jù)有不同的觀察。getData()和exists()返回節(jié)點(diǎn)的數(shù)據(jù)毯焕,而getChildren()返回子節(jié)點(diǎn)列表衍腥。所以,setData()將為znode觸發(fā)數(shù)據(jù)觀察芥丧。成功的create()將為新創(chuàng)建的節(jié)點(diǎn)觸發(fā)數(shù)據(jù)觀察紧阔,為其父節(jié)點(diǎn)觸發(fā)子節(jié)點(diǎn)觀察。成功的delete()將會(huì)為被刪除的節(jié)點(diǎn)觸發(fā)數(shù)據(jù)觀察以及子節(jié)點(diǎn)觀察(因?yàn)楣?jié)點(diǎn)不能再有子節(jié)點(diǎn)了)续担,為其父節(jié)點(diǎn)觸發(fā)子節(jié)點(diǎn)觀察擅耽。
觀察維護(hù)在客戶端連接到的ZooKeeper服務(wù)器中。這讓觀察的設(shè)置物遇、維護(hù)和分發(fā)是輕量級(jí)的乖仇。客戶端連接到新的服務(wù)器時(shí)询兴,所有會(huì)話事件將被觸發(fā)乃沙。同服務(wù)器斷開連接期間不會(huì)收到觀察∈ⅲ客戶端重新連接時(shí)警儒,如果需要,先前已經(jīng)注冊(cè)的觀察將被重新注冊(cè)和觸發(fā)。通常這都是透明的蜀铲。有一種情況下觀察事件將丟失:對(duì)還沒有創(chuàng)建的節(jié)點(diǎn)設(shè)置存在觀察边琉,而在斷開連接期間創(chuàng)建節(jié)點(diǎn),然后刪除记劝。
時(shí)序性和一致性
Watches是在client連接到Zookeeper服務(wù)端的本地維護(hù)变姨,這可讓watches成為輕量的,可維護(hù)的和派發(fā)的厌丑。當(dāng)一個(gè)client連接到新server定欧,watch將會(huì)觸發(fā)任何session事件,斷開連接后不能接收到怒竿。當(dāng)客戶端重連砍鸠,先前注冊(cè)的watches將會(huì)被重新注冊(cè)并觸發(fā)抹腿。
關(guān)于watches辽聊,Zookeeper維護(hù)這些保證:
(1)Watches和其他事件胞得、watches和異步恢復(fù)都是有序的埂息。Zookeeper客戶端保證每件事都是有序派發(fā)
(2)客戶端在看到新數(shù)據(jù)之前先看到watch事件
(3)對(duì)應(yīng)更新順序的watches事件順序由Zookeeper服務(wù)所見
- 監(jiān)聽原理詳解:
1)首先要有一個(gè)main()線程
2)在main線程中創(chuàng)建Zookeeper客戶端,這時(shí)就會(huì)創(chuàng)建兩個(gè)線程晋控,一個(gè)負(fù)責(zé)網(wǎng)絡(luò)連接通信(connet),一個(gè)負(fù)責(zé)監(jiān)聽(listener)。
3)通過connect線程將注冊(cè)的監(jiān)聽事件發(fā)送給Zookeeper厚骗。
4)在Zookeeper的注冊(cè)監(jiān)聽器列表中將注冊(cè)的監(jiān)聽事件添加到列表中。
5)Zookeeper監(jiān)聽到有數(shù)據(jù)或路徑變化兢哭,就會(huì)將這個(gè)消息發(fā)送給listener線程领舰。
6)listener線程內(nèi)部調(diào)用了process()方法。
- 常見的監(jiān)聽
(1)監(jiān)聽節(jié)點(diǎn)數(shù)據(jù)的變化:
get path [watch]
(2)監(jiān)聽子節(jié)點(diǎn)增減的變化
ls path [watch]
3.5 寫數(shù)據(jù)流程
- Client 向 ZooKeeper 的 Server1 上寫數(shù)據(jù)迟螺,發(fā)送一個(gè)寫請(qǐng)求冲秽。
- 如果Server1不是Leader,那么Server1 會(huì)把接受到的請(qǐng)求進(jìn)一步轉(zhuǎn)發(fā)給Leader矩父,因?yàn)槊總€(gè)ZooKeeper的Server里面有一個(gè)是Leader锉桑。這個(gè)Leader 會(huì)將寫請(qǐng)求廣播給各個(gè)Server,比如Server1和Server2窍株,各個(gè)Server寫成功后就會(huì)通知Leader民轴。
- 當(dāng)Leader收到大多數(shù) Server 數(shù)據(jù)寫成功了,那么就說(shuō)明數(shù)據(jù)寫成功了球订。如果這里三個(gè)節(jié)點(diǎn)的話后裸,只要有兩個(gè)節(jié)點(diǎn)數(shù)據(jù)寫成功了,那么就認(rèn)為數(shù)據(jù)寫成功了冒滩。寫成功之后微驶,Leader會(huì)告訴Server1數(shù)據(jù)寫成功了。
- Server1會(huì)進(jìn)一步通知 Client 數(shù)據(jù)寫成功了开睡,這時(shí)就認(rèn)為整個(gè)寫操作成功因苹。ZooKeeper 整個(gè)寫數(shù)據(jù)流程就是這樣的苟耻。