Spring?Boot?集成Redisson實現(xiàn)分布式鎖詳細(xì)案例
前言
Spring Boot集成Redis實現(xiàn)單機分布式鎖針對單機分布式鎖還是存在鎖定續(xù)期、可重入的問題,本文將采用Spring Boot 集成Ression實現(xiàn)分布式鎖進(jìn)行詳細(xì)講解。
分布式鎖實現(xiàn)
引入jar包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.13.6</version>
</dependency>說明:關(guān)于集成Redisson,我們需要注意與Spring Boot的版本對應(yīng)。
具體對應(yīng)的關(guān)系如下:

注意:3.13.6對應(yīng)的Spring Boot的版本為2.3.0,而redis-spring-data為redis-spring-data-23。我們可以通過查看pom文件的引用從而得到依賴關(guān)系。
Redisson的配置
application.yml中引入redisson.yml配置
redis:
redisson:
file: classpath:redisson.ymlredisson.yml配置
singleServerConfig:
password: xxxx
address: "redis://127.0.0.1:6379"
database: 1
threads: 0
nettyThreads: 0
codec: !<org.redisson.codec.FstCodec> {}
transportMode: "NIO"說明:本文配置的是單機環(huán)境,如果需要配置集群環(huán)境,可以采用如下配置:
clusterServersConfig:
idleConnectionTimeout: 10000
connectTimeout: 10000
timeout: 3000
retryAttempts: 3
retryInterval: 1500
failedSlaveReconnectionInterval: 3000
failedSlaveCheckInterval: 60000
password: null
subscriptionsPerConnection: 5
clientName: null
loadBalancer: !<org.redisson.connection.balancer.RoundRobinLoadBalancer> {}
subscriptionConnectionMinimumIdleSize: 1
subscriptionConnectionPoolSize: 50
slaveConnectionMinimumIdleSize: 24
slaveConnectionPoolSize: 64
masterConnectionMinimumIdleSize: 24
masterConnectionPoolSize: 64
readMode: "SLAVE"
subscriptionMode: "SLAVE"
nodeAddresses:
- "redis://127.0.0.1:7004"
- "redis://127.0.0.1:7001"
- "redis://127.0.0.1:7000"
scanInterval: 1000
pingConnectionInterval: 0
keepAlive: false
tcpNoDelay: false
threads: 16
nettyThreads: 32
codec: !<org.redisson.codec.MarshallingCodec> {}
transportMode: "NIO"封裝Redisson工具類
@Component
public class RedissonLockUtil
{
private static final Logger logger = LoggerFactory.getLogger(RedissonLockUtil.class);
@Autowired
private RedissonClient redissonClient;
/**
* 加鎖
* @param lockKey
* @return
*/
public RLock lock(String lockKey)
{
RLock lock = redissonClient.getLock(lockKey);
return lock;
}
/**
* 公平鎖
* @param key
* @return
*/
public RLock fairLock(String key)
{
return redissonClient.getFairLock(key);
}
/**
* 帶超時的鎖
* @param lockKey
* @param timeout 超時時間 單位:秒
*/
public RLock lock(String lockKey, int timeout)
{
RLock lock = redissonClient.getLock(lockKey);
lock.lock(timeout, TimeUnit.SECONDS);
return lock;
}
/**
* 讀寫鎖
* @param key
* @return
*/
public RReadWriteLock readWriteLock(String key) {
return redissonClient.getReadWriteLock(key);
}
/**
* 帶超時的鎖
* @param lockKey
* @param unit 時間單位
* @param timeout 超時時間
*/
public RLock lock(String lockKey, TimeUnit unit ,int timeout) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock(timeout, unit);
return lock;
}
/**
* 加鎖
* @param key
* @param supplier
* @return
*/
public <T> T lock(String key, Supplier<T> supplier) {
RLock lock = lock(key);
try {
lock.lock();
return supplier.get();
} finally {
if (lock != null && lock.isLocked()) {
lock.unlock();
}
}
}
/**
* 嘗試獲取鎖
* @param lockKey
* @param waitTime 等待時間
* @param leaseTime 自動釋放鎖時間
* @return
*/
public boolean tryLock(String lockKey, int waitTime, int leaseTime) {
RLock lock = redissonClient.getLock(lockKey);
try {
return lock.tryLock(waitTime, leaseTime, TimeUnit.SECONDS);
} catch (InterruptedException e) {
return false;
}
}
/**
* 嘗試獲取鎖
* @param lockKey
* @param unit 時間單位
* @param waitTime 等待時間
* @param leaseTime 自動釋放鎖時間
* @return
*/
public boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime) {
RLock lock = redissonClient.getLock(lockKey);
try {
return lock.tryLock(waitTime, leaseTime, unit);
} catch (InterruptedException e) {
return false;
}
}
/**
* 釋放鎖
* @param lockKey
*/
public void unlock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
lock.unlock();
}
/**
* 釋放鎖
* @param lock
*/
public void unlock(RLock lock)
{
lock.unlock();
}
} 模擬秒殺扣減庫存
public int lockStock()
{
String lockKey="lock:stock";
String clientId = UUID.randomUUID().toString();
//加鎖
RLock lock=redissonLockUtil.lock(lockKey);
lock.lock();
try
{
logger.info("加鎖成功 clientId:{}",clientId);
int stockNum= Integer.valueOf((String)redisUtil.get("seckill:goods:stock"));
if(stockNum>0)
{
stockNum--;
redisUtil.set("seckill:goods:stock",String.valueOf(stockNum));
logger.info("秒殺成功,剩余庫存:{}",stockNum);
}
else
{
logger.error("秒殺失敗,剩余庫存:{}", stockNum);
}
//獲取庫存數(shù)量
return stockNum;
}
catch (Exception e)
{
logger.error("decry stock eror",e);
}
finally
{
if(lock!=null)
{
lock.unlock();
}
}
return 0;
}測試代碼
@RequestMapping("/redisLockTest")
public void redisLockTest()
{
// 初始化秒殺庫存數(shù)量
redisUtil.set("seckill:goods:stock", "10");
List<Future> futureList = new ArrayList<>();
//多線程異步執(zhí)行
ExecutorService executors = Executors.newScheduledThreadPool(10);
//
for (int i = 0; i < 30; i++)
{
futureList.add(executors.submit(this::lockStock));
try
{
Thread.sleep(100);
}
catch (InterruptedException e)
{
logger.error("redisLockTest error",e);
}
}
// 等待結(jié)果,防止主線程退出
futureList.forEach(t -> {
try
{
int stockNum =(int) t.get();
logger.info("庫存剩余數(shù)量:{}",stockNum);
}
catch (Exception e)
{
logger.error("get stock num error",e);
}
});
}執(zhí)行結(jié)果如下:

