天天看點

redis cluster 分布式鎖_Redis實作的分布式鎖和分布式限流

随着現在分布式越來越普遍,分布式鎖也十分常用,這篇文章解釋了 使用zookeeper實作分布式鎖,本次咱們說一下如何用Redis實作分布式鎖和分布限流。

Redis有個事務鎖,就是如下的指令,這個指令的含義是将一個value設定到一個key中,如果不存在将會指派并且設定逾時時間為30秒,如何這個key已經存在了,則不進行設定。

SET key value NX PX 30000           

這個事務鎖很好的解決了兩個單獨的指令,一個設定set key value nx,即該key不存在的話将對其進行設定,另一個是expire key seconds,設定該key的逾時時間。我們可以想一下,如果這兩個指令用程式單獨使用會存在什麼問題:

  • 如果一個set key的指令設定了key,然後程式異常了,expire時間沒有設定,那麼這個key會一直鎖住。
  • 如果一個set key時出現了異常,但是直接執行了expire,過了一會兒之後另一個進行set key,還沒怎麼執行代碼,結果key過期了,别的線程也進入了鎖。

還有很多出問題的可能點,這裡我們就不讨論了,下面咱們來看看如何實作吧。

本文使用的Spring Boot 2.x + Spring data redis + Swagger +lombok + AOP + lua腳本。在實作的過程中遇到了很多問題,都一一解決實作了。

依賴的POM檔案如下:

redis cluster 分布式鎖_Redis實作的分布式鎖和分布式限流
redis cluster 分布式鎖_Redis實作的分布式鎖和分布式限流
redis cluster 分布式鎖_Redis實作的分布式鎖和分布式限流

使用了兩個lua腳本,一個用于執行lock,另一個執行unlock。

咱們簡單看一下,lock腳本就是采用Redis事務執行的set nx px指令,其實還有set nx ex指令,這個ex指令是采用秒的方式進行設定過期時間,這個px是采用毫秒的方式設定過期時間。

value需要使用一個唯一的值,這個值在解鎖的時候需要判斷是否一緻,如果一緻的話就進行解鎖。這個也是官方推薦的方法。另外在lock的地方我設定了一個result,用于輸出測試時的結果,這樣就可以結合程式去進行debug了。

redis cluster 分布式鎖_Redis實作的分布式鎖和分布式限流
redis cluster 分布式鎖_Redis實作的分布式鎖和分布式限流

來看下代碼,主要寫了兩個方法,一個是用與鎖另外一個是用于結解鎖。這塊需要注意的是使用RedisTemplate,這塊意味着key和value一定都是String的,我在使用的過程中就出現了一些錯誤。首先初始化兩個腳本到程式中,然後調用執行腳本。

redis cluster 分布式鎖_Redis實作的分布式鎖和分布式限流

還有一個就是腳本定義的地方需要注意,傳回的結果集一定是Long, Boolean,List, 一個反序列化的值。這塊要注意。

redis cluster 分布式鎖_Redis實作的分布式鎖和分布式限流
redis cluster 分布式鎖_Redis實作的分布式鎖和分布式限流

好了,這塊就寫好了,然後寫好controller類準備測試。

redis cluster 分布式鎖_Redis實作的分布式鎖和分布式限流

我也寫了一個測試類,用于測試和輸出結果, 使用100個線程,然後鎖的時間設定10秒,controller裡邊需要休眠3秒模拟業務執行。

redis cluster 分布式鎖_Redis實作的分布式鎖和分布式限流

擷取鎖的地方就會執行do business logic, 然後會有部分線程擷取到鎖并執行業務,執行完業務的就會釋放鎖。

redis cluster 分布式鎖_Redis實作的分布式鎖和分布式限流

分布式鎖就實作好了,接下來實作分布式限流。先看一下limit的lua腳本,需要給腳本傳兩個值,一個值是限流的key,一個值是限流的數量。

擷取目前key,然後判斷其值是否為nil,如果為nil的話需要指派為0,然後進行加1并且和limit進行比對,如果大于limt即傳回0,說明限流了,如果小于limit則需要使用Redis的INCRBY key 1,就是将key進行加1指令。并且設定逾時時間,逾時時間是秒,并且如果有需要的話這個秒也是可以用參數進行設定。

redis cluster 分布式鎖_Redis實作的分布式鎖和分布式限流

執行limit的腳本和執行lock的腳本類似。

redis cluster 分布式鎖_Redis實作的分布式鎖和分布式限流

接下來咱們寫一個限流注解,并且設定注解的key和限流的大小:

redis cluster 分布式鎖_Redis實作的分布式鎖和分布式限流

然後對注解進行切面,在切面中判斷是否超過limit,如果超過limit的時候就需要抛出異常exceeded limit,否則正常執行。

redis cluster 分布式鎖_Redis實作的分布式鎖和分布式限流

因為有抛出異常,這裡我弄了一個統一的controller錯誤處理,如果controller出現Exception的時候都需要走這塊異常。如果是正常的RunTimeException的時候擷取一下,否則将異常擷取一下并且輸出。

redis cluster 分布式鎖_Redis實作的分布式鎖和分布式限流

好了,接下來将注解寫到自定義的controller上,limit的大小為10,也就是10秒鐘内限制10次通路。

redis cluster 分布式鎖_Redis實作的分布式鎖和分布式限流

也是來一段Test方法來跑,老方式100個線程開始跑,隻有10次,其他的都是limit。沒有問題。

redis cluster 分布式鎖_Redis實作的分布式鎖和分布式限流

總結一下,這次實作采用了使用lua腳本和Redis實作了鎖和限流,但是真實使用的時候還需要多測試,另外如果此次Redis也是采用的單機實作方法,使用叢集的時候可能需要改造一下。

關于鎖這塊其實Reids自己也實作了RedLock, java實作的版本Redission。也有很多公司使用了,功能非常強大。各種場景下都用到了。

繼續閱讀