redis如何實現分佈式重入鎖?
在上一節課中,我們已經知道SETNX是不支持重入鎖的,但我們需要重入鎖,怎麼辦呢?
目前對於redis的重入鎖業界還是有很多解決方案的,最流行的就是採用Redisson,關於什麼是Redisson?下面詳細介紹
什麼是 Redisson?
Redisson是Redis官方推薦的Java版的Redis客戶端。
它基於Java實用工具包中常用接口,爲使用者提供了一系列具有分佈式特性的常用工具類。
它在網絡通信上是基於NIO的Netty框架,保證網絡通信的高性能。
在分佈式鎖的功能上,它提供了一系列的分佈式鎖;如:可重入鎖(Reentrant Lock)、公平鎖(Fair Lock、聯鎖(MultiLock)、
紅鎖(RedLock)、 讀寫鎖(ReadWriteLock)等等。
案例實戰:體驗redis分佈式重入鎖
步驟1:加入pom依賴包
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.9.0</version>
</dependency>
步驟2:配置文件
#redi開關配置 見:封裝類 CacheConfiguration
#設置模式 sentinel/cluster/single
spring.redis.mode=single
#redis基礎配置 見:封裝類 RedisProperties
spring.redis.database=0
spring.redis.password=
spring.redis.timeout=3000
#redis線程池配置 見:封裝類 RedisPoolProperties
spring.redis.pool.conn-timeout=3000
spring.redis.pool.so-timeout=3000
spring.redis.pool.size=10
#單機模式 見:封裝類 RedisSingleProperties
spring.redis.single.address=192.168.1.138:6379
#集羣模式 見:封裝類 RedisClusterProperties
spring.redis.cluster.scan-interval=1000
spring.redis.cluster.nodes=192.168.2.58:7000,192.168.2.58:7001,192.168.2.58:7002,192.168.2.58:7003,192.168.2.58:7004,192.168.2.58:7005,192.168.2.58:7006
spring.redis.cluster.read-mode=SLAVE
spring.redis.cluster.retry-attempts=3
spring.redis.cluster.failed-attempts=3
spring.redis.cluster.slave-connection-pool-size=64
spring.redis.cluster.master-connection-pool-size=64
spring.redis.cluster.retry-interval=1500
#哨兵模式 見:RedisSentinelProperties
spring.redis.sentinel.master=business-master
spring.redis.sentinel.nodes=
spring.redis.sentinel.master-onlyWrite=true
spring.redis.sentinel.fail-max=3
步驟3:Redisson重入鎖代碼
public class RedisController {
@Autowired
RedissonClient redissonClient;
@GetMapping(value = "/lock")
public void get(String key) throws InterruptedException {
this.getLock(key, 1);
}
private void getLock(String key, int n) throws InterruptedException {
//模擬遞歸,3次遞歸後退出
if (n > 3) {
return;
}
//步驟1:獲取一個分佈式可重入鎖RLock
//分佈式可重入鎖RLock :實現了java.util.concurrent.locks.Lock接口,同時還支持自動過期解鎖。
RLock lock = redissonClient.getLock(key);
//步驟2:嘗試拿鎖
// 1. 默認的拿鎖
//lock.tryLock();
// 2. 支持過期解鎖功能,10秒鐘以後過期自動解鎖, 無需調用unlock方法手動解鎖
//lock.tryLock(10, TimeUnit.SECONDS);
// 3. 嘗試加鎖,最多等待3秒,上鎖以後10秒後過期自動解鎖
// lock.tryLock(3, 10, TimeUnit.SECONDS);
boolean bs = lock.tryLock(3, 10, TimeUnit.SECONDS);
if (bs) {
try {
// 業務代碼
log.info("線程{}業務邏輯處理: {},遞歸{}" ,Thread.currentThread().getName(), key,n);
//模擬處理業務
Thread.sleep(1000 * 5);
//模擬進入遞歸
this.getLock(key, ++n);
} catch (Exception e) {
log.error(e.getLocalizedMessage());
} finally {
//步驟3:解鎖
lock.unlock();
log.info("線程{}解鎖退出",Thread.currentThread().getName());
}
} else {
log.info("線程{}未取得鎖",Thread.currentThread().getName());
}
}
}
步驟4:體驗測試
ie流浪器輸入:http://192.168.1.4:9090/lock?key=aganlock
刷新3次 看效果
2020-03-08 12:19:24.237 INFO 76507 --- [nio-9090-exec-1] c.agan.redis.controller.RedisController : 線程http-nio-9090-exec-1業務邏輯處理: aganlock,遞歸1
2020-03-08 12:19:29.069 INFO 76507 --- [nio-9090-exec-2] c.agan.redis.controller.RedisController : 線程http-nio-9090-exec-2未取得鎖
2020-03-08 12:19:29.377 INFO 76507 --- [nio-9090-exec-1] c.agan.redis.controller.RedisController : 線程http-nio-9090-exec-1業務邏輯處理: aganlock,遞歸2
2020-03-08 12:19:31.896 INFO 76507 --- [nio-9090-exec-3] c.agan.redis.controller.RedisController : 線程http-nio-9090-exec-3未取得鎖
2020-03-08 12:19:34.579 INFO 76507 --- [nio-9090-exec-1] c.agan.redis.controller.RedisController : 線程http-nio-9090-exec-1業務邏輯處理: aganlock,遞歸3
2020-03-08 12:19:39.820 INFO 76507 --- [nio-9090-exec-1] c.agan.redis.controller.RedisController : 線程http-nio-9090-exec-1解鎖退出
2020-03-08 12:19:39.823 INFO 76507 --- [nio-9090-exec-1] c.agan.redis.controller.RedisController : 線程http-nio-9090-exec-1解鎖退出
2020-03-08 12:19:39.827 INFO 76507 --- [nio-9090-exec-1] c.agan.redis.controller.RedisController : 線程http-nio-9090-exec-1解鎖退出
通過測試結果:
1.發送了3次請求,springboot啓用了3條線程來處理,分別是nio-9090-exec-1 io-9090-exec-2 nio-9090-exec-3
2.nio-9090-exec-1線程,在getLock方法遞歸了3次,即證明了lock.tryLock是可重入鎖
3.只有當nio-9090-exec-1線程執行完後,io-9090-exec-2 nio-9090-exec-3 未取得鎖
因爲lock.tryLock(3, 10, TimeUnit.SECONDS),嘗試加鎖,最多等待3秒,上鎖以後10秒後過期自動解鎖
所以等了3秒都等不到,就放棄了