zookeeper 用戶端編程

來源:互聯網
上載者:User

zookeeper是一個分布式的開源的分布式協調服務,用它可以來現同步服務,配置維護。zookeeper的穩定性也是可以
保證的,筆者曾參與過的使用zookeeper的兩個應用,一個是用zookeeper來做分布式鎖,屬於同步協調服務,另一個
是配置維護,三台機器運行了一年多了,沒有出現什麼問題。
------------------------
同步服務:利用zookeeper可以使機器之間的同步如同concurrent包下的同步器同步線程一樣,
舉兩個互斥同步的例子
1.  應用系統部署在多台機器上,需要啟動一個時間任務,而這個時間任務只能在一台機器上執行,否則會出現
髒讀髒寫,zookeeper可以實現。
2.  應用系統部署在多台機器上,需要暴露一個服務,而這個服務同樣不是全部的機器都有暴露,例如MASTER,
而暴露這個服務的機器宕機後,其它的機器能自動的接替暴露這個服務,zookeeper可以實現。但是這協調中有一定
的時間差問題。
------------------------
配置維護:服務叢集需要使用一組配置,為了保證配置在叢集中的一致性,以及對配置更改後的立即響應要求,
使用zookeeper可以實現。hadoop的配置中心就是使用zookeeper來做管理的。
------------------------
zookeeper應用機器結構

用戶端根據伺服器位址隨機的選擇一個地址進行串連,如果串連出錯會串連其他的地址,用戶端一直串連這個地址
知道這個伺服器不可用。用戶端發送的指令,所有修改資料的指令都由其已連線的服務器發送給LEADER進行處理,
查詢指令則在其串連的機器上進行。
zookeeper資料群組織形式

zookeeper上的資料結構類似於檔案結構,每一個節點上都可以寫入資料,可以建立葉子節點,也只能刪除葉子節點
同時可以對任意路徑進行監聽各種類型的事件,當該事件在該路徑發生時,已連線的服務器會發送NOTIFY包過來通知
用戶端進行註冊的WATCHER的回調處理。

zookeeper上的資料操作主要有CREATE,DELETE,EXISTS,GETDATA,SETDATA,GETCHILDREN
-------------------
CREATE建立一個節點,並在這個節點處寫入資料,建立節點有四種模式
1. PERSISTENT 建立持久化節點,對應機器關閉串連後節點/資料不會消失
2. PERSISTENT_SEQUENTIAL 如果PATH是以’/’結尾則以這個PATH作為父節點,建立一個子節點,其子節點名字是一個
按先後順序排列的數值;否則建立一個名字是 ‘/’後面字元加上先後順序排列的數值字串的節點,同樣建立
持久節點,例如:
path-------- PERSISTENT-------- PERSISTENT_SEQUENTIAL
/lock/------ error-----------------在/lock/下建立子節點: /lock/000001
/lock-------建立/lock節點-------建立節點 /lock000001
3. EPHEMERAL 建立瞬時節點,Zookeeper在感知串連機器宕機後會清除它建立的瞬節點
4. EPHEMERAL_SEQUENTIAL 穿件瞬時順序節點,和PERSISTENT_SEQUENTIAL一樣,區別在於它是瞬時的
-------------------
DELETE  -  刪除一個節點
EXISTS  -  查看一個節點的狀況,如果沒有返回null,可注入watcher
GETDATA  -  擷取一個節點的資料,可注入watcher
SETDATA  -  設定一個節點的資料
GETCHILDREN -  擷取一個節點的子節點,可注入watcher
-------------------

Watcher種類
1. zookeeper執行個體化時注入的預設Watcher
2. dataWatchers 一個Map<string Set<Watcher>>資料結構,儲存調用getData時 注入的Watcher或者調用exist時
path指定的節點存在
3. existWatchers 一個Map<string Set<Watcher>>資料結構,儲存調用exits時對應的path節點不存在條件下注入
的Watcher
4. childWatchers一個Map<string Set<Watcher>>資料結構,儲存調用getChildren 時注入的Watcher
預設Watcher主要是處理串連建立成功,串連斷了-會重連,SESSION失效-這個需要重新執行個體zookeeper對象這些事件
其他的三個Watcher都是MAP<String, SET<Watcher>>結構的,MAP中的KEY是zookeeper資料路徑,當調用
exists,getData,getChildren函數時會需要傳入一個Watcher,對應的path上的資料發生改變時,zookeeper伺服器
會發送NOTIFY包給用戶端,用戶端在收到NOTIFY包後根據包指明的path和事件類別目錄回調有關Watcher的process函數。
所有需要系統的管理這些Watcher,故分成三個MAP來管理這些Watcher。
當調用getData時將注入的Watcher加入到dataWatchers.get(path)的SET裡面,調用childWatchers時一樣。需要
重點注意的是當調用exists時,如果path不存在則加入到existWatchers.get(path)的SET裡面,否則加入到
dataWatchers.get(path)的SET裡面,這樣做的原因是為了保持和用戶端接收到NOTIFY包時處理一致。

