三、Zookeeper內(nèi)部原理

個人專題目錄


3.1 選舉機(jī)制(重點(diǎn))

  • 半數(shù)機(jī)制:集群中半數(shù)以上機(jī)器存活,集群可用。所以Zookeeper適合安裝奇數(shù)臺服務(wù)器。

  • Zookeeper雖然在配置文件中并沒有指定Master和Slave。但是,Zookeeper工作時,是有一個節(jié)點(diǎn)為Leader,其他則為Follower,Leader是通過內(nèi)部的選舉機(jī)制臨時產(chǎn)生的。

  • 以一個簡單的例子來說明整個選舉的過程。

假設(shè)有五臺服務(wù)器組成的Zookeeper集群,它們的id從1-5,同時它們都是最新啟動的,也就是沒有歷史數(shù)據(jù),在存放數(shù)據(jù)量這一點(diǎn)上,都是一樣的。假設(shè)這些服務(wù)器依序啟動,來看看會發(fā)生什么,如圖所示。

1546055458160.png
  1. 服務(wù)器1啟動,此時只有它一臺服務(wù)器啟動了,它發(fā)出去的報文沒有任何響應(yīng),所以它的選舉狀態(tài)一直是LOOKING狀態(tài)。
  2. 服務(wù)器2啟動,它與最開始啟動的服務(wù)器1進(jìn)行通信,互相交換自己的選舉結(jié)果,由于兩者都沒有歷史數(shù)據(jù),所以id值較大的服務(wù)器2勝出,但是由于沒有達(dá)到超過半數(shù)以上的服務(wù)器都同意選舉它(這個例子中的半數(shù)以上是3),所以服務(wù)器1、2還是繼續(xù)保持LOOKING狀態(tài)。
  3. 服務(wù)器3啟動,根據(jù)前面的理論分析,服務(wù)器3成為服務(wù)器1、2、3中的老大,而與上面不同的是,此時有三臺服務(wù)器選舉了它,所以它成為了這次選舉的Leader。
  4. 服務(wù)器4啟動,根據(jù)前面的分析,理論上服務(wù)器4應(yīng)該是服務(wù)器1、2、3、4中最大的,但是由于前面已經(jīng)有半數(shù)以上的服務(wù)器選舉了服務(wù)器3,所以它只能接收當(dāng)小弟的命了。
  5. 服務(wù)器5啟動,同4一樣當(dāng)小弟。

3.2 節(jié)點(diǎn)類型

  • Znode有兩種類型:

短暫(ephemeral):客戶端和服務(wù)器端斷開連接后,創(chuàng)建的節(jié)點(diǎn)自動刪除

持久(persistent):客戶端和服務(wù)器端斷開連接后,創(chuàng)建的節(jié)點(diǎn)不刪除

  • Znode有四種形式的目錄節(jié)點(diǎn)(默認(rèn)是persistent )

(1)持久化目錄節(jié)點(diǎn)(PERSISTENT)

    客戶端與zookeeper斷開連接后,該節(jié)點(diǎn)依舊存在

(2)持久化順序編號目錄節(jié)點(diǎn)(PERSISTENT_SEQUENTIAL)

    客戶端與zookeeper斷開連接后,該節(jié)點(diǎn)依舊存在,只是Zookeeper給該節(jié)點(diǎn)名稱進(jìn)行順序編號

(3)臨時目錄節(jié)點(diǎn)(EPHEMERAL)

客戶端與zookeeper斷開連接后,該節(jié)點(diǎn)被刪除

(4)臨時順序編號目錄節(jié)點(diǎn)(EPHEMERAL_SEQUENTIAL)

客戶端與zookeeper斷開連接后,該節(jié)點(diǎn)被刪除,只是Zookeeper給該節(jié)點(diǎn)名稱進(jìn)行順序編號

  • 創(chuàng)建znode時設(shè)置順序標(biāo)識,znode名稱后會附加一個值,順序號是一個單調(diào)遞增的計數(shù)器,由父節(jié)點(diǎn)維護(hù)
  • 在分布式系統(tǒng)中,順序號可以被用于為所有的事件進(jìn)行全局排序,這樣客戶端可以通過順序號推斷事件的順序

Znode維護(hù)了一個stat結(jié)構(gòu),這個stat包含數(shù)據(jù)變化的版本號、訪問控制列表變化、還有時間戳。版本號和時間戳一起,可讓Zookeeper驗證緩存和協(xié)調(diào)更新。每次znode的數(shù)據(jù)發(fā)生了變化,版本號就增加。

例如,無論何時客戶端檢索數(shù)據(jù),它也一起檢索數(shù)據(jù)的版本號。并且當(dāng)客戶端執(zhí)行更新或刪除時,客戶端必須提供他正在改變的znode的版本號。如果它提供的版本號和真實的數(shù)據(jù)版本號不一致,更新將會失敗。

zookeeper內(nèi)部維護(hù)了一套類似UNIX的樹形數(shù)據(jù)結(jié)構(gòu):由znode構(gòu)成的集合,

