摘要: 前言 最近懶成一坨屎,學不動系列一波接一波,大多還都是底層原理相關的。上週末抽時間重讀了周志明大溼的 JVM 高效併發部分,每讀一遍都有不一樣的感悟。路漫漫,藉此,把前段時間搞着玩的秒殺案例中的分佈式鎖深刻了解一下。
最近懶成一坨屎,學不動系列一波接一波,大多還都是底層原理相關的。上週末抽時間重讀了周志明大溼的 JVM 高效併發部分,每讀一遍都有不一樣的感悟。路漫漫,藉此,把前段時間搞着玩的秒殺案例中的分佈式鎖深刻了解一下。算法
在嘗試瞭解分佈式鎖以前,你們能夠想象一下,什麼場景下會使用分佈式鎖?數據庫
單機應用架構中,秒殺案例使用ReentrantLcok或者synchronized來達到秒殺商品互斥的目的。然而在分佈式系統中,會存在多臺機器並行去實現同一個功能。也就是說,在多進程中,若是還使用以上JDK提供的進程鎖,來併發訪問數據庫資源就可能會出現商品超賣的狀況。所以,須要咱們來實現本身的分佈式鎖。緩存
實現一個分佈式鎖應該具有的特性:網絡
▪ 高可用、高性能的獲取鎖與釋放鎖session
▪ 在分佈式系統環境下,一個方法或者變量同一時間只能被一個線程操做架構
▪ 具有鎖失效機制,網絡中斷或宕機沒法釋放鎖時,鎖必須被刪除,防止死鎖併發
▪ 具有阻塞鎖特性,即沒有獲取到鎖,則繼續等待獲取鎖分佈式
▪ 具有非阻塞鎖特性,即沒有獲取到鎖,則直接返回獲取鎖失敗高併發
▪ 具有可重入特性,一個線程中能夠屢次獲取同一把鎖,好比一個線程在執行一個帶鎖的方法,該方法中又調用了另外一個須要相同鎖的方法,則該線程能夠直接執行調用的方法,而無需從新得到鎖工具
在以前的秒殺案例中,咱們曾介紹過關於分佈式鎖幾種實現方式:
▪ 基於數據庫實現分佈式鎖
▪ 基於 Redis 實現分佈式鎖
▪ 基於 Zookeeper 實現分佈式鎖
前兩種對於分佈式生產環境來講並非特別推薦,高併發下數據庫鎖性能太差,Redis在鎖時間限制和緩存一致性存在必定問題。這裏咱們重點介紹一下 Zookeeper 如何實現分佈式鎖。
實現原理
ZooKeeper是一個分佈式的,開放源碼的分佈式應用程序協調服務,它內部是一個分層的文件系統目錄樹結構,規定同一個目錄下只能存在惟一文件名。
數據模型
▪ PERSISTENT 持久化節點,節點建立後,不會由於會話失效而消失
▪ EPHEMERAL 臨時節點, 客戶端session超時此類節點就會被自動刪除
▪ EPHEMERAL_SEQUENTIAL 臨時自動編號節點
▪ PERSISTENT_SEQUENTIAL 順序自動編號持久化節點,這種節點會根據當前已存在的節點數自動加 1
監視器(watcher)
當建立一個節點時,能夠註冊一個該節點的監視器,當節點狀態發生改變時,watch被觸發時,ZooKeeper將會向客戶端發送且僅發送一條通知,由於watch只能被觸發一次。
根據zookeeper的這些特性,咱們來看看如何利用這些特性來實現分佈式鎖:
▪ 建立一個鎖目錄lock
▪ 線程A獲取鎖會在lock目錄下,建立臨時順序節點
▪ 獲取鎖目錄下全部的子節點,而後獲取比本身小的兄弟節點,若是不存在,則說明當前線程順序號最小,得到鎖
▪ 線程B建立臨時節點並獲取全部兄弟節點,判斷本身不是最小節點,設置監聽(watcher)比本身次小的節點(只關注比本身次小的節點是爲了防止發生「羊羣效應」)
▪ 線程A處理完,刪除本身的節點,線程B監聽到變動事件,判斷本身是最小的節點,得到鎖
代碼分析
儘管ZooKeeper已經封裝好複雜易出錯的關鍵服務,將簡單易用的接口和性能高效、功能穩定的系統提供給用戶。可是若是讓一個普通開發者去手擼一個分佈式鎖仍是比較困難的,在秒殺案例中咱們直接使用 Apache 開源的curator 開實現 Zookeeper 分佈式鎖。
這裏咱們使用如下版本,截止目前最新版4.0.1:
首先,咱們看下InterProcessLock接口中的幾個方法:
獲取鎖:
Zookeeper獲取鎖實現:
釋放鎖:
爲了更好的理解其原理和代碼分析中獲取鎖的過程,這裏咱們實現一個簡單的Demo:
這裏咱們開啓5個線程,每一個線程獲取鎖的最大等待時間爲5秒,爲了模擬具體業務場景,方法中設置4秒等待時間。開始執行main方法,經過ZooInspector監控/curator/lock下的節點以下圖:
對,沒錯,設置4秒的業務處理時長就是爲了觀察生成了幾個順序節點。果真如案例中所述,每一個線程都會生成一個節點而且仍是有序的。
觀察控制檯,咱們會發現只有兩個線程獲取鎖成功,另外三個線程超時獲取鎖失敗會自動刪除節點。線程執行完畢咱們刷新一下/curator/lock節點,發現剛纔建立的五個子節點已經不存在了。
經過分析第三方開源工具實現的分佈式鎖方式,收穫仍是滿滿的。學習自己就是一個由淺入深的過程,從如何調用API,到理解其代碼邏輯實現,想要更深刻能夠去挖掘Zookeeper的核心算法ZAB協議。
最後爲了方便你們學習,總結了學習過程當中遇到的幾個關鍵詞:重入鎖、自旋鎖、有序節點、阻塞、非阻塞、監聽,但願對你們有所幫助。
本文做者:小柒2012
本文爲雲棲社區原創內容,未經容許不得轉載。