基于注解實現(xiàn)的Redis分布式鎖

redislock-spring-boot-starter

Github地址:https://github.com/peikaiqiang/redislock-spring-boot-starter

簡介

redislock-spring-boot-starter 是基于redis實現(xiàn)的分布式注解鎖驱闷,原理很簡單者娱,就是在redis里面setnx一個key,如果這個key不存在,則加鎖成功役衡,多線程環(huán)境下再次進入這個方法就會加鎖失敗咙轩。項目以spring-boot-starter的形式發(fā)布眶明,引入方便惩坑。既可以以加注解的方式實現(xiàn)方法如入口層面的加鎖,也可以注入相應(yīng)的加鎖類朝扼,以嵌入代碼的形式加鎖赃阀。作者更推薦使用注解的形式,注解切面的order是-1擎颖,也就是在spring事務(wù)切面的外層榛斯。開箱即用,只需要配置spring redis即可搂捧,該項目已經(jīng)在作者的兩家公司應(yīng)用了驮俗,覺得不錯記得點贊!

關(guān)于timeout的設(shè)置

小于50毫秒則會直接休眠timeout的時間允跑。大于50時王凑,則第一次休眠50毫秒,此后休眠時間逐次翻倍吮蛹,最后一次休眠的時間為剩余待休眠時間荤崇,即保證總休眠時間等于timeout的值。

關(guān)于key到期后的安全問題

假如第一個線程執(zhí)行方法超時潮针,redis key到期后,第二個線程可以進入重新加鎖倚喂。第一個超時方法執(zhí)行完后是不會把第二個線程加的鎖釋放掉的每篷,程序已經(jīng)做了處理。怎么處理第二個線程進入的問題端圈,框架沒有處理焦读,依賴使用者做好業(yè)務(wù)冪等處理。全局超時時間是90秒舱权,可以根據(jù)業(yè)務(wù)自行調(diào)整矗晃,也可以在單個注解上設(shè)置單次的超時時間。另外相比手動加鎖宴倍,不會有遺漏釋放鎖的風(fēng)險张症。

命名更新

項目名稱之前是RedisLock仓技,考慮到用spring-boot-starter比較方便,所以使用官方推薦的命名方法俗他。原項目1.0.0版本的jar包maven中央庫脖捻,強烈建議更換新項目的maven dependency。

配置

  • SpringBootApplication 引入注解 @EnableRedisLock
@EnableRedisLock
@SpringBootApplication
public class Application() {
}

  • pom 引入redis
<dependency>
    <groupId>com.warmmen</groupId>
    <artifactId>redislock-spring-boot-starter</artifactId>
</dependency>

  • redis 配置兆衅,具體參考springboot官方文檔
spring:
  redis:
      host: localhost
      port: 6379
      password:
      pool:
        max-active: 8
        max-idle: 8
        min-idle: 0
        max-wait: 10000

配置文件屬性

redis-lock.timeout 全局默認(rèn)阻塞獲取鎖的超時時間地沮,單位毫秒,默認(rèn)500羡亩。
redis-lock.prefix 所有加鎖的key的前綴摩疑,默認(rèn)為 "REDIS_LOCK_" 。
redis-lock.defaultError 加鎖失敗拋出的異常信息畏铆,默認(rèn) "The lock has been occupied.re"未荒。
redis-lock.liveTime 鎖持續(xù)的時間,單位毫秒及志,默認(rèn)90000片排。鎖失效后,其他線程可以進入該鎖速侈,設(shè)置需謹(jǐn)慎率寡。

注解方式

  • @RedisLock 方法注解
方法加鎖的標(biāo)志,方法必須實現(xiàn)jdk接口倚搬,原理同事務(wù)冶共。
value,redis中存的key值每界。
suffix 后綴捅僵,redis中存的key值附加的后綴。
blocked 是否阻塞獲取鎖眨层,默認(rèn)否庙楚,非阻塞獲取會立馬返回結(jié)果。如果為true趴樱,則在第一次獲取鎖失敗后馒闷,會多次休眠嘗試重新獲取鎖。
error 獲取鎖失敗時拋出的異常信息叁征,默認(rèn)拋出的信息: "The lock has been occupied."
timeout 阻塞獲取鎖的超時時間纳账,單位毫秒,如不設(shè)值捺疼,則取全局的默認(rèn)超時時間疏虫,默認(rèn)500毫秒。也可以在 application.yml配置文件中修改redis-lock.timeout的值。如有設(shè)值卧秘,則該注解修飾的方法以timeout值作為超時時間呢袱。

  • @RedisLockRequest 方法注解
