Redisson分布式
概念:是一個在Redis的基礎上實現的Java駐內存數據網格(In-Memory Data Grid)。它不僅提供了一系列的分布式的Java常用對象,還提供了許多分布式服務
引入依賴
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.13.1</version>
</dependency>
配置
@Configuration
public class MyRedissonConfig {
@Bean(destroyMethod = "shutdown")
public RedissonClient redissonClient() throws IOException{
//1.配置連接
Config config = new Config();
config.useSingleServer()
.setPassword("123456")
//可以用"rediss://"來啟用SSL連接
.setAddress("redis://123.56.16.54:6379");
//2.創建客戶端
RedissonClient redissonClient= Redisson.create(config);
return redissonClient;
}
}
分布式鎖
1、可重入鎖
@ResponseBody
@GetMapping("/hello")
public String hello() {
//1、獲取一把鎖,只要鎖的名字一樣,就是同一把鎖
RLock lock = redisson.getLock(" my-lock ");
//2、加鎖
lock.lock();//阻塞式等待。默認加的鎖都是30s時間。
//1)、鎖的自動續期,如果業務超長,運行期間自動給鎖續上新的30s。不用擔心業務時間長,鎖自動過期被用特
//2)、加鎖的業務只要運行完成,就不會給當前鎖續期,即使不手動解鎖,鎖默認在30s以后自動刪除。
try{
System.out.println("加鎖成功,執行業務..." + Thread.currentThread().getId());
Thread.sleep(30000);
} catch (Exception e) {
} finally {
//3、解鎖將設解鎖代碼沒有運行,redisson會不會出現死鎖
System.out.println("釋放鎖..." + Thread.currentThread().getId());
lock.unlock();
}
return "hello";
}
基於Redis的Redisson分布式可重入鎖RLock
Java對象實現了java.util.concurrent.locks.Lock
接口
大家都知道,如果負責儲存這個分布式鎖的Redisson節點宕機以后,而且這個鎖正好處於鎖住的狀態時,這個鎖會出現鎖死的狀態。為了避免這種情況的發生,Redisson內部提供了一個監控鎖的看門狗,它的作用是在Redisson實例被關閉前,不斷的延長鎖的有效期。默認情況下,看門狗的檢查鎖的超時時間是30秒鍾,也可以通過修改Config.lockWatchdogTimeout來另行指定。
try {
while (true) {
ttl = tryAcquire(leaseTime, unit, threadId);
// lock acquired
if (ttl == null) {
break;
}
// waiting for message
if (ttl >= 0) {
try {
future.getNow().getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
if (interruptibly) {
throw e;
}
future.getNow().getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
}
} else {
if (interruptibly) {
future.getNow().getLatch().acquire();
} else {
future.getNow().getLatch().acquireUninterruptibly();
}
}
}
} finally {
unsubscribe(future, threadId);
}
另外Redisson還通過加鎖的方法提供了leaseTime
的參數來指定加鎖的時間。超過這個時間后鎖便自動解開了。
// 加鎖以后10秒鍾自動解鎖,不會續期
// 無需調用unlock方法手動解鎖
lock.lock(10, TimeUnit.SECONDS);//10秒自動解鎖,自動解鎖時間一定要大於業務的執行時間。
//問題:Lock.Lock(10,TimeUnit.SECONDS);在鎖時間到了以后,不會自動續期。
//1、如果我們傳遞了鎖的超時時間,就發送給redis執行腳本,進行占鎖,默認超時就是我們指定的時間
//2、如果我們未指定鎖的超時時間,就使用30*1000(LockivatchdogTimeout看門狗的默認時間)
//只要占鎖成功,就會啟動一個定時任務(重新給看門狗定義過期時間,新的過期時間是默認的30s),每個10s自動續期到30s
// internalLockLeaseTime【看門狗時間】/3,10s后
// 嘗試加鎖,最多等待100秒,超出不等,上鎖以后10秒自動解鎖
boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
if (res) {
try {
...
} finally {
lock.unlock();
}
}
2、公平鎖
保證了當多個Redisson客戶端線程同時請求加鎖時,優先分配給先發出請求的線程。所有請求線程會在一個隊列中排隊,當某個線程出現宕機時,Redisson會等待5秒后繼續下一個線程,也就是說如果前面有5個線程都處於等待狀態,那么后面的線程會等待至少25秒。
RLock fairLock = redisson.getFairLock("anyLock");
// 最常見的使用方法
fairLock.lock();
// 10秒鍾以后自動解鎖
// 無需調用unlock方法手動解鎖
fairLock.lock(10, TimeUnit.SECONDS);
// 嘗試加鎖,最多等待100秒,上鎖以后10秒自動解鎖
boolean res = fairLock.tryLock(100, 10, TimeUnit.SECONDS);
...
fairLock.unlock();
3、讀寫鎖
分布式可重入讀寫鎖允許同時有多個讀鎖和一個寫鎖處於加鎖狀態。
RReadWriteLock rwlock = redisson.getReadWriteLock("anyRWLock");
// 最常見的加鎖使用方法
rwlock.readLock().lock();
// 或
rwlock.writeLock().lock();
// 10秒鍾以后自動解鎖
// 無需調用unlock方法手動解鎖
rwlock.readLock().lock(10, TimeUnit.SECONDS);
// 或
rwlock.writeLock().lock(10, TimeUnit.SECONDS);
// 嘗試加鎖,最多等待100秒,上鎖以后10秒自動解鎖
boolean res = rwlock.readLock().tryLock(100, 10, TimeUnit.SECONDS);
// 或
boolean res = rwlock.writeLock().tryLock(100, 10, TimeUnit.SECONDS);
...
lock.unlock();
進行寫操作之前不能有鎖,進行讀操作之前可以有讀鎖,不能有寫鎖
4、信號量
@ResponseBody
@GetMapping("/acquire")
public String AcquireSemaphore(){
RSemaphore semaphore = redisson.getSemaphore("semaphore");
//需要獲取幾個信號量參數就寫幾,默認為1
boolean b = semaphore.tryAcquire();
if(b){
//獲取成功
return "獲取成功:信號量-1..."+"當前信號量"+semaphore.availablePermits();
}else{
return "獲取失敗:等待...."+"當前信號量"+semaphore.availablePermits();
}
}
@ResponseBody
@GetMapping("/release")
public String releaseSemaphore(){
RSemaphore semaphore = redisson.getSemaphore("semaphore");
semaphore.release();//semaphore.release(4);表示一次性釋放4個信號量
return "釋放成功:信號量+1......"+"當前信號量"+semaphore.availablePermits();
}
5、閉鎖
(CountDownLatch)
RCountDownLatch latch = redisson.getCountDownLatch("anyCountDownLatch");
latch.trySetCount(3);//redis中存在一個anyCountDownLatch=3,當其等於0時就閉鎖
latch.await();
// 在其他線程或其他JVM里
RCountDownLatch latch = redisson.getCountDownLatch("anyCountDownLatch");
latch.countDown();//redis中的anyCountDownLatch減一