我們前面文章《分布式鎖服務的思考》中已經提出來為什么要加鎖,以及對什么資源加鎖,大家最好先過去簡單看一下。今天我們繼續學習一下如何結合Redis實現一個分布式鎖服務。我們先實現一個簡單的分布式鎖服務,然后分析單節點鎖存在的問題,再由問題引入Redis的分布式鎖服務紅鎖的設計。
目錄:
redis簡單介紹
簡單的分布式鎖服務
可靠的分布式鎖服務
代碼示例
1.Redis簡單介紹Redis本身是內存數據庫,適合用來對熱點數據、瞬時數據(expire)、近似統計數據做存儲,不小心丟失了部分數據也不要緊。例如我們用來統計每個IP的訪問頻率,用來做限流或者防止爬蟲,同時也記錄IP和對應的登錄用戶ID,防止惡意接口檢測。同時Redis具有一個特征:結合Lua腳本可以實現原子性操作,我們可以利用Redis的這個特點,來實現一個鎖服務。
2.使用Redis實現一個不可靠的分布式鎖服務主要的指令:
該指令對key=resource的資源加過期時間為3000ms的鎖,如果該資源已經被加鎖了,說明key存在,本次加鎖失敗。
釋放鎖的Lua腳本如下:
我們使用Java代碼結合上面的Lua腳本實現一個簡單的鎖,如下:
思考:
既然單節點存在單點故障,我們添加一個slave可以嗎?不可以,因為redis的主備是異步復制。
客戶端A在master上獲得鎖,之后master在沒有同步到slave就崩潰了。
slave提升為master,客戶端B在新的master上獲得了A已經獲得的資源鎖。
非分布式或集群的系統本身就沒有可靠性可言,所以上面我們討論的鎖在非分布式系統中可以應用良好。下面帶大家認識下Redis的分布式鎖服務——紅鎖。
3.利用Redis實現可靠的分布式鎖服務——紅鎖Redis把分布式鎖的算法稱之為紅鎖,Redis+Lock=RedLock。紅鎖需要N個(=3)Redis獨立節點,這些節點相互之間不需要有信息交流,保持獨立即可。紅鎖基本的思路是:客戶端在每個Redis實例上獲得鎖(就是上面講過的指令),只要大多數實例上成功獲得鎖就算加鎖成功。紅鎖的算法已經有很多實現版本,JAVA對應的實現版本是Redisson,我們可以在業務中直接引入并使用。雖然不需要我們自己去實現算法,但是有必要了解一下紅鎖的算法,下面我們描述一下客戶端獲得鎖的流程:
客戶端首先記錄當前時間,用于后面計算總耗時。
客戶端使用相同的key和隨機value,從所有的Redis節點上獲得鎖。客戶端在每個實例上設置鎖的過程中,需要設超時時間(5-50ms),不成功就換下一個實例繼續設置鎖,用來防止客戶端阻塞在一個down掉都實例上。
客戶端需要計算獲得鎖的總耗時。客戶端從至少N/2+1個節點上成功獲得鎖,且總耗時小于鎖過期時間才能成功獲得鎖。
客戶端獲得鎖之后,該鎖的有效期不再是最初的過期時間,因為客戶端要從多個節點上獲得鎖,需要去掉這些過程耗時。
如果客戶端最終獲得鎖失敗,必須在所有節點上執行鎖釋放。
以上算法保證了:
鎖互斥性,同一時間只能有一個鎖
不會死鎖,使用鎖過期時間。
多節點容錯,只要大多數節點獲取了鎖就可以認為成功獲取鎖。
4.Redisson實現的分布式鎖Redisson是基于Netty、Redis、JDK實現的更上層的應用工具,它提供了分布式對象和服務。Redisson命令的發送可以支持同步方式,異步方式,Redisson可以支持多種Redis的實際部署方式,例如:單節點、集群、主備、熱備、哨兵、云服務。可參考下面官方的架構圖:
我們使用Redisson的分布式鎖服務,因為Redisson支持Redis的集群部署、熱備部署、哨兵部署、主備部署多種方式,而今天我們重點不在Redis的部署,所以我們就簡單部署3個獨立的Redis節點就可以實驗了。
使用編碼的方式獲得Redisson實例
通過3個實例,獲得紅鎖
加鎖和釋放鎖
我們通過上面的代碼片段,簡單寫一個工具類:
貼圖保格式
總結Redis的應用場景簡介。這里大家先思考redis開發模式和規范,強調下,僅僅使用key-value是很恐怖的事情。
我們分析了單節點Redis的鎖不可靠,引入了紅鎖算法。
帶領大家認識了功能豐富的Redisson,我們僅介紹了紅鎖功能。