總結(jié)
本文針對Spring Boot集成Redisson的基本使用,關(guān)于Redisson源碼的分析將在后續(xù)的文章中進(jìn)行講解,如有疑問,請隨時反饋,
- SpringBoot利用注解來實現(xiàn)Redis分布式鎖
- springboot 集成redission 以及分布式鎖的使用詳解
- SpringBoot之使用Redis實現(xiàn)分布式鎖(秒殺系統(tǒng))
- Redis分布式鎖升級版RedLock及SpringBoot實現(xiàn)方法
- SpringBoot整合Redis正確的實現(xiàn)分布式鎖的示例代碼
- SpringBoot使用Redis實現(xiàn)分布式鎖
- SpringBoot使用Redisson實現(xiàn)分布式鎖(秒殺系統(tǒng))
- SpringBoot集成Redisson實現(xiàn)分布式鎖的方法示例
- springboot+redis分布式鎖實現(xiàn)模擬搶單
- 適合 Spring Boot 3.0x的Redis 分布式鎖詳解
相關(guān)文章
如何使用try-with-resource機制關(guān)閉連接
這篇文章主要介紹了使用try-with-resource機制關(guān)閉連接的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07
Java虛擬機JVM之server模式與client模式的區(qū)別
這篇文章主要介紹了Java虛擬機JVM的client模式和Server模式兩者的區(qū)別和聯(lián)系2017-12-12
Spring?boot2.0?實現(xiàn)日志集成的方法(2)
這篇文章主要介紹了Spring?boot2.0?實現(xiàn)日志集成的方法,上一章講解了spring?boot日志簡單集成,這篇我們將日志進(jìn)行分類,常規(guī)日志、異常日志、監(jiān)控日志等,需要將日志輸出到不同的文件,具體內(nèi)容需要的小伙伴可以參考一下2022-04-04
SpringBoot利用jpa連接MySQL數(shù)據(jù)庫的方法
這篇文章主要介紹了SpringBoot利用jpa連接MySQL數(shù)據(jù)庫的方法,本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-10-10

