Zookeeper分布式鎖安全嗎
Martin和Antirez爭論點
在之前的文章討論基于Redis的RedLock分布式鎖中,有提到劍橋分布式專家Martin指出,RedLock安全性并不高,并且其中有一個假設(shè)場景如下
假設(shè)存在多個實例A、B、C、D、E,同時存在客戶端1和2有如下場景
- 客戶端1請求實例A、B、C、D、E獲取鎖成功。
- 客戶端1開始操作共享資源,這時發(fā)生GC網(wǎng)絡(luò)暫停,stop-the-world。
- 在GC期間客戶端1持有的所有實例上的鎖過期。
- 客戶端2向?qū)嵗鼳、B、C、D、E請求獲取鎖,成功。
- 客戶端2操作共享資源,這時客戶端1從GC中恢復(fù),客戶端1無法感知鎖已經(jīng)過期,也操作共享資源導(dǎo)致沖突。
這個假設(shè)Redis之父Antirez指出在獲取鎖后發(fā)生的NPC(N:網(wǎng)絡(luò)延遲、P:進程暫停、C:時鐘跳躍)問題RedLock無法處理,但這不僅僅是RedLock的問題,其余分布式鎖也有這個問題,如Zookeeper。
Zookeeper是否安全呢
上面的場景與我們理解的如果需要構(gòu)建更加安全的分布式鎖,首要參考的就是Zookeeper思想有沖突,那么我們應(yīng)該如何抉擇呢,下面參考Zookeeper的作者之一Flavio Junqueira寫的一篇博客Note on fencing and distributed locks
原文鏈接:https://fpj.systems/2016/02/10/note-on-fencing-and-distributed-locks/
Zookeeper構(gòu)建分布式鎖的步驟
Flavio Junqueira指出構(gòu)建的一種方式,步驟如下
- 客戶端嘗試創(chuàng)建一個znode節(jié)點如/lock,第一個客戶端創(chuàng)建成功后相當(dāng)于拿到了鎖,后續(xù)的客戶端再去創(chuàng)建/lock因為已經(jīng)存在那么創(chuàng)建會失敗。
- 持有鎖的客戶端在訪問共享資源后,將znode節(jié)點刪除,那么其它客戶端可以繼續(xù)創(chuàng)建znode節(jié)點。
- znode的節(jié)點應(yīng)該是臨時的,這樣才能保證如果客戶端突然崩潰,這個客戶端持有的znode節(jié)點才會被刪除,保證鎖的釋放。
這樣看起來非常完美,因為沒有Redis那種設(shè)置自動過期時間,可能因為時鐘跳躍導(dǎo)致鎖提前過期的情況,但真是這樣嗎,我們先來思考一個問題,客戶端突然崩潰zookeeper如何能快速感知到呢?
Zookeeper心跳檢測
每個客戶端都會和Zookeeper之間維護一個Session,這個Session依賴心跳維護,如果Zookeeper在規(guī)定時間內(nèi)未收到客戶端心跳回復(fù),那么將認(rèn)為客戶端失去鏈接,這時就會將這個Session創(chuàng)建的所有臨時節(jié)點刪除。
Zookeeper的安全問題
正是因為Zookeeper存在心跳檢測這個問題,那么可能出現(xiàn)以下場景。
- 客戶端1連接Zookeeper后創(chuàng)建一個znode臨時節(jié)點/lock成功。
- 客戶端1進入長時間的GC,進程暫停。
- 客戶端1連接到Zookeeper的session長時間未收到心跳回復(fù),并且超過過期時間,自動刪除創(chuàng)建的臨時節(jié)點/lock。
- 客戶端2連接Zookeeper后同樣創(chuàng)建znode臨時節(jié)點/lock成功。
- 客戶端2開始操作共享資源,這時客戶端1恢復(fù)同樣操作共享資源,沖突產(chǎn)生。
這個場景就和上面分布式專家Martin提出的場景類似,在獲取鎖后發(fā)生NPC問題,這是單純依靠分布式鎖無法處理。
ZooKeeper的watch機制
ZooKeeper雖然單純依賴自己無法解決獲取鎖后的NPC安全性問題,但是其watch特性,將分布式鎖變地像一個單機鎖實現(xiàn)。
當(dāng)客戶端試圖創(chuàng)建一個臨時節(jié)點/lock時,如果發(fā)現(xiàn)節(jié)點已經(jīng)創(chuàng)建,這時客戶端可以不立即失敗,客戶端可以進入一個阻塞等待狀態(tài),等待當(dāng)/lock節(jié)點被刪除后,Zookeeper通過watch機制通知給客戶端,這樣的方式就好像JAVA中獲取單機鎖一樣方便。
Zookeeper和Redis對比
Zookeeper和Redis都能實現(xiàn)分布式鎖,優(yōu)勢如下
- 在客戶端和Zookeeper連接正常的情況下,客戶端可以持有鎖任意時長,這可以確??蛻舳嗽诔钟墟i后操作共享資源并不會因為業(yè)務(wù)操作過長而導(dǎo)致鎖過期,可以解決Redis過期時間到底設(shè)置多久的難題。
- Zookeeper支持的Watch機制,可以讓Zookeeper實現(xiàn)的分布式鎖,使用起來更加靈活,像使用本地鎖一樣。
劣勢
- Zookeeper與客戶端如果長期沒有心跳那么鎖將自動釋放。
- 性能不如Redis。
總結(jié)
分布式鎖可以由多種方式實現(xiàn),但是看完分布式專家Martin和Redis作者的討論后,在極端情況分布式鎖并不是完全安全的,Zookeeper也不能例外,所以這也就是分布式鎖都面臨著NPC三座大山的考驗,如果我們放在關(guān)鍵業(yè)務(wù)處理時并不能完全依靠分布式鎖,還需要有類似Martin提出的fecing token防護令牌兜底。