? ? ? ?
目錄
本地鎖的局限性
Redisson解決分布式鎖問題
?????????
????????在分布式環境下,分布式鎖可以保證在多個節點上的并發操作時數據的一致性和互斥性。分布式鎖有多種實現方案,最常用的兩種方案是:zookeeper和redis,本文介紹redis實現分布式鎖方案。
本地鎖的局限性
? ? ? ? JDK中自帶的synchronized及lock鎖,這些鎖都是本地鎖,在一個節點服務(一個JVM)下可以實現“鎖”的效果,一旦服務多實例,本地鎖就會存在問題。下方示例演示并發對redis的值做自增操作,觀察效果,步驟如下:
? ? ? ? 1、編寫請求和服務接口
? ? ? ? 此代碼的主要邏輯是redis中key=num的值每次+1(初始值是0),其中redisTest方法上加上本地鎖(synchronized)
@GetMapping("/redisTest")
public GenericWebResult redisTest() {Long result = this.blogContentService.redisTest();log.info("設置redis中num值:{}",result);return GenericWebResult.ok((Object) "測試成功",result);
}
@Override
public synchronized Long redisTest() throws BusinessException {Long result = 0l;String key = "num";RBucket<Object> bucket = this.redissonClient.getBucket(key);if(bucket != null) {Long ori = (Long)bucket.get();result = ori.longValue() + 1;this.redissonClient.getBucket(key).set(result);}return result;
}
? ? ? ? 2、單個實例啟動后,用jemter并發請求測試?
? ? ? ? 單實例啟動后,服務端口:8888,jemter通過100個線程并發請求訪問后,redis中key=num的值是100,符合預期。
?
? ? ? ? 3、后臺服務啟動多個實例, 再次用jemter并發請求測試?
? ? ? ? 后臺服務啟動了2個實例,端口分別是8888和9999,并通過網關gateway對外公布統一的請求地址,網關會自動均衡到端口是8888和9999的2個服務中(這部分的配置不在此文贅述,詳情見網關Gateway-CSDN博客),網關服務的端口是7777,請求的統一地址是:http://localhost:7777/blog-content/blogContent/redisTest?,通過jmeter測試如下(num初始恢復到0):
? ? ? ? 執行后,發現redis中key=num的值變成了65,不是100,不符合預期,證明本地鎖在分布式環境下是有局限性的,它只能保證在一個JVM下鎖的有效性。
Redisson解決分布式鎖問題
????????Redisson是一個在Redis的基礎上實現的Java駐內存數據網格(In-Memory Data Grid)。它不僅提供了一系列的分布式的Java常用對象,還提供了許多分布式服務,其中就包含了各種分布式鎖的實現。使用Redisson實現分布式鎖,需要如下步驟:
? ? ? ? 1、項目添加依賴
<!-- redisson -->
<dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId><exclusions><exclusion><groupId>org.redisson</groupId><artifactId>redisson-spring-data-23</artifactId></exclusion></exclusions>
</dependency>
<dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-data-26</artifactId><version>3.47.0</version>
</dependency>
? ? ? ? 2、改造redisTest方法,引入鎖
? ? ? ? 通過redissonClient.getLock獲取鎖,在執行業務邏輯前鎖住lock.lock(),執行完業務邏輯后釋放鎖lock.unlock()
@Override
public Long redisTest() throws BusinessException {Long result = 0l;RLock lock = redissonClient.getLock("lock");lock.lock();//業務邏輯try {log.info("業務邏輯開始...");Thread.sleep(1000);//模擬業務邏輯時長String key = "num";RBucket<Object> bucket = this.redissonClient.getBucket(key);if(bucket != null) {Long ori = (Long)bucket.get();result = ori.longValue() + 1;this.redissonClient.getBucket(key).set(result);}log.info("業務邏輯結束...");} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();log.info("redis 解鎖...");}return result;
}
? ? ? ? 3、和第一章節一樣,后臺服務同樣啟動2個實例,端口分別是8888和9999,用jmeter模擬并發請求網關端口。
?
? ? ? ? 從后臺請求日志可以看出,2個服務每隔1秒獲取到鎖,然后執行業務邏輯(num++),最終num的值是100,符合預期,實現了分布式環境下鎖的效果。
?
? ? ? ??
????????
????????
????????
?????????
????????