SpringBoot集成Redisson實(shí)現(xiàn)分布式鎖的方法示例
上篇 《SpringBoot 集成 redis 分布式鎖優(yōu)化》對(duì)死鎖的問題進(jìn)行了優(yōu)化,今天介紹的是 redis 官方推薦使用的 Redisson ,Redisson 架設(shè)在 redis 基礎(chǔ)上的 Java 駐內(nèi)存數(shù)據(jù)網(wǎng)格(In-Memory Data Grid),基于NIO的 Netty 框架上,利用了 redis 鍵值數(shù)據(jù)庫。功能非常強(qiáng)大,解決了很多分布式架構(gòu)中的問題。
Github的wiki地址: https://github.com/redisson/redisson/wiki
官方文檔: https://github.com/redisson/redisson/wiki/目錄
項(xiàng)目代碼結(jié)構(gòu)圖:
導(dǎo)入依賴
<dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.8.0</version> </dependency>
屬性配置
在 application.properites 資源文件中添加單機(jī)&哨兵相關(guān)配置
server.port=3000 # redisson lock 單機(jī)模式 redisson.address=redis://127.0.0.1:6379 redisson.password= #哨兵模式 #redisson.master-name= master #redisson.password= #redisson.sentinel-addresses=10.47.91.83:26379,10.47.91.83:26380,10.47.91.83:26381
注意:
這里如果不加 redis:// 前綴會(huì)報(bào) URI 構(gòu)建錯(cuò)誤
Caused by: java.net.URISyntaxException: Illegal character in scheme name at index 0
更多的配置信息可以去官網(wǎng)查看
定義Lock的接口定義類
package com.tuhu.thirdsample.service;
import org.redisson.api.RLock;
import java.util.concurrent.TimeUnit;
/**
* @author chendesheng
* @create 2019/10/12 10:48
*/
public interface DistributedLocker {
RLock lock(String lockKey);
RLock lock(String lockKey, int timeout);
RLock lock(String lockKey, TimeUnit unit, int timeout);
boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime);
void unlock(String lockKey);
void unlock(RLock lock);
}
Lock接口實(shí)現(xiàn)類
package com.tuhu.thirdsample.service;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import java.util.concurrent.TimeUnit;
/**
* @author chendesheng
* @create 2019/10/12 10:49
*/
public class RedissonDistributedLocker implements DistributedLocker{
private RedissonClient redissonClient;
@Override
public RLock lock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock();
return lock;
}
@Override
public RLock lock(String lockKey, int leaseTime) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock(leaseTime, TimeUnit.SECONDS);
return lock;
}
@Override
public RLock lock(String lockKey, TimeUnit unit ,int timeout) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock(timeout, unit);
return lock;
}
@Override
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;
}
}
@Override
public void unlock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
lock.unlock();
}
@Override
public void unlock(RLock lock) {
lock.unlock();
}
public void setRedissonClient(RedissonClient redissonClient) {
this.redissonClient = redissonClient;
}
}
redisson屬性裝配類
package com.tuhu.thirdsample.common;
import lombok.Data;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* @author chendesheng
* @create 2019/10/11 20:04
*/
@Configuration
@ConfigurationProperties(prefix = "redisson")
@ConditionalOnProperty("redisson.password")
@Data
public class RedissonProperties {
private int timeout = 3000;
private String address;
private String password;
private int database = 0;
private int connectionPoolSize = 64;
private int connectionMinimumIdleSize=10;
private int slaveConnectionPoolSize = 250;
private int masterConnectionPoolSize = 250;
private String[] sentinelAddresses;
private String masterName;
}
SpringBoot自動(dòng)裝配類
package com.tuhu.thirdsample.configuration;
import com.tuhu.thirdsample.common.RedissonProperties;
import com.tuhu.thirdsample.service.DistributedLocker;
import com.tuhu.thirdsample.service.RedissonDistributedLocker;
import com.tuhu.thirdsample.util.RedissonLockUtil;
import org.apache.commons.lang3.StringUtils;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.config.SentinelServersConfig;
import org.redisson.config.SingleServerConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author chendesheng
* @create 2019/10/12 10:50
*/
@Configuration
@ConditionalOnClass(Config.class)
@EnableConfigurationProperties(RedissonProperties.class)
public class RedissonAutoConfiguration {
@Autowired
private RedissonProperties redissonProperties;
/**
* 哨兵模式自動(dòng)裝配
* @return
*/
@Bean
@ConditionalOnProperty(name="redisson.master-name")
RedissonClient redissonSentinel() {
Config config = new Config();
SentinelServersConfig serverConfig = config.useSentinelServers().addSentinelAddress(redissonProperties.getSentinelAddresses())
.setMasterName(redissonProperties.getMasterName())
.setTimeout(redissonProperties.getTimeout())
.setMasterConnectionPoolSize(redissonProperties.getMasterConnectionPoolSize())
.setSlaveConnectionPoolSize(redissonProperties.getSlaveConnectionPoolSize());
if(StringUtils.isNotBlank(redissonProperties.getPassword())) {
serverConfig.setPassword(redissonProperties.getPassword());
}
return Redisson.create(config);
}
/**
* 單機(jī)模式自動(dòng)裝配
* @return
*/
@Bean
@ConditionalOnProperty(name="redisson.address")
RedissonClient redissonSingle() {
Config config = new Config();
SingleServerConfig serverConfig = config.useSingleServer()
.setAddress(redissonProperties.getAddress())
.setTimeout(redissonProperties.getTimeout())
.setConnectionPoolSize(redissonProperties.getConnectionPoolSize())
.setConnectionMinimumIdleSize(redissonProperties.getConnectionMinimumIdleSize());
if(StringUtils.isNotBlank(redissonProperties.getPassword())) {
serverConfig.setPassword(redissonProperties.getPassword());
}
return Redisson.create(config);
}
/**
* 裝配locker類,并將實(shí)例注入到RedissLockUtil中
* @return
*/
@Bean
DistributedLocker distributedLocker(RedissonClient redissonClient) {
DistributedLocker locker = new RedissonDistributedLocker();
((RedissonDistributedLocker) locker).setRedissonClient(redissonClient);
RedissonLockUtil.setLocker(locker);
return locker;
}
}
Lock幫助類
package com.tuhu.thirdsample.util;
import com.tuhu.thirdsample.service.DistributedLocker;
import org.redisson.api.RLock;
import java.util.concurrent.TimeUnit;
/**
* @author chendesheng
* @create 2019/10/12 10:54
*/
public class RedissonLockUtil {
private static DistributedLocker redissLock;
public static void setLocker(DistributedLocker locker) {
redissLock = locker;
}
/**
* 加鎖
* @param lockKey
* @return
*/
public static RLock lock(String lockKey) {
return redissLock.lock(lockKey);
}
/**
* 釋放鎖
* @param lockKey
*/
public static void unlock(String lockKey) {
redissLock.unlock(lockKey);
}
/**
* 釋放鎖
* @param lock
*/
public static void unlock(RLock lock) {
redissLock.unlock(lock);
}
/**
* 帶超時(shí)的鎖
* @param lockKey
* @param timeout 超時(shí)時(shí)間 單位:秒
*/
public static RLock lock(String lockKey, int timeout) {
return redissLock.lock(lockKey, timeout);
}
/**
* 帶超時(shí)的鎖
* @param lockKey
* @param unit 時(shí)間單位
* @param timeout 超時(shí)時(shí)間
*/
public static RLock lock(String lockKey, TimeUnit unit , int timeout) {
return redissLock.lock(lockKey, unit, timeout);
}
/**
* 嘗試獲取鎖
* @param lockKey
* @param waitTime 最多等待時(shí)間
* @param leaseTime 上鎖后自動(dòng)釋放鎖時(shí)間
* @return
*/
public static boolean tryLock(String lockKey, int waitTime, int leaseTime) {
return redissLock.tryLock(lockKey, TimeUnit.SECONDS, waitTime, leaseTime);
}
/**
* 嘗試獲取鎖
* @param lockKey
* @param unit 時(shí)間單位
* @param waitTime 最多等待時(shí)間
* @param leaseTime 上鎖后自動(dòng)釋放鎖時(shí)間
* @return
*/
public static boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime) {
return redissLock.tryLock(lockKey, unit, waitTime, leaseTime);
}
}
控制層
package com.tuhu.thirdsample.task;
import com.tuhu.thirdsample.common.KeyConst;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
/**
* @author chendesheng
* @create 2019/10/12 11:03
*/
@RestController
@RequestMapping("/lock")
@Slf4j
public class LockController {
@Autowired
RedissonClient redissonClient;
@GetMapping("/task")
public void task(){
log.info("task start");
RLock lock = redissonClient.getLock(KeyConst.REDIS_LOCK_KEY);
boolean getLock = false;
try {
if (getLock = lock.tryLock(0,5,TimeUnit.SECONDS)){
//執(zhí)行業(yè)務(wù)邏輯
System.out.println("拿到鎖干活");
}else {
log.info("Redisson分布式鎖沒有獲得鎖:{},ThreadName:{}",KeyConst.REDIS_LOCK_KEY,Thread.currentThread().getName());
}
} catch (InterruptedException e) {
log.error("Redisson 獲取分布式鎖異常,異常信息:{}",e);
}finally {
if (!getLock){
return;
}
//如果演示的話需要注釋該代碼;實(shí)際應(yīng)該放開
//lock.unlock();
//log.info("Redisson分布式鎖釋放鎖:{},ThreadName :{}", KeyConst.REDIS_LOCK_KEY, Thread.currentThread().getName());
}
}
}
RLock 繼承自 java.util.concurrent.locks.Lock ,可以將其理解為一個(gè)重入鎖,需要手動(dòng)加鎖和釋放鎖 。
來看它其中的一個(gè)方法:tryLock(long waitTime, long leaseTime, TimeUnit unit)
getLock = lock.tryLock(0,5,TimeUnit.SECONDS)
通過 tryLock() 的參數(shù)可以看出,在獲取該鎖時(shí)如果被其他線程先拿到鎖就會(huì)進(jìn)入等待,等待 waitTime 時(shí)間,如果還沒用機(jī)會(huì)獲取到鎖就放棄,返回 false;若獲得了鎖,除非是調(diào)用 unlock 釋放,那么會(huì)一直持有鎖,直到超過 leaseTime 指定的時(shí)間。
以上就是 Redisson 實(shí)現(xiàn)分布式鎖的核心方法,有人可能要問,那怎么確定拿的是同一把鎖,分布式鎖在哪?
這就是 Redisson 的強(qiáng)大之處,其底層還是使用的 Redis 來作分布式鎖,在我們的RedissonManager中已經(jīng)指定了 Redis 實(shí)例,Redisson 會(huì)進(jìn)行托管,其原理與我們手動(dòng)實(shí)現(xiàn) Redis 分布式鎖類似。
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- SpringBoot集成Redisson實(shí)現(xiàn)分布式鎖的示例代碼
- Springboot使用redisson實(shí)現(xiàn)分布式鎖的代碼示例
- SpringBoot整合分布式鎖redisson的示例代碼
- 基于Redis分布式鎖Redisson及SpringBoot集成Redisson
- SpringBoot整合Redisson實(shí)現(xiàn)分布式鎖
- Springboot中如何使用Redisson實(shí)現(xiàn)分布式鎖淺析
- SpringBoot使用Redisson實(shí)現(xiàn)分布式鎖(秒殺系統(tǒng))
- Spring Task實(shí)現(xiàn)定時(shí)任務(wù)的示例
相關(guān)文章
IntelliJ IDEA 安裝 Grep Console插件 自定義控制臺(tái)輸出多顏色格式功能
由于Intellij idea不支持顯示ascii顏色,grep-console插件能很好的解決這個(gè)問題,下面就以開發(fā)JavaEE項(xiàng)目中,結(jié)合Log4j配置多顏色日志輸出功能,感興趣的朋友一起看看吧2020-05-05
TF-IDF理解及其Java實(shí)現(xiàn)代碼實(shí)例
這篇文章主要介紹了TF-IDF理解及其Java實(shí)現(xiàn)代碼實(shí)例,簡(jiǎn)單介紹了tfidf算法及其相應(yīng)公式,然后分享了Java實(shí)現(xiàn)代碼,具有一定參考價(jià)值,需要的朋友可以了解下。2017-11-11
maven如何在tomcat8中實(shí)現(xiàn)自動(dòng)部署
本篇文章主要介紹了maven如何在tomcat8中實(shí)現(xiàn)自動(dòng)部署,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-10-10
springboot實(shí)現(xiàn)token驗(yàn)證登陸狀態(tài)的示例代碼
本文主要介紹了spring?boot?實(shí)現(xiàn)token驗(yàn)證登陸狀態(tài),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-07-07
mybatis?實(shí)體類字段大小寫問題?字段獲取不到值的解決
這篇文章主要介紹了mybatis?實(shí)體類字段大小寫問題?字段獲取不到值的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11