獲取Http請求參數(shù)的值
value 參數(shù)的名稱

  • @RedisLockParam 參數(shù)注解
獲取參數(shù)的值
value
當(dāng)value為空時:如果參數(shù)實現(xiàn)了RedisLockable接口,則獲取RedisLockable接口的key()斯议,否則返回參數(shù)的toString()作為redis鎖的key产捞。
當(dāng)value非空時:以SpEL表達(dá)式獲取參數(shù)中的字段值,假如參數(shù)是Map類型哼御,value=“name”, 則返回Map中key為name的值坯临;如果參數(shù)是object,則返回name字段的toString(), 假如name字段還是個object恋昼,也可以用 “name.filed” 取出name中field字段的值看靠,以此類推(Map 也是相同原則)。

  • RedisLockable 接口
自定義獲取參數(shù)的鎖Key

java 代碼方式

@Autowired
DefaultRedisLock defaultRedisLock;

void doSomething() {
    try {
        boolean success = defaultRedisLock.tryLock("DO_SOMETHING", 100);

        if (success) {
            // do something
        }
    } finally {
        // unlock in finally
        defaultRedisLock.unlock("DO_SOMETHING");
    }
}

tryLock 和 lock 區(qū)別液肌?
lock加鎖失敗會拋出RedisLockException異常挟炬,tryLock不會。
tryLock 可以加阻塞時間嗦哆。

鎖key的拼接順序

  1. redis-lock.prefix 的值谤祖。
  2. @RedisLockRequest 配置的請求參數(shù)中獲取,配置多個時老速,按配置的順序獲取粥喜。
  3. @RedisLockParam中獲取,多個時以下劃線(_)分隔橘券。
  4. suffix 的值额湘。

如果第2,3步取值都為空旁舰,則會拋出異常锋华。

例子

@RedisLock(vakue = "Test_Method", suffix = "Suf", blocked = true, timeout = 1000, error = "加鎖失敗,請重試箭窜!")
@RedisLockRequest({"arg1", "arg2"})
public void test(@RedisLockParam param1,
                 @RedisLockParam("name") Map<String, Object> param2)

如果獲取鎖成功毯焕,則key的值為:REDIS_LOCK_Test_Method_arg1_arg2_param1_name_Suf,鎖的持續(xù)時間時90秒
如果獲取鎖失敗绽快,拋出異常:"加鎖失敗芥丧,請重試!"
最后附上Github地址:https://github.com/peikaiqiang/redislock-spring-boot-starter
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末坊罢,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子擅耽,更是在濱河造成了極大的恐慌活孩,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件乖仇,死亡現(xiàn)場離奇詭異憾儒,居然都是意外死亡询兴,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進店門起趾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來诗舰,“玉大人,你說我怎么就攤上這事训裆】舾” “怎么了?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵边琉,是天一觀的道長属百。 經(jīng)常有香客問我,道長变姨,這世上最難降的妖魔是什么族扰? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮定欧,結(jié)果婚禮上渔呵,老公的妹妹穿的比我還像新娘。我一直安慰自己砍鸠,他們只是感情好扩氢,可當(dāng)我...
    茶點故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著睦番,像睡著了一般类茂。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上托嚣,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天巩检,我揣著相機與錄音,去河邊找鬼示启。 笑死兢哭,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的夫嗓。 我是一名探鬼主播迟螺,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼舍咖!你這毒婦竟也來了矩父?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤排霉,失蹤者是張志新(化名)和其女友劉穎窍株,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡球订,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年后裸,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片冒滩。...
    茶點故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡微驶,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出开睡,到底是詐尸還是另有隱情因苹,我是刑警寧澤,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布士八,位于F島的核電站容燕,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏婚度。R本人自食惡果不足惜蘸秘,卻給世界環(huán)境...
    茶點故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蝗茁。 院中可真熱鬧醋虏,春花似錦、人聲如沸哮翘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽饭寺。三九已至阻课,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間艰匙,已是汗流浹背限煞。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留员凝,地道東北人署驻。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像健霹,于是被迫代替她去往敵國和親旺上。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,941評論 2 355