znode的集合又是一個樹形結(jié)構(gòu),
每一個znode又有很多屬性進(jìn)行描述。 Znode = path + data + Stat

znode是由客戶端創(chuàng)建的,它和創(chuàng)建它的客戶端的內(nèi)在聯(lián)系,決定了它的存在性:

PERSISTENT-持久化節(jié)點(diǎn):創(chuàng)建這個節(jié)點(diǎn)的客戶端在與zookeeper服務(wù)的連接斷開后,這個節(jié)點(diǎn)也不會被刪除(除非您使用API強(qiáng)制刪除)。

PERSISTENT_SEQUENTIAL-持久化順序編號節(jié)點(diǎn):當(dāng)客戶端請求創(chuàng)建這個節(jié)點(diǎn)A后,zookeeper會根據(jù)parent-znode的zxid狀態(tài),為這個A節(jié)點(diǎn)編寫一個全目錄唯一的編號(這個編號只會一直增長)。當(dāng)客戶端與zookeeper服務(wù)的連接斷開后,這個節(jié)點(diǎn)也不會被刪除。
 
EPHEMERAL-臨時目錄節(jié)點(diǎn):創(chuàng)建這個節(jié)點(diǎn)的客戶端在與zookeeper服務(wù)的連接斷開后,這個節(jié)點(diǎn)(還有涉及到的子節(jié)點(diǎn))就會被刪除。
 
EPHEMERAL_SEQUENTIAL-臨時順序編號目錄節(jié)點(diǎn):當(dāng)客戶端請求創(chuàng)建這個節(jié)點(diǎn)A后,zookeeper會根據(jù)parent-znode的zxid狀態(tài),為這個A節(jié)點(diǎn)編寫一個全目錄唯一的編號(這個編號只會一直增長)。當(dāng)創(chuàng)建這個節(jié)點(diǎn)的客戶端與zookeeper服務(wù)的連接斷開后,這個節(jié)點(diǎn)被刪除。
 
另外,無論是EPHEMERAL還是EPHEMERAL_SEQUENTIAL節(jié)點(diǎn)類型,在zookeeper的client異常終止后,節(jié)點(diǎn)也會被刪除

3.3 Stat結(jié)構(gòu)體

  1. czxid-創(chuàng)建節(jié)點(diǎn)的事務(wù)zxid

  2. 每次修改ZooKeeper狀態(tài)都會收到一個zxid形式的時間戳,也就是ZooKeeper事務(wù)ID。

  3. 事務(wù)ID是ZooKeeper中所有修改總的次序。每個修改都有唯一的zxid,如果zxid1小于zxid2,那么zxid1在zxid2之前發(fā)生。

  4. ctime - znode被創(chuàng)建的毫秒數(shù)(從1970年開始)

  5. mzxid - znode最后更新的事務(wù)zxid

  6. mtime - znode最后修改的毫秒數(shù)(從1970年開始)

  7. pZxid-znode最后更新的子節(jié)點(diǎn)zxid

  8. cversion - znode子節(jié)點(diǎn)變化號,znode子節(jié)點(diǎn)修改次數(shù)

  9. dataversion - znode數(shù)據(jù)變化號

  10. aclVersion - znode訪問控制列表的變化號

  11. ephemeralOwner- 如果是臨時節(jié)點(diǎn),這個是znode擁有者的session id。如果不是臨時節(jié)點(diǎn)則是0。

  12. dataLength- znode的數(shù)據(jù)長度

  13. numChildren - znode子節(jié)點(diǎn)數(shù)量

3.4 監(jiān)聽器原理(重點(diǎn))

客戶端注冊監(jiān)聽它關(guān)心的目錄節(jié)點(diǎn),
當(dāng)目錄節(jié)點(diǎn)發(fā)生變化(數(shù)據(jù)改變、被刪除、子目錄節(jié)點(diǎn)增加刪除)時,
zookeeper會通知客戶端。

ZooKeeper 支持watch(觀察)的概念。客戶端可以在每個znode結(jié)點(diǎn)上設(shè)置一個觀察。如果被觀察服務(wù)端的znode結(jié)點(diǎn)有變更,那么watch就會被觸發(fā),這個watch所屬的客戶端將接收到一個通知包被告知結(jié)點(diǎn)已經(jīng)發(fā)生變化,把相應(yīng)的事件通知給設(shè)置過Watcher的Client端。

Zookeeper里的所有讀取操作:getData(),getChildren()和exists()都有設(shè)置watch的選項

一次觸發(fā)

當(dāng)數(shù)據(jù)有了變化時zkserver向客戶端發(fā)送一個watch,它是一次性的動作,即觸發(fā)一次就不再有效,類似一次性紙杯。

只監(jiān)控一次

如果想繼續(xù)Watch的話,需要客戶端重新設(shè)置Watcher。因此如果你得到一個watch事件且想在將來的變化得到通知,必須新設(shè)置另一個watch。

