Spring Boot 整合 redisson 實現分布式鎖

2021-01-15 黑馬程式設計師

面試總是會被問到有沒有用過分布式鎖、redis 鎖,大部分讀者平時很少接觸到,所以只能很無奈的回答 「沒有」。本文通過 Spring Boot 整合 redisson 來實現分布式鎖,並結合 demo 測試結果。

首先看下大佬總結的圖:

圖片來源於網絡,侵刪

正文

增加依賴

<!--redis--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!--redisson--><dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId><version>3.10.6</version></dependency>

配置 信息

spring:# redisredis: host: 47.103.5.190 port: 6379 jedis: pool:# 連接池最大連接數(使用負值表示沒有限制) max-active: 100# 連接池中的最小空閒連接 max-idle: 10# 連接池最大阻塞等待時間(使用負值表示沒有限制) max-wait: -1# 連接超時時間(毫秒) timeout: 5000#默認是索引為0的資料庫 database: 0

配置類

/*** redisson 配置,下面是單節點配置: * * @author gourd */@ConfigurationpublicclassRedissonConfig{@Value("${spring.redis.host}")privateString host;@Value("${spring.redis.port}")privateString port;@Value("${spring.redis.password:}")privateString password;@BeanpublicRedissonClient redissonClient() {Config config = newConfig();//單節點 config.useSingleServer().setAddress("redis://"+ host + ":"+ port);if(StringUtils.isEmpty(password)) { config.useSingleServer().setPassword(null);} else{ config.useSingleServer().setPassword(password);}//添加主從配置// config.useMasterSlaveServers().setMasterAddress("").setPassword("").addSlaveAddress(new String[]{"",""});// 集群模式配置 setScanInterval()掃描間隔時間,單位是毫秒, //可以用"rediss://"來啟用SSL連接// config.useClusterServers().setScanInterval(2000).addNodeAddress("redis://127.0.0.1:7000", "redis://127.0.0.1:7001").addNodeAddress("redis://127.0.0.1:7002");returnRedisson.create(config);}}Redisson 工具類/** * redis分布式鎖幫助類 * * @author gourd * */publicclassRedisLockUtil{privatestaticDistributedLocker distributedLocker = SpringContextHolder.getBean("distributedLocker",DistributedLocker.class);/** * 加鎖 * @param lockKey * @return */publicstaticRLocklock(String lockKey) {return distributedLocker.lock(lockKey);}/** * 釋放鎖 * @param lockKey */publicstaticvoid unlock(String lockKey) { distributedLocker.unlock(lockKey);}/** * 釋放鎖 * @param lock */publicstaticvoid unlock(RLocklock) { distributedLocker.unlock(lock);}/** * 帶超時的鎖 * @param lockKey * @param timeout 超時時間 單位:秒 */publicstaticRLocklock(String lockKey, int timeout) {return distributedLocker.lock(lockKey, timeout);}/** * 帶超時的鎖 * @param lockKey * @param unit 時間單位 * @param timeout 超時時間 */publicstaticRLocklock(String lockKey, int timeout,TimeUnit unit ) {return distributedLocker.lock(lockKey, unit, timeout);}/** * 嘗試獲取鎖 * @param lockKey * @param waitTime 最多等待時間 * @param leaseTime 上鎖後自動釋放鎖時間 * @return */publicstaticboolean tryLock(String lockKey, int waitTime, int leaseTime) {return distributedLocker.tryLock(lockKey, TimeUnit.SECONDS, waitTime, leaseTime);}/** * 嘗試獲取鎖 * @param lockKey * @param unit 時間單位 * @param waitTime 最多等待時間 * @param leaseTime 上鎖後自動釋放鎖時間 * @return */publicstaticboolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime) {return distributedLocker.tryLock(lockKey, unit, waitTime, leaseTime);}/** * 獲取計數器 * * @param name * @return */publicstaticRCountDownLatch getCountDownLatch(String name){return distributedLocker.getCountDownLatch(name);}/** * 獲取信號量 * * @param name * @return */publicstaticRSemaphore getSemaphore(String name){return distributedLocker.getSemaphore(name);}}

底層封裝

/*** @author gourd */publicinterfaceDistributedLocker{RLocklock(String lockKey);RLockock(String lockKey, int timeout);RLocklock(String lockKey, TimeUnit unit, int timeout);boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime);void unlock(String lockKey);void unlock(RLocklock);}/** * @author gourd */@ComponentpublicclassRedisDistributedLockerimplementsDistributedLocker{@AutowiredprivateRedissonClient redissonClient;@OverridepublicRLocklock(String lockKey) {RLocklock= redissonClient.getLock(lockKey);lock.lock();returnlock;}@OverridepublicRLocklock(String lockKey, int leaseTime) {RLocklock= redissonClient.getLock(lockKey);lock.lock(leaseTime, TimeUnit.SECONDS);returnlock;}@OverridepublicRLocklock(String lockKey, TimeUnit unit ,int timeout) {RLocklock= redissonClient.getLock(lockKey);lock.lock(timeout, unit);returnlock;}@Overridepublicboolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime) {RLocklock= redissonClient.getLock(lockKey);try{returnlock.tryLock(waitTime, leaseTime, unit);} catch(InterruptedException e) {returnfalse;}}@Overridepublicvoid unlock(String lockKey) {RLocklock= redissonClient.getLock(lockKey);lock.unlock();}@Overridepublicvoid unlock(RLocklock) {lock.unlock();}}