事件處理
NodeDataChanged & NodeCreated
回調dataWatchers.get(path) & existWatchers.get(path)得到的Watcher集合,同時會將path這一MAP映射
從dataWatchers&existWatchers裡面清除
---------
NodeChildrenChanged
回調childWatchers.get(path得到的Watcher集合,同時會將path這一MAP映射從childWatchers.get裡面清除
---------
NodeDeleted
回調dataWatchers.get(path) & childWatchers.get(path)得到的Watcher集合,同時會將path這一MAP映射從
dataWatchers& childWatchers裡面清除,同時會將path這一MAP映射從existWatchers裡面清除
上面提到過對於exists的Watcher,為了保證Watcher能被回調,如果節點原本不存在的話,那麼這個節點就
只會有NodeCreated事件,故加入到existWatchers能找到,如果存在的話就可能是資料修改和刪除事件了,
如果是資料修改同樣可以監聽到,存在是加入到dataWatchers裡,同樣可以監聽到。
----------------------------------------------------
zookeeper用戶端的一些細節問題

建立串連需要指定zookeeper叢集的IP地址,SessionTimeout,預設的watcher。
在建立串連過程中根據叢集的IP地址隨機的選擇一個地址,然後啟動兩個線程,一個做網路,一個處理事件回調的。
做網路線程SendThread建立網路連接是使用的非阻塞模式,當串連建立成功後,會把一個ConnectRequest包加入到
發送隊列中,其中包括了用戶端自己可能制定的sessionId, sessionPasswd,和自己指定的sessionTimeout,如果需要
驗證資訊的話,驗證資訊包也會加入到發送隊列中。再到Selector上註冊OP_READ|OP_WRITE事件;如果串連沒建立
成功則等待Selector上OP_CONNECT事件,直到串連建立成功,進行上面提到的串連建立成功後的操作。
到目前為止ZooKeeper.States還是CONNECTING,接下來就是調用doIO()做讀寫操作,當前伺服器是不會發送資料
過來的,故沒有資料可讀,當時發送隊列裡面已經包含至少一個資料包了,故將其發送出去。
接著就是讀取伺服器的對串連包和驗證資訊的響應資料了,首先會讀取響應資料裡面的negotiatedSessionTimeout值,
如果小於等於零,會做四件事,一是往事件隊列裡面添加Watcher.Event.KeeperState.Expired事件,再往事件隊列裡
面委任標記線程死亡的對象,三是拋出SessionExpiredException異常,再就是ZooKeeper.States=CLOSED,三讓做
死迴圈邏輯的SendThread退到while語句處從而退出死迴圈,線程死掉。
如果negotiatedSessionTimeout是大於0的則進行本地邏輯資料的設定
readTimeout = negotiatedSessionTimeout * 2 / 3;
connectTimeout = negotiatedSessionTimeout / serverAddrs.size();
接著zooKeeper.state=CONNECTED。串連才真正建立成功,到這個點zookeeper提供的操作才可以使用。
接著SendThread走其正常的邏輯
--------------
1. 如果由於異常導致先前的串連斷開,會重新走上面提到的建立串連的過程
2. 進行串連空閑事件或者READ閒置事件的判斷,如果zooKeeper.state不是CONNECTED,則用上次做寫操作的
時間和目前時間的差值和connectTimeout作比較,如果大於的話會拋出SessionTimeoutException;如果上次
讀的時間和目前時間差值大於readTimeout 同樣拋出SessionTimeoutException,這樣通知上層其正在做重連操作,
同時通知上層其正在等待響應的資料包和沒有來得及發送的資料包。
3. 如果上次讀的時間和目前時間差值大於readTimeout/2則會做發送PING資料包的操作。
4. 如果當前可讀,則讀取響應資料包,根據響應的資料類型進行處理,共分為四種,一種是對PING的響應;一種
是對於先前提到的驗證的響應包,如果驗證失敗和Expired一樣會導致SendThread退出;一種是NOTIFY資料包,
裡面指定了路徑和事件類型,根據上面提到的事件處理方式,往事件處理任務隊列裡添加這個事件給EventThread
線程處理;一種是使用者操作返回的響應資料包,對於這種資料包要核對這個包的ID號和等待響應的資料包的隊列的
第一個包的ID是否一致,如果一致則進行關於這個包的下一步的處理,如包含Watcher則加入到上面提到的Watcher
管理資料結構裡面,再從等待響應的資料包的隊列裡移除,對於zookeeper資料結構的操作分為同步和非同步,如果是
同步操作,提交操作資料包的線程還需要在此次喚醒,如果是非同步則向事件處理任務隊列裡添加回調任務;如果ID
不一致則說明發送了錯誤,則拋出IOException異常,導致重新走上面提到的建立串連的過程。
zookeeper對於當個用戶端提交的任務是FIFO的,用來保證其[sequential consistency for single client]。
5. 如果當前發送任務隊列有資料則做發送操作,在發送時會為每個資料分配全域唯一的ID號,在發送完成後
將這個包移動到已經發送在等待回應的隊列中去。
6. 做完IO後在回到1之前會進行是否退出的判斷,如果zooKeeper.state是CLOSED|AUTH_FAILED會退出,否則回到1。
--------------
1. 在讀取資料的過程中如果發送IO錯誤,或者邏輯錯誤而拋出的異常,都會被抓捕到,進行後續處理:
關閉當前的Socket的輸入輸出資料流和Socket,對於還在帶發送隊列裡面和在已經發送出去的資料包,如果是同步
的需要喚醒這些提交包的線程,如果是非同步則將通知任務失敗的任務提交到EventThread的任務隊列裡面,再
清除這兩個隊列。如果不是因為Expired或者驗證失敗則會通知預設的Watcher其重連,如果是因為這兩個事件,
這個zookeeper對象已經不可用了,不會再重連了,用戶端在接受到這兩個事件時需要重新執行個體化zookeeper對象。
2. Watcher在被回調一次後會再 Watcher管理器中刪除,如果想繼續監聽這個事件,需要繼續注入。
3. 注意臨時資料的問題,如果用戶端建立了一個臨時資料,然後重啟了機器,在短時間內,這個時間跟伺服器配置的
SESSIONTIMEOUT有關,是存在的,過了這個時間點就會消失,這個時間要注意。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.