發(fā)往客戶端

Watches是異步發(fā)往客戶端的,Zookeeper提供一個順序保證:在看到watch事件之前絕不會看到變化,這樣不同客戶端看到的是一致性的順序。

在(導(dǎo)致觀察事件被觸發(fā)的)修改操作的成功返回碼到達(dá)客戶端之前,事件可能在去往客戶端的路上,但是可能不會到達(dá)客戶端。觀察事件是異步地發(fā)送給觀察者(客戶端)的。ZooKeeper會保證次序:在收到觀察事件之前,客戶端不會看到已經(jīng)為之設(shè)置觀察的節(jié)點(diǎn)的改動。網(wǎng)絡(luò)延遲或者其他因素可能會讓不同的客戶端在不同的時間收到觀察事件和更新操作的返回碼。這里的要點(diǎn)是:不同客戶端看到的事情都有一致的次序。

為數(shù)據(jù)設(shè)置watch

節(jié)點(diǎn)有不同的改動方式。可以認(rèn)為ZooKeeper維護(hù)兩個觀察列表:數(shù)據(jù)觀察和子節(jié)點(diǎn)觀察。getData()和exists()設(shè)置數(shù)據(jù)觀察。getChildren()設(shè)置子節(jié)點(diǎn)觀察。此外,還可以認(rèn)為不同的返回數(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()將會為被刪除的節(jié)點(diǎn)觸發(fā)數(shù)據(jù)觀察以及子節(jié)點(diǎn)觀察(因為節(jié)點(diǎn)不能再有子節(jié)點(diǎn)了),為其父節(jié)點(diǎn)觸發(fā)子節(jié)點(diǎn)觀察。

觀察維護(hù)在客戶端連接到的ZooKeeper服務(wù)器中。這讓觀察的設(shè)置、維護(hù)和分發(fā)是輕量級的??蛻舳诉B接到新的服務(wù)器時,所有會話事件將被觸發(fā)。同服務(wù)器斷開連接期間不會收到觀察??蛻舳酥匦逻B接時,如果需要,先前已經(jīng)注冊的觀察將被重新注冊和觸發(fā)。通常這都是透明的。有一種情況下觀察事件將丟失:對還沒有創(chuàng)建的節(jié)點(diǎn)設(shè)置存在觀察,而在斷開連接期間創(chuàng)建節(jié)點(diǎn),然后刪除。

時序性和一致性

Watches是在client連接到Zookeeper服務(wù)端的本地維護(hù),這可讓watches成為輕量的,可維護(hù)的和派發(fā)的。當(dāng)一個client連接到新server,watch將會觸發(fā)任何session事件,斷開連接后不能接收到。當(dāng)客戶端重連,先前注冊的watches將會被重新注冊并觸發(fā)。

關(guān)于watches,Zookeeper維護(hù)這些保證:
(1)Watches和其他事件、watches和異步恢復(fù)都是有序的。Zookeeper客戶端保證每件事都是有序派發(fā)
(2)客戶端在看到新數(shù)據(jù)之前先看到watch事件
(3)對應(yīng)更新順序的watches事件順序由Zookeeper服務(wù)所見

  • 監(jiān)聽原理詳解:

1)首先要有一個main()線程

2)在main線程中創(chuàng)建Zookeeper客戶端,這時就會創(chuàng)建兩個線程,一個負(fù)責(zé)網(wǎng)絡(luò)連接通信(connet),一個負(fù)責(zé)監(jiān)聽(listener)。

3)通過connect線程將注冊的監(jiān)聽事件發(fā)送給Zookeeper。

4)在Zookeeper的注冊監(jiān)聽器列表中將注冊的監(jiān)聽事件添加到列表中。

5)Zookeeper監(jiān)聽到有數(shù)據(jù)或路徑變化,就會將這個消息發(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ù)流程

  1. Client 向 ZooKeeper 的 Server1 上寫數(shù)據(jù),發(fā)送一個寫請求。
  2. 如果Server1不是Leader,那么Server1 會把接受到的請求進(jìn)一步轉(zhuǎn)發(fā)給Leader,因為每個ZooKeeper的Server里面有一個是Leader。這個Leader 會將寫請求廣播給各個Server,比如Server1和Server2,各個Server寫成功后就會通知Leader。
  3. 當(dāng)Leader收到大多數(shù) Server 數(shù)據(jù)寫成功了,那么就說明數(shù)據(jù)寫成功了。如果這里三個節(jié)點(diǎn)的話,只要有兩個節(jié)點(diǎn)數(shù)據(jù)寫成功了,那么就認(rèn)為數(shù)據(jù)寫成功了。寫成功之后,Leader會告訴Server1數(shù)據(jù)寫成功了。
  4. Server1會進(jìn)一步通知 Client 數(shù)據(jù)寫成功了,這時就認(rèn)為整個寫操作成功。ZooKeeper 整個寫數(shù)據(jù)流程就是這樣的。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容