測試

模擬並發測試

/*** redis分布式鎖控制器 * @author gourd * @since 2019-07-30 */@RestController@Api(tags = "redisson", description = "redis分布式鎖控制器")@RequestMapping("/redisson")@Slf4jpublicclassRedissonLockController{/** * 鎖測試共享變量 */privateInteger lockCount = 10;/** * 無鎖測試共享變量 */privateInteger count = 10;/** * 模擬線程數 */privatestaticint threadNum = 10;/** * 模擬並發測試加鎖和不加鎖 * @return */@GetMapping("/test")@ApiOperation(value = "模擬並發測試加鎖和不加鎖")publicvoidlock(){// 計數器finalCountDownLatch countDownLatch = newCountDownLatch(1);for(int i = 0; i < threadNum; i ++) {MyRunnable myRunnable = newMyRunnable(countDownLatch);Thread myThread = newThread(myRunnable); myThread.start();}// 釋放所有線程 countDownLatch.countDown();}/** * 加鎖測試 */privatevoid testLockCount() {String lockKey = "lock-test";try{// 加鎖,設置超時時間2sRedisLockUtil.lock(lockKey,2, TimeUnit.SECONDS); lockCount--; log.info("lockCount值:"+lockCount);}catch(Exception e){ log.error(e.getMessage(),e);}finally{// 釋放鎖RedisLockUtil.unlock(lockKey);}}/** * 無鎖測試 */privatevoid testCount() { count--; log.info("count值:"+count);}publicclassMyRunnableimplementsRunnable{/** * 計數器 */finalCountDownLatch countDownLatch;publicMyRunnable(CountDownLatch countDownLatch) {this.countDownLatch = countDownLatch;}@Overridepublicvoid run() {try{// 阻塞當前線程,直到計時器的值為0 countDownLatch.await();} catch(InterruptedException e) { log.error(e.getMessage(),e);}// 無鎖操作 testCount();// 加鎖操作 testLockCount();}}}

調用接口後列印值:

測試結果根據列印

結果可以明顯看到,未加鎖的 count-- 後值是亂序的,而加鎖後的結果和我們預期的一樣。

由於條件問題沒辦法測試分布式的並發。只能模擬單服務的這種並發,但是原理是一樣,希望對大家有幫助。如有錯誤之處,歡迎指正

相關焦點

  • SpringBoot整合redisson實現分布式鎖
    回來之後就惡補了一下,本文主要做下記錄,通過SpringBoot整合redisson來實現分布式鎖,並結合demo測試結果。--redis--><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId></dependency><!
  • 新課上線-spring boot2.0實戰中間件redisson與典型應用場景
    它是架設在redis基礎之上,但擁有的功能卻遠遠多於原生Redis 所提供的,比如分布式對象、分布式集合體系、分布式鎖以及分布式服務調度等一系列具有分布式特性的對象實例…而這些東西debug將在本門課程進行淋漓盡致的介紹並實戰,除此之外,我們將基於spring boot2.0搭建的多模塊項目實戰典型的應用場景:對象存儲、數據字典、簡訊發送、實時/定時郵件發送、布隆過濾器、
  • SpringBoot2.0實戰(22)整合Redis之實現分布式鎖
    >分布式鎖是控制分布式系統之間同步訪問共享資源的一種方式,在分布式系統中,如果不同的應用之間共享一個或一組資源,那麼訪問這些資源的時候,往往需要互斥來防止彼此幹擾來保證一致性,在這種情況下,便需要使用到分布式鎖。
  • Spring Cloud基於Redis實現的分布式鎖
    基於Redis實現的分布式鎖Spring Cloud 分布式環境下,同一個服務都是部署在不同的機器上,這種情況無法像單體架構下數據一致性問題採用加鎖就實現數據一致性問題,在高並發情況下,對於分布式架構顯然是不合適的,針對這種情況我們就需要用到分布式鎖了。
  • 分布式鎖解決方案-Redis
    因此synchronized無法保證數據的一致性,這也是為什麼要學習分布式解決方案的原因## 分布式鎖需要滿足的幾點1.互斥;任何時刻只能有一個client獲取鎖2.釋放死鎖;即使鎖定資源的服務崩潰或者分區,仍然能釋放鎖3.容錯性;只要多數節點
  • Spring Integration實現分布式鎖
    作者 | 僅此而已-遠方來源 | urlify.cn/b2umMv學習本篇之前,可以先看下文章 什麼是分布式鎖,了解下基本概念。Spring Integration提供的全局鎖,目前為這幾種存儲提供了實現:Gemfire、JDBC、Redis、Zookeeper它們使用相同的API抽象--這正是Spring最擅長的。
  • 分布式鎖沒那麼難,手把手教你實現 Redis 分布鎖!|保姆級教程
    那就先寫下最近在鼓搗一個東西,使用 Redis 實現可重入分布鎖。看到這裡,有的朋友可能會提出來使用 redisson 不香嗎,為什麼還要自己實現?哎,redisson 真的很香,但是現有項目中沒辦法使用,只好自己手擼一個可重入的分布式鎖了。
  • 我猜你還沒明白如何利用好Redis使用實現分布式鎖?
    3.基於Redis實現分布式鎖3.1 使用Redis命令實現分布式鎖3.1.1加鎖加鎖實際上就是在redis中,給Key鍵設置一個值3.2使用Redisson實現分布式鎖3.2.1Redisson介紹Redisson是架設在Redis基礎上的一個Java駐內存數據網格(In-Memory Data Grid)。
  • 用Redisson實現分布式鎖,so easy
    針對項目中使用的分布式鎖進行簡單的示例配置以及源碼解析,並列舉源碼中使用到的一些基礎知識點,但是沒有對redisson中使用到的netty知識進行解析。Maven配置<dependency>    <groupId>org.redisson</groupId>    <artifactId>redisson</artifactId
  • SpringBoot2 整合JTA組件,多數據源事務管理
    XA協議是資料庫層面的一套分布式事務管理的規範,JTA是XA協議在Java中的實現,多個資料庫或是消息廠商實現JTA接口,開發人員只需要調用SpringJTA接口即可實現JTA事務管理功能。JTA事務比JDBC事務更強大。一個JTA事務可以有多個參與者,而一個JDBC事務則被限定在一個單一的資料庫連接。
  • 基於 Redis 實現的分布式鎖
    ;import org.springframework.util.ReflectionUtils;import org.springframework.util.StringUtils;import com.mfz.lock.annotation.KeyParam;import lombok.extern.slf4j.Slf4j;/** * 分布式鎖Key的默認生成器<br/> * *
  • Java都為我們提供了各種鎖,為什麼還需要分布式鎖?
    這篇文章主要是針對為什麼需要使用分布式鎖這個話題來展開討論的。前一段時間在群裡有個兄弟問,既然分布式鎖能解決大部分生產問題,那麼java為我們提供的那些鎖有什麼用呢?直接使用分布式鎖不就結了嘛。針對這個問題我想了很多,一開始是在網上找找看看有沒有類似的回答。後來想了想。想要解決這個問題,還需要從本質上來分析。OK,開始上車出發。
  • 深入Spring Boot (十):整合Mybatis框架詳解
    Spring Boot整合Mybatis支持XML配置和全註解兩種方式,本篇將詳細解說這兩種方式的Mybatis整合,主要包含以下5部分內容:數據源配置;資料庫連接池配置;XML配置方式整合;註解方式整合;
  • 深入Spring Boot (十一):整合Redis詳解
    本篇將介紹如何整合Redis及使用Redis實現簡單的查詢緩存,主要包括以下7部分內容:緩存RedisLettuceSpring Data RedisSpring Cache整合Redis小結緩存個人理解的緩存是指用於存儲頻繁使用的數據的空間
  • Spring Boot Redis 實現分布式鎖,真香
    本篇棧長以 Redis 為例(這也是用得最多的方案),教大家如何利用 Spring Boot 集成 Redis 實現緩存,如何簡單、快速實現 Redis 分布式鎖。分布式鎖介紹Spring Boot 實現 Redis 分布式鎖在 spring-integration 這個項目中,參考:https://docs.spring.io/spring-integration/docs/5.3.1.RELEASE/reference/html/redis.html34
  • Spring Boot與Shiro整合實現用戶認證
    Spring Boot整合Shiro1.2.1. 導入shiro與spring整合依賴修改pom.xml<!-- shiro與spring整合依賴 --><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>1.4.0</version></dependency>
  • 整合JPA、Redis實現分布式的Session共享
    一.SpringBoot整合JPA、Redis實現分布式的Session共享1. 創建web項目我們按照之前的經驗,創建一個web程序,並將之改造成Spring Boot項目,具體過程略。2.添加依賴包<dependency> <groupId>org.springframework.boot</groupId>
  • SpringBoot2 整合 Swagger2
    SpringBoot2 整合 Swagger2SpringBoot整合三板斧第一步、引入pom<dependency> <groupId>com.spring4all</groupId> <artifactId>swagger-spring-boot-starter</artifactId> <version>1.9.0.RELEASE</version></dependency><dependency> <groupId>com.
  • Spring boot整合持久層框架Mybatis
    前言本文主要分析Spring boot 整合持久層框架第一步:添加spring boot和mybatis的依賴,可以根據實際情況進行修改版本spring boot相關依賴></parent><dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </
  • Spring Boot+Redis輕鬆實現接口冪等性
    三、我們已經了解了什麼樣的接口需要保證冪等性,那應該怎麼實現接口的冪等性呢?一般有如下幾種解決方案:狀態機 -- 修改狀態,更新時判斷狀態。(有局限性)使用緩存,基於請求參數、session或者token(用戶信息)防止重複提交(使用redis用作緩存時和分布式鎖原理相同)。