SpringBoot整合Redisson實踐
簡介:
Redisson 是架設在 Redis 基礎上的一個 Java 駐內(nèi)存數(shù)據(jù)網(wǎng)格(In-Memory Data Grid)。充分 的利用了 Redis 鍵值數(shù)據(jù)庫提供的一系列優(yōu)勢,基于 Java 實用工具包中常用接口,為使用者 提供了一系列具有分布式特性的常用工具類。
使得原本作為協(xié)調(diào)單機多線程并發(fā)程序的工 具包獲得了協(xié)調(diào)分布式多機多線程并發(fā)系統(tǒng)的能力,大大降低了設計和研發(fā)大規(guī)模分布式 系統(tǒng)的難度。同時結(jié)合各富特色的分布式服務,更進一步簡化了分布式環(huán)境中程序相互之間的協(xié)作。
一.簡單使用
1、導入依賴
<!--使用redisson作為分布式鎖-->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.16.8</version>
</dependency>2、新建Redisson配置
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyRedissonConfig {
/**
* 所有對Redisson的使用都是通過RedissonClient對象
* @return
*/
@Bean(destroyMethod = "shutdown")
public RedissonClient redissonClient(){
// 創(chuàng)建配置 指定redis地址及節(jié)點信息
Config config = new Config();
config.useSingleServer().setAddress("XXX.XX.XX.X(redis地址):端口").setPassword("xxxxxxxxx");
// 根據(jù)config創(chuàng)建出RedissonClient實例
RedissonClient redissonClient = Redisson.create(config);
return redissonClient;
}
}3、測試
@Autowired
RedissonClient redissonClient;
@Test
void redisson(){
System.out.println(redissonClient);
}
運行測試代碼發(fā)現(xiàn)控制臺有異常,報錯信息為 :
java.lang.IllegalArgumentException: Redis url should start with redis:// or rediss:// (for SSL connection)
提示我們要在地址前加上redis:// ,SSL連接則需要加上rediss://
所以需要改為setAddress("redis://XXX.XX.XX.X:6379")即可
打印結(jié)果:
org.redisson.Redisson@6159fb3c
二.分布式鎖
1、可重入鎖
基于Redis的Redisson分布式可重入鎖RLock Java對象實現(xiàn)了java.util.concurrent.locks.Lock接口。同時還提供了異步(Async)、反射式(Reactive)和RxJava2標準的接口。
常用代碼:
RLock lock = redisson.getLock("anyLock");
// 最常見的使用方法
lock.lock();測試代碼:
@ResponseBody
@GetMapping("/hello")
public String hello() {
// 1.獲取一把鎖,只要鎖的名字一樣,就是同一把鎖
RLock lock = redisson.getLock("my-lock");
lock.lock(); // 阻塞式等待。默認加的鎖是30s時間
try {
// 1、鎖的自動續(xù)期,運行期間自動給鎖續(xù)上新的30s,無需擔心業(yè)務時間長,鎖過期會自動被釋放
// 2、加鎖的業(yè)務只要運行完成,就不會給當前鎖續(xù)期,即使不手動釋放鎖,鎖默認在30s后自動釋放,避免死鎖
System.out.println("加鎖成功,執(zhí)行業(yè)務代碼..."+Thread.currentThread().getId());
Thread.sleep(30000);
} catch (Exception e) {
e.printStackTrace();
}finally {
System.out.println("釋放鎖..."+Thread.currentThread().getId());
lock.unlock();
}
return "Hello!";
}
結(jié)果:
加鎖成功,執(zhí)行業(yè)務代碼...99
釋放鎖...99
可以發(fā)現(xiàn),當我們的業(yè)務超長時,運行期間,redisson會為我們自動續(xù)期鎖,業(yè)務執(zhí)行完將不會續(xù)期,即使不手動釋放鎖,鎖也會默認在30s后釋放。
2、看門狗機制
redisson 中提供的續(xù)期機制
開一個監(jiān)聽線程,如果方法還沒執(zhí)行完,就幫你重置 redis 鎖的過期時間。
原理:
- 啟動定時任務重新給鎖設置過期時間,默認過期時間是 30 秒,每 10 秒(看門狗默認事件的1/3)續(xù)期一次(補到 30 秒)
- 如果線程掛掉(注意 debug 模式也會被它當成服務器宕機),則不會續(xù)期
- 只有l(wèi)ock.lock(); 會有看門狗機制;
- lock.lock(10,,TimeUnit.SECONDS);手動設置過期時間的話,則不會有看門狗機制(推薦)
3、讀寫鎖
一次只有一個線程可以占有寫模式的讀寫鎖, 但是可以有多個線程同時占有讀模式的讀寫鎖.(該數(shù)據(jù)加寫鎖、讀數(shù)據(jù)加讀鎖)
- 當讀寫鎖是寫加鎖狀態(tài)時, 在這個鎖被解鎖之前, 所有試圖對這個鎖加鎖的線程都會被阻塞.
- 當讀寫鎖在讀加鎖狀態(tài)時, 所有試圖以讀模式對它進行加鎖的線程都可以得到訪問權, 但是如果線程希望以寫模式對此鎖進行加鎖, 它必須直到所有的線程釋放鎖.
測試讀寫鎖代碼:
@Autowired
RedissonClient redisson;
@Autowired
RedisTemplate redisTemplate;
@ResponseBody
@GetMapping("/write")
public String writeValue(){
RReadWriteLock lock = redisson.getReadWriteLock("rw-lock");
RLock rLock = lock.writeLock();
String s = "";
try {
s = UUID.randomUUID().toString();
// 模擬業(yè)務時間
Thread.sleep(30000);
} catch (Exception e){
}finally {
rLock.unlock();
}
redisTemplate.opsForValue().set("writeValue",s);
return s;
}
@GetMapping(value = "/read")
@ResponseBody
public String readValue() {
String s = "";
RReadWriteLock readWriteLock = redisson.getReadWriteLock("rw-lock");
//加讀鎖
RLock rLock = readWriteLock.readLock();
try {
rLock.lock();
s = (String) redisTemplate.opsForValue().get("writeValue");
TimeUnit.SECONDS.sleep(10);
} catch (Exception e) {
e.printStackTrace();
} finally {
rLock.unlock();
}
return s;
}
當我們訪問 localhost:10000/write寫入數(shù)據(jù)時,因為線程睡眠了30s(模擬業(yè)務),此時我們訪問 localhost:10000/read 將會一直阻塞,等待寫鎖釋放,讀鎖才能占鎖從而獲取執(zhí)行業(yè)務。
4、信號量
信號量為存儲在redis中的一個數(shù)字,當這個數(shù)字大于0時,即可以調(diào)用acquire()方法增加數(shù)量,也可以調(diào)用release()方法減少數(shù)量,但是當調(diào)用release()之后小于0的話方法就會阻塞,直到數(shù)字大于0。
可以應用于秒殺、限流等操作。
簡單應用:
@GetMapping(value = "/park")
@ResponseBody
public String park() {
RSemaphore park = redisson.getSemaphore("park");
try {
park.acquire();// 獲取一個信號量(redis中信號量值-1),如果redis中信號量為0了,則在這里阻塞住,直到信號量大于0,可以拿到信號量,才會繼續(xù)執(zhí)行。
} catch (InterruptedException e) {
e.printStackTrace();
}
return "ok";
}
@GetMapping(value = "/go")
@ResponseBody
public String go() {
RSemaphore park = redisson.getSemaphore("park");
park.release(); //釋放一個信號量(redis中信號量值+1)
return "ok";
}其中:redisson.getSemaphore("park").acquire() 當信號量為0時將會一直阻塞,直到信號量大于0,才會繼續(xù)執(zhí)行。但redisson.getSemaphore("park").tryAcquire() 將不會阻塞,能拿到信號量就返回true,否則返回false,lock.tryLock() 同理。
5、閉鎖
在要完成某些運算時,只有其它線程的運算全部運行完畢,當前運算才繼續(xù)下去。
模擬場景:
學校放假,學校門衛(wèi)鎖門必須等待所有班級全部離開,才將學校大門鎖住。
@GetMapping(value = "/lockDoor")
@ResponseBody
public String lockDoor() throws InterruptedException {
RCountDownLatch lockDoor = redisson.getCountDownLatch("lockDoor");
lockDoor.trySetCount(5); // 設置計數(shù)為5
lockDoor.await(); //等待閉鎖完成
return "放假啦...";
}
@GetMapping(value = "/go/{id}")
public String go(@PathVariable("id") Integer id) {
RCountDownLatch lockDoor = redisson.getCountDownLatch("lockDoor");
lockDoor.countDown(); // 計數(shù)減1
return id+"班都走光了";
}結(jié)果:我們先訪問 /lockDoor 線程將會阻塞,連續(xù)訪問五次 /go/1 ,輸出 放假了。
三.緩存數(shù)據(jù)一致性解決方案




盡量給鎖加上過期時間;對于讀寫狀態(tài)時,應該加上分布式讀寫鎖。
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
springboot新建項目pom.xml文件第一行報錯的解決
這篇文章主要介紹了springboot新建項目pom.xml文件第一行報錯的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-01-01
SpringMVC通過注解實現(xiàn)全局異常處理問題小結(jié)
文章介紹SpringMVC全局異常處理機制,通過@ControllerAdvice和@ExceptionHandler注解實現(xiàn)統(tǒng)一異常處理,提升代碼可維護性與響應一致性,減少重復try-catch代碼,增強系統(tǒng)健壯性,本文給大家介紹的非常詳細,感興趣的朋友跟隨小編一起看看吧2025-09-09
Netty之使用DelimiterBasedFrameDecoder進行消息分隔詳解
這篇文章主要介紹了Netty之使用DelimiterBasedFrameDecoder進行消息分隔詳解,在使用Netty進行TCP消息傳輸時,為了上層協(xié)議能夠?qū)ο⒄_區(qū)分,避免粘包和拆包導致的問題,一般可以通過消息定長、將回車換行符作為消息結(jié)束符,需要的朋友可以參考下2023-12-12
Java之定時器Timer和定時任務TimerTask應用以及原理解讀
文章介紹了Java JDK自帶的定時器Timer和定時任務TimerTask的使用和原理,Timer和TimerTask成對出現(xiàn),Timer是定時器,TimerTask是定時任務,TimerTask實現(xiàn)Runnable接口的run方法,Timer的屬性TimerThreadthread繼承Thread2024-12-12
springboot內(nèi)嵌Tomcat安全漏洞修復方式
針對CVE-2020-1938漏洞,建議升級Tomcat至安全版本以避免受影響,影響版本包括:Apache Tomcat 9.x小于9.0.31、Apache Tomcat 8.x小于8.5.51、Apache Tomcat 7.x小于7.0.100及Apache Tomcat 6.x,2024-10-10
Java?ThreadPoolExecutor線程池有關介紹
這篇文章主要介紹了Java?ThreadPoolExecutor線程池有關介紹,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下2022-09-09
java LocalDateTime加時間,計算兩個時間的差方式
文章介紹了如何在Java中使用LocalDateTime類添加時間并計算兩個時間的差值,通過比較來總結(jié)個人經(jīng)驗,并鼓勵讀者參考和支持腳本之家2025-03-03

