偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Spring Boot Redis實(shí)現(xiàn)分布式鎖,真香?。?/h1>

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

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

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

分布式鎖介紹

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

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

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

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

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

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

添加依賴

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

  •  spring-boot-starter-data-redis
  •  spring-boot-starter-integration
  •  spring-integration-redis 
  1. <dependency>  
  2.  <groupId>org.springframework.boot</groupId>  
  3.  <artifactId>spring-boot-starter-data-redis</artifactId>  
  4. </dependency>  
  5. <dependency>  
  6.  <groupId>org.springframework.boot</groupId>  
  7.  <artifactId>spring-boot-starter-integration</artifactId> 
  8. </dependency>  
  9. <dependency>  
  10.  <groupId>org.springframework.integration</groupId>  
  11.  <artifactId>spring-integration-redis</artifactId>  
  12. </dependency> 

Spring Boot 基礎(chǔ)知識(shí)就不介紹了,不熟悉的可以關(guān)注公眾號(hào)Java技術(shù)棧,在后臺(tái)回復(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 是我封裝了的一個(gè) Redis 鎖服務(wù),代碼有點(diǎn)多,這里就不貼了,完整的代碼示例在 Github 上,大家可以 Star 一下:

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

測(cè)試:

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 

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

源碼分析

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

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

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

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

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

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

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

  •  locks:

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

獲取鎖對(duì)象

如下面獲取鎖對(duì)象源碼所示:

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

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

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

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

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

  •  lockedAt:

lockedAt 上鎖時(shí)間,移除過(guò)時(shí)鎖會(huì)用到。

阻塞上鎖

RedisLock#lock():

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

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

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

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

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

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

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

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

 

責(zé)任編輯:龐桂玉 來(lái)源: Java技術(shù)棧
相關(guān)推薦

2022-10-10 14:41:44

RedisJVM數(shù)據(jù)

2023-10-12 10:32:51

2022-01-06 10:58:07

Redis數(shù)據(jù)分布式鎖

2023-08-21 19:10:34

Redis分布式

2022-09-22 13:28:34

Redis分布式鎖

2024-01-30 08:41:33

線程執(zhí)行Redis分布式鎖

2022-09-29 08:28:57

SpringRedis分布式

2019-02-26 09:51:52

分布式鎖RedisZookeeper

2019-06-19 15:40:06

分布式鎖RedisJava

2024-10-07 10:07:31

2024-04-01 05:10:00

Redis數(shù)據(jù)庫(kù)分布式鎖

2020-07-30 09:35:09

Redis分布式鎖數(shù)據(jù)庫(kù)

2023-03-01 08:07:51

2023-01-13 07:39:07

2023-10-11 09:37:54

Redis分布式系統(tǒng)

2019-12-25 14:35:33

分布式架構(gòu)系統(tǒng)

2021-10-25 10:21:59

ZK分布式鎖ZooKeeper

2024-11-28 15:11:28

2022-03-24 07:51:27

seata分布式事務(wù)Java

2020-11-16 12:55:41

Redis分布式鎖Zookeeper
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)