文章轉載:http://agapple.iteye.com/blog/1292129
背景
繼續前面的zookeeper學習的專題,這次主要是結合項目中遇到的一些問題,進一步學習了下zookeeper的一些內部機制。
針對以下幾個問題:
1. zk是否可以保證watcher事件不丟失?
2. zk的EPHEMERAL節點的自動過期時間?
3. zk的如何保證節點數據不丟失?
如果你已經非常清楚這以上的幾個問題,看官們可以不用往下看了。
persit機制
zookeeper中的persit機制主要是通過本地disk進行持久化,在本地disk上會有個memory數據對象保持同步。
持久化實現:
ZKDatabase
- DataTree (內存樹)
- FileTxnSnapLog (disk持久化)
- committedLog (FileTxnSnapLog的一份內存數據cache,默認存儲500條變更記錄)
DataTree(內存樹)
zookeeper本身的數據結構就是一個樹結構
數據模型(DataTree):
- DataNode (1:n)
- data WatchManager (1:1,處理node節點的CRUD的變更事件,發送Watcher事件)
- child WatchManager (1:1, 處理node子節點的變更事件,發送Watcher事件)
- sessions (ephemerals)
- parent
- data byte[]
- acl(安全)
- stat(審計信息)
- children
FileTxnSnapLog (disk持久化)
持久化數據分兩類:
- TxnLog (類似於mysql/oracle的binlog/redolog)
- SnapShot (DataTree的數據鏡像)
- 首先節點啓動後,嘗試讀取本地的SnapShot log數據(zkDb.loadDataBase()),反序列化爲DataTree對象,並獲取last zxid。
- follower啓動後會向leader發送自己的last zxid
- leader收到zxid後,對比自己當前的ZKDatabase中的last zxid
如果當前follower的zxid在內存committedLog中,直接將內存中的committedLog提取出來進行發送,否則將當前的DataTree直接發送給follower.(不再是發送變更記錄) - 數據同步完成後,follower會開始接收request請求
一致性機制
整個zk集羣在處理數據變更過程中,會是先append變更信息到Txnlog中(此時會觸發take snap操作),最後在FinalRequestProcessor中更新內存中的DataTree信息。
觸發take snap的條件:
if (logCount > (snapCount / 2 + randRoll)) {
randRoll = r.nextInt(snapCount/2);
snapCount可以通過jvm參數zookeeper.snapCount指定,默認爲100000。 這裏zookeeper很巧妙的加兩個隨機處理,避免zk機器在同一時間點進行take snap處理,影響性能。
session機制
zookeeper會爲每個client分配一個session,類似於web服務器一樣。針對session可以有保存一些關聯數據,zookeeper裏針對session的一些關聯數據主要就是EPHEMERAL節點。
EPHEMERAL的翻譯爲短命的,技術上理解就是session關閉後,其節點即消失,和session保持相同的生命週期。
創建EPHEMERAL節點:
zookeeper.create(path, data, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
我們能用EPHEMERAL節點做啥?
- 分佈式集羣是否存活監控. (每個節點啓動後,註冊一個EPHEMERAL節點到zookeeper中,註冊一個Watcher獲取EPHEMERAL節點的存在情況,消失即可代表集羣節點dead)
- 分佈式lock (每個鎖競爭者,排隊時都在zookeeper中註冊一個EPHEMERAL節點,排隊過程中有節點dead了,zookeeper可以自動將其剔除隊列,避免出現deadlock)
- 替代web服務器的session,實現一個集中式session, session數據中的setAttribute都創建爲EPHEMERAL節點,session關閉後即可自動刪除,不會造成java中的"內存泄漏"
- zookeeper server掛了,對應的session是否會丟失?
- zookeeper client發生了failover後(出現了Connection Loss異常),對應的session是否會丟失?
- 在服務端,zookeeper中session的存儲是有進行持久化的, 具體可見perist機制的描述。 一個新節點啓動後,會從leader中同步對應的session數據
- 在客戶端,zookeeper在每次出現failover後(出現了Connection Loss異常),會重新帶上sessionId,sessionPasswd發起一次鏈接請求。接收到該請求的server,會返回內存中的session信息
session expired機制
zookeeper中session expired機制和node數據一致性的保證原理類似,對應的follower都是受控於leader。
- follower接收到客戶端鏈接請求,就會向leader發送一次createSession的操作請求,leader收到後進行廣播通知給所有的follower/observer節點createSession
- leader會通過內存版的(SessionTrackerImpl),定期掃描過期的session,發送一次closeSession的請求給所有的客戶端
- 在2發送過程中,如果有follower接收到過期session的請求,會提交給leader進行仲裁,leader會直接返回session expired。
session expired幾個參數:
- 服務端: minSessionTimeout (默認值爲:tickTime * 2) , maxSessionTimeout (默認值爲 : tickTime * 20) , ticktime的默認值爲3000ms。所以session範圍爲6s ~ 60s
- 客戶端: sessionTimeout, 無默認值,創建實例時必填。
<span style="FONT-WEIGHT: normal">int minSessionTimeout = zk.getMinSessionTimeout();
if (sessionTimeout < minSessionTimeout) {
sessionTimeout = minSessionTimeout;
}
int maxSessionTimeout = zk.getMaxSessionTimeout();
if (sessionTimeout > maxSessionTimeout) {
sessionTimeout = maxSessionTimeout;
}</span>
Watcher機制
watcher是zookeeper實現分佈式lock一個很重要的feature,在寫分佈式lock時一定要對其有所瞭解。
就會冒出如下問題:
- 什麼情況下,會觸發什麼類型的watcher?
- watcher信息出現failover是否會丟失?
- watcher信息出現session expired是否會丟失?
- exists方法: 設置watcher時,如果對應服務端已經不存在node時,watcher是不會留在服務端,下次不會被觸發。針對這種情況需要判斷返回的stat == null來進行處理
- getChildren方法: 和exist一樣,需要處理節點不存在時watcher不會被記錄。 還有一個點,當前的父node發生delete變化時,也可以得到觸發
- getData方法: 和exist一樣,需要處理節點不存在時watcher不會被記錄
- Watcher是一個本地jvm的callback,在和服務端交互過程中是不會進行傳遞的。只是會將是否有watcher的boolean變量傳遞給server端
- 在服務端,在FinalRequestProcessor處理對應的node操作時,會根據客戶端傳遞的watcher變量,添加到對應的zkDataBase中進行持久化存儲,同時將自己NIOServerCnxn做爲一個Watcher callback,監聽服務端事件變化
- leader通過投票通過了某次node變化請求後,通知給對應的follower,follower根據自己內存中的zkDataBase信息,發送notification信息給zookeeper 客戶端
- zookeeper客戶端接收到notification信息後,找到對應變化path的watcher列表,挨個進行觸發回調。
zookeeper異常處理
官方文檔:http://wiki.apache.%20org/hadoop/ZooKeeper/FAQ
主要處理兩個系統異常:
- KeeperException.ConnectionLossException (client與其中的一臺server socket鏈接出現異常)
- KeeperException.SessionExpiredException (client的session超過sessionTimeout爲進行任何操作)