日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
SpringBootRedis實(shí)現(xiàn)分布式鎖,真香!!

Redis 本篇棧長以 Redis 為例(這也是用得最多的方案),教大家如何利用 Spring Boot 集成 Redis 實(shí)現(xiàn)緩存,如何簡單、快速實(shí)現(xiàn) Redis 分布式鎖。

成都創(chuàng)新互聯(lián)公司專注為客戶提供全方位的互聯(lián)網(wǎng)綜合服務(wù),包含不限于成都做網(wǎng)站、成都網(wǎng)站建設(shè)、南崗網(wǎng)絡(luò)推廣、微信小程序開發(fā)、南崗網(wǎng)絡(luò)營銷、南崗企業(yè)策劃、南崗品牌公關(guān)、搜索引擎seo、人物專訪、企業(yè)宣傳片、企業(yè)代運(yùn)營等,從售前售中售后,我們都將竭誠為您服務(wù),您的肯定,是我們最大的嘉獎;成都創(chuàng)新互聯(lián)公司為所有大學(xué)生創(chuàng)業(yè)者提供南崗建站搭建服務(wù),24小時服務(wù)熱線:028-86922220,官方網(wǎng)址:www.cdcxhl.com

之前看很多人手寫分布式鎖,其實(shí) Spring Boot 現(xiàn)在已經(jīng)做的足夠好了,開箱即用,支持主流的 Redis、Zookeeper 中間件,另外還支持 JDBC。

本篇棧長以 Redis 為例(這也是用得最多的方案),教大家如何利用 Spring Boot 集成 Redis 實(shí)現(xiàn)緩存,如何簡單、快速實(shí)現(xiàn) Redis 分布式鎖。

分布式鎖介紹

Spring Boot 實(shí)現(xiàn) Redis 分布式鎖在 spring-integration 這個項目中,參考:

https://docs.spring.io/spring-integration/docs/5.3.1.RELEASE/reference/html/redis.html#redis-lock-registry

首先來看下 LockRegistry 鎖注冊接口的所有實(shí)現(xiàn)類結(jié)構(gòu)圖:

DefaultLockRegistry 就是純單機(jī)的可重入鎖,PassThruLockRegistry 是一個空實(shí)現(xiàn)類,也都沒有什么利用價值。

Spring Integration 4.0 引入了基于 Redis 的分布式鎖:RedisLockRegistry,并且從 5.0 開始實(shí)現(xiàn)了 ExpirableLockRegistry 接口,用來移除超時且沒有用的鎖。

分布式鎖實(shí)戰(zhàn)

添加依賴

上面提到 Spring Boot 實(shí)現(xiàn) Redis 分布式鎖在 spring-integration 這個項目中,所以需要這三個依賴:

  •  spring-boot-starter-data-redis
  •  spring-boot-starter-integration
  •  spring-integration-redis 
  
 
 
 
  1.   
  2.  org.springframework.boot  
  3.  spring-boot-starter-data-redis  
  4.   
  5.   
  6.  org.springframework.boot  
  7.  spring-boot-starter-integration 
  8.   
  9.   
  10.  org.springframework.integration  
  11.  spring-integration-redis  
  12.  

Spring Boot 基礎(chǔ)知識就不介紹了,不熟悉的可以關(guān)注公眾號Java技術(shù)棧,在后臺回復(fù):boot,可以閱讀我寫的歷史實(shí)戰(zhàn)教程。

配置分布式鎖

  
 
 
 
  1. @Bean(destroyMethod = "destroy")  
  2. public RedisLockRegistry redisLockRegistry(RedisConnectionFactory redisConnectionFactory) {  
  3.     return new RedisLockRegistry(redisConnectionFactory, "lock");  

使用示例

  
 
 
 
  1. @GetMapping("/redis/lock")  
  2. public String lock(@RequestParam("key") String key) {  
  3.     for (int i = 0; i < 10; i++) {  
  4.         new Thread(() -> {  
  5.             redisLockService.lock(key);  
  6.             try {  
  7.                 Thread.sleep(3000L);  
  8.             } catch (InterruptedException e) {  
  9.                 e.printStackTrace();  
  10.             }  
  11.             System.out.println(DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss"));  
  12.             redisLockService.unlock(key);  
  13.         }  
  14.         ).start();  
  15.     }  
  16.     return "OK";  

RedisLockService 是我封裝了的一個 Redis 鎖服務(wù),代碼有點(diǎn)多,這里就不貼了,完整的代碼示例在 Github 上,大家可以 Star 一下:

https://github.com/javastacks/spring-boot-best-practice

測試:

http://localhost:8080/redis/lock?key=yeah

輸出:

  
 
 
 
  1. 2020-06-23 11:15:34  
  2. 2020-06-23 11:15:37  
  3. 2020-06-23 11:15:40  
  4. 2020-06-23 11:15:43  
  5. 2020-06-23 11:15:46  
  6. 2020-06-23 11:15:49  
  7. 2020-06-23 11:15:52  
  8. 2020-06-23 11:15:55 
  9. 2020-06-23 11:15:58  
  10. 2020-06-23 11:16:01 

可以看到每個線程需要等上一個線程休眠 3 秒后才能獲取到鎖。

源碼分析

集成完了,會使用了,還得研究下 RedisLockRegistry 的源碼,不然遇到什么坑還得再踩一篇。

RedisLockRegistry 有兩個類構(gòu)造器:

  •  connectionFactory:Redis 連接工廠
  •  registryKey:鎖前綴
  •  expireAfter:失效時間(非必須項,默認(rèn)60秒)

所以,我們要注冊 expireAfter 這個選項,默認(rèn) 60 秒是否滿足業(yè)務(wù)需要,如果超過默認(rèn)的 60 少時間,否則將導(dǎo)致鎖失效。

還有兩個和 RedisLockRegistry  相關(guān)且很重要的成員變量:

  
 
 
 
  1. private final String clientId = UUID.randomUUID().toString();  
  2. private final Map locks = new ConcurrentHashMap<>(); 
  •  clientId:

首先用來標(biāo)識當(dāng)前 RedisLockRegistry 實(shí)例ID,并且在設(shè)置、移除鎖的時候都會要用到,用來判斷是不是當(dāng)前的鎖注冊實(shí)例。

  •  locks:

用來在內(nèi)存中緩存當(dāng)前鎖注冊實(shí)例所有鎖對象。

獲取鎖對象

如下面獲取鎖對象源碼所示:

每個 key 在內(nèi)存中(ConcurrentHashMap)都對應(yīng)一個鎖對象,鎖對象有生成過就直接返回,沒有就生成再返回,有了這個鎖對象才能進(jìn)行上鎖和解鎖操作。

這個鎖對象(RedisLock)其實(shí)也是實(shí)現(xiàn)了 Java 中的 java.util.concurrent.locks.Lock 鎖接口:

鎖對象(RedisLock)也有兩個很重要的成員變量:

  
 
 
 
  1. private final ReentrantLock localLock = new ReentrantLock();  
  2. private volatile long lockedAt; 
  •  localLock:

localLock 是一個本地內(nèi)存可重入鎖,每次去 Redis 上鎖前,都要用本地 localLock 上鎖先,這樣能做到盡可能的少往 Redis 上鎖,也能從一方面提升鎖的性能。

  •  lockedAt:

lockedAt 上鎖時間,移除過時鎖會用到。

阻塞上鎖

RedisLock#lock():

每隔 100 毫秒嘗試獲取一次鎖,直到獲取鎖成功為止,不接受打斷異常,遇到其他異常會釋放本地鎖返回異常并結(jié)束。

主要看下設(shè)置 Redis 鎖的 Lua 腳本:

根據(jù) key 查詢其對應(yīng)的值:clientId,如果和當(dāng)前 clientId 一致則延長失效時間,如果 clientId 不存在就直接上鎖,以上都不成立返回 false。

這樣做的好處是,可以將整個 Redis Lua 腳本作為一個原子執(zhí)行,而不用考慮并發(fā)及事務(wù)影響。

好了,核心的源碼分析完了,其實(shí)也很簡單,大家還不懂的或者有興趣的可以再研究下。

本文完整示例源代碼和上篇 Spring Boot 快速集成 Redis 的示例代碼一起上傳到了 Github,歡迎大家 Star 關(guān)注學(xué)習(xí)。

https://github.com/javastacks/spring-boot-best-practice

所以,你還在手寫分布式鎖嗎?趕快 Get 起來吧!


網(wǎng)頁名稱:SpringBootRedis實(shí)現(xiàn)分布式鎖,真香!!
路徑分享:http://www.dlmjj.cn/article/djijcci.html