個人專題目錄
3.1 選舉機制(重點)
半數機制:集群中半數以上機器存活,集群可用。所以Zookeeper適合安裝奇數臺服務器。
Zookeeper雖然在配置文件中并沒有指定
Master和Slave
。但是,Zookeeper工作時,是有一個節點為Leader,其他則為Follower,Leader是通過內部的選舉機制臨時產生的。以一個簡單的例子來說明整個選舉的過程。
假設有五臺服務器組成的Zookeeper集群,它們的id從1-5,同時它們都是最新啟動的,也就是沒有歷史數據,在存放數據量這一點上,都是一樣的。假設這些服務器依序啟動,來看看會發生什么,如圖所示。
- 服務器1啟動,此時只有它一臺服務器啟動了,它發出去的報文沒有任何響應,所以它的選舉狀態一直是LOOKING狀態。
- 服務器2啟動,它與最開始啟動的服務器1進行通信,互相交換自己的選舉結果,由于兩者都沒有歷史數據,所以id值較大的服務器2勝出,但是由于沒有達到超過半數以上的服務器都同意選舉它(這個例子中的半數以上是3),所以服務器1、2還是繼續保持LOOKING狀態。
- 服務器3啟動,根據前面的理論分析,服務器3成為服務器1、2、3中的老大,而與上面不同的是,此時有三臺服務器選舉了它,所以它成為了這次選舉的Leader。
- 服務器4啟動,根據前面的分析,理論上服務器4應該是服務器1、2、3、4中最大的,但是由于前面已經有半數以上的服務器選舉了服務器3,所以它只能接收當小弟的命了。
- 服務器5啟動,同4一樣當小弟。
3.2 節點類型
- Znode有兩種類型:
短暫(ephemeral):客戶端和服務器端斷開連接后,創建的節點自動刪除
持久(persistent):客戶端和服務器端斷開連接后,創建的節點不刪除
- Znode有四種形式的目錄節點(默認是persistent )
(1)持久化目錄節點(PERSISTENT)
客戶端與zookeeper斷開連接后,該節點依舊存在
(2)持久化順序編號目錄節點(PERSISTENT_SEQUENTIAL)
客戶端與zookeeper斷開連接后,該節點依舊存在,只是Zookeeper給該節點名稱進行順序編號
(3)臨時目錄節點(EPHEMERAL)
客戶端與zookeeper斷開連接后,該節點被刪除
(4)臨時順序編號目錄節點(EPHEMERAL_SEQUENTIAL)
客戶端與zookeeper斷開連接后,該節點被刪除,只是Zookeeper給該節點名稱進行順序編號
- 創建znode時設置順序標識,znode名稱后會附加一個值,順序號是一個單調遞增的計數器,由父節點維護
- 在分布式系統中,順序號可以被用于為所有的事件進行全局排序,這樣客戶端可以通過順序號推斷事件的順序
Znode維護了一個stat結構,這個stat包含數據變化的版本號、訪問控制列表變化、還有時間戳。版本號和時間戳一起,可讓Zookeeper驗證緩存和協調更新。每次znode的數據發生了變化,版本號就增加。
例如,無論何時客戶端檢索數據,它也一起檢索數據的版本號。并且當客戶端執行更新或刪除時,客戶端必須提供他正在改變的znode的版本號。如果它提供的版本號和真實的數據版本號不一致,更新將會失敗。
zookeeper內部維護了一套類似UNIX的樹形數據結構:由znode構成的集合,
znode的集合又是一個樹形結構,
每一個znode又有很多屬性進行描述。 Znode = path + data + Stat
znode是由客戶端創建的,它和創建它的客戶端的內在聯系,決定了它的存在性:
PERSISTENT-持久化節點:創建這個節點的客戶端在與zookeeper服務的連接斷開后,這個節點也不會被刪除(除非您使用API強制刪除)。
PERSISTENT_SEQUENTIAL-持久化順序編號節點:當客戶端請求創建這個節點A后,zookeeper會根據parent-znode的zxid狀態,為這個A節點編寫一個全目錄唯一的編號(這個編號只會一直增長)。當客戶端與zookeeper服務的連接斷開后,這個節點也不會被刪除。
EPHEMERAL-臨時目錄節點:創建這個節點的客戶端在與zookeeper服務的連接斷開后,這個節點(還有涉及到的子節點)就會被刪除。
EPHEMERAL_SEQUENTIAL-臨時順序編號目錄節點:當客戶端請求創建這個節點A后,zookeeper會根據parent-znode的zxid狀態,為這個A節點編寫一個全目錄唯一的編號(這個編號只會一直增長)。當創建這個節點的客戶端與zookeeper服務的連接斷開后,這個節點被刪除。
另外,無論是EPHEMERAL還是EPHEMERAL_SEQUENTIAL節點類型,在zookeeper的client異常終止后,節點也會被刪除
3.3 Stat結構體
czxid-創建節點的事務zxid
每次修改ZooKeeper狀態都會收到一個zxid形式的時間戳,也就是ZooKeeper事務ID。
事務ID是ZooKeeper中所有修改總的次序。每個修改都有唯一的zxid,如果zxid1小于zxid2,那么zxid1在zxid2之前發生。
ctime - znode被創建的毫秒數(從1970年開始)
mzxid - znode最后更新的事務zxid
mtime - znode最后修改的毫秒數(從1970年開始)
pZxid-znode最后更新的子節點zxid
cversion - znode子節點變化號,znode子節點修改次數
dataversion - znode數據變化號
aclVersion - znode訪問控制列表的變化號
ephemeralOwner- 如果是臨時節點,這個是znode擁有者的session id。如果不是臨時節點則是0。
dataLength- znode的數據長度
numChildren - znode子節點數量
3.4 監聽器原理(重點)
客戶端注冊監聽它關心的目錄節點,
當目錄節點發生變化(數據改變、被刪除、子目錄節點增加刪除)時,
zookeeper會通知客戶端。
ZooKeeper 支持watch(觀察)的概念。客戶端可以在每個znode結點上設置一個觀察。如果被觀察服務端的znode結點有變更,那么watch就會被觸發,這個watch所屬的客戶端將接收到一個通知包被告知結點已經發生變化,把相應的事件通知給設置過Watcher的Client端。
Zookeeper里的所有讀取操作:getData(),getChildren()和exists()都有設置watch的選項
一次觸發
當數據有了變化時zkserver向客戶端發送一個watch,它是一次性的動作,即觸發一次就不再有效,類似一次性紙杯。
只監控一次
如果想繼續Watch的話,需要客戶端重新設置Watcher。因此如果你得到一個watch事件且想在將來的變化得到通知,必須新設置另一個watch。
發往客戶端
Watches是異步發往客戶端的,Zookeeper提供一個順序保證:在看到watch事件之前絕不會看到變化,這樣不同客戶端看到的是一致性的順序。
在(導致觀察事件被觸發的)修改操作的成功返回碼到達客戶端之前,事件可能在去往客戶端的路上,但是可能不會到達客戶端。觀察事件是異步地發送給觀察者(客戶端)的。ZooKeeper會保證次序:在收到觀察事件之前,客戶端不會看到已經為之設置觀察的節點的改動。網絡延遲或者其他因素可能會讓不同的客戶端在不同的時間收到觀察事件和更新操作的返回碼。這里的要點是:不同客戶端看到的事情都有一致的次序。
為數據設置watch
節點有不同的改動方式。可以認為ZooKeeper維護兩個觀察列表:數據觀察和子節點觀察
。getData()和exists()設置數據觀察。getChildren()設置子節點觀察。此外,還可以認為不同的返回數據有不同的觀察。getData()和exists()返回節點的數據,而getChildren()返回子節點列表。所以,setData()將為znode觸發數據觀察。成功的create()將為新創建的節點觸發數據觀察,為其父節點觸發子節點觀察。成功的delete()將會為被刪除的節點觸發數據觀察以及子節點觀察(因為節點不能再有子節點了),為其父節點觸發子節點觀察。
觀察維護在客戶端連接到的ZooKeeper服務器中。這讓觀察的設置、維護和分發是輕量級的。客戶端連接到新的服務器時,所有會話事件將被觸發。同服務器斷開連接期間不會收到觀察。客戶端重新連接時,如果需要,先前已經注冊的觀察將被重新注冊和觸發。通常這都是透明的。有一種情況下觀察事件將丟失:對還沒有創建的節點設置存在觀察,而在斷開連接期間創建節點,然后刪除。
時序性和一致性
Watches是在client連接到Zookeeper服務端的本地維護,這可讓watches成為輕量的,可維護的和派發的。當一個client連接到新server,watch將會觸發任何session事件,斷開連接后不能接收到。當客戶端重連,先前注冊的watches將會被重新注冊并觸發。
關于watches,Zookeeper維護這些保證:
(1)Watches和其他事件、watches和異步恢復都是有序的。Zookeeper客戶端保證每件事都是有序派發
(2)客戶端在看到新數據之前先看到watch事件
(3)對應更新順序的watches事件順序由Zookeeper服務所見
- 監聽原理詳解:
1)首先要有一個main()線程
2)在main線程中創建Zookeeper客戶端,這時就會創建兩個線程,一個負責網絡連接通信(connet),一個負責監聽(listener)。
3)通過connect線程將注冊的監聽事件發送給Zookeeper。
4)在Zookeeper的注冊監聽器列表中將注冊的監聽事件添加到列表中。
5)Zookeeper監聽到有數據或路徑變化,就會將這個消息發送給listener線程。
6)listener線程內部調用了process()方法。
- 常見的監聽
(1)監聽節點數據的變化:
get path [watch]
(2)監聽子節點增減的變化
ls path [watch]
3.5 寫數據流程
- Client 向 ZooKeeper 的 Server1 上寫數據,發送一個寫請求。
- 如果Server1不是Leader,那么Server1 會把接受到的請求進一步轉發給Leader,因為每個ZooKeeper的Server里面有一個是Leader。這個Leader 會將寫請求廣播給各個Server,比如Server1和Server2,各個Server寫成功后就會通知Leader。
- 當Leader收到大多數 Server 數據寫成功了,那么就說明數據寫成功了。如果這里三個節點的話,只要有兩個節點數據寫成功了,那么就認為數據寫成功了。寫成功之后,Leader會告訴Server1數據寫成功了。
- Server1會進一步通知 Client 數據寫成功了,這時就認為整個寫操作成功。ZooKeeper 整個寫數據流程就是這樣的。