2013-01-11 55 views
17

在閱讀ZooKeeper的recipe for lock時,我感到困惑。看起來,這個分佈式鎖的配方不能保證「任何時間的快照,沒有兩個客戶端認爲他們擁有相同的鎖」。但是由於ZooKeeper得到了廣泛的採用,如果參考文檔中有這樣的錯誤,應該早有人指出它,那麼我誤解了什麼?關注動物園管理員的鎖定配方

報價the recipe for distributed locks

完全分佈式的鎖是全球同步的,在任何時間快照含義沒有兩個客戶認爲他們持有相同的鎖。這些可以使用ZooKeeeper來實現。與優先級隊列一樣,首先定義一個鎖定節點。

  1. 調用Create()與 「locknode/GUID的鎖相」 的路徑和順序和短暫的標誌設置。
  2. 在鎖定節點上調用getChildren()而不設置監視標誌(這對於避免羣效應很重要)。
  3. 如果在步驟1中創建的路徑名具有最低的序列號後綴,則客戶端具有該鎖並且客戶端退出該協議。
  4. 客戶端調用exists(),並在鎖目錄的路徑中設置具有次低序號的watch標誌。
  5. 如果存在()返回false,則轉到步驟2。否則,等待來自前面步驟的路徑通知之前執行步驟2

考慮以下情況:

  • Client1通過ZooKeeper節點「locknode/guid-lock-0」成功獲取鎖定(步驟3);
  • Client2創建節點「locknode/guid-lock-1」,未能獲取該鎖,現在正在觀看「locknode/guid-lock-0」;
  • 之後,出於某種原因(比如網絡擁塞),Client1未能及時向ZooKeeper集羣發送心跳消息,但Client1仍在工作,錯誤地認爲它仍然保持鎖定狀態。
  • 但是,動物園管理員可能會認爲客戶端1的會話超時,然後

    1. 刪除「locknode/GUID-鎖0」,
    2. 發送通知到客戶機2(或者先發的通知? ),
    3. 但不能及時向客戶端1發送「會話超時」通知(例如,由於網絡擁塞)。
  • 客戶機2得到通知,進到步驟2,得到的唯一節點「」 locknode/GUID-鎖定-1" ,它創建本身;因此,客戶機2將假定它持有鎖
  • 但在同時,Client1認爲它擁有該鎖。

這是一個有效的方案嗎?

+1

關於_zookeeper-users_郵件列表的平行討論:http://thread.gmane.org/gmane.comp.java.hadoop.zookeeper.user/5065 – seh

回答

15

您描述的情況可能會出現。客戶端1認爲它有鎖,但實際上它的會話已超時,並且客戶端2獲得該鎖。

ZooKeeper客戶端庫將通知客戶端1其連接已斷開(但客戶端不知道會話已到期,直到客戶端連接到服務器),因此客戶端可以編寫一些代碼並假定他如果他已經斷開太久,鎖已經丟失。但是使用鎖的線程需要定期檢查鎖是否仍然有效,這本質上是活潑的。

+0

謝謝;這是否意味着,「不變的」 - **在任何快照中沒有兩個客戶認爲他們擁有相同的鎖**不成立? – hulunbier

+0

這是正確的 – sbridges

+3

另一個微不足道的方式來違反「在任何快照時間」不變(因此證明語句爲false)是持有該鎖的客戶端上的GC長暫停。例如:客戶端C獲取鎖定,同時拿着它,java GC啓動並凍結該進程60秒。 10秒鐘後,C會話過期,另一個進程獲得鎖定。時間11是「2個客戶認爲他們持有相同鎖的時間快照」。 – Marco

0

...但是,動物園管理員可能會認爲客戶端1的會話timeouted,然後...

從動物園管理員文檔:

  • 去除一個節點只會導致一個客戶端被喚醒,因爲每個節點只有一個客戶端正在監視 。這樣,你就避免了牛羣效應。
  • 沒有投票或超時。

所以我不認爲你描述的問題出現了。在我看來,如果創建它們的客戶發生了什麼事情,可能會掛上鎖的風險,但是您描述的情況不應該出現。

+0

謝謝;但我不明白你的想法;對我來說,「避免羣效應」&&「無輪詢或超時」不能確保客戶端2在鎖定客戶端1時無法獲取鎖定。此外,會話超時後,ZK會自動刪除臨時節點,所以我看不到「懸掛鎖的風險」... – hulunbier

+0

如果Client1的會話超時,那麼在此情況下鎖不再存在,但Client1將當時從Zookeeper獲得SESSION_EXPIRED或CONNECTION_LOSS,所以他們會知道他們已經失去了連接。 – glenatron

+0

但是,如果SESSION_EXPIRED消息在時間**中未被髮送到Client1 **,該怎麼辦?例如,由於暫時的高數據包丟失率(TCP連接仍處於建立狀態) – hulunbier