Redisson分布式锁的实现
maven 引入依赖包
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.16.4</version>
</dependency>
静态方式 Redisson 分布式重入锁用法
Redisson 支持单点模式、主从模式、哨兵模式、集群模式,这里以单点模式为例:
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import java.util.concurrent.TimeUnit;
public class RedisLock {
public static void setLock(String lockKey,Long waitTimeout,Long leaseTime){
// 1.构造redisson实现分布式锁必要的Config redis 的连接地址和密码以及选择的数据库
Config config = new Config();
config.useSingleServer().setAddress("redis连接地址").setPassword("密码").setDatabase(第几个数据库);
// 2.构造RedissonClient
RedissonClient redissonClient = Redisson.create(config);
// 3.获取锁对象实例(无法保证是按线程的顺序获取到)
RLock rLock = redissonClient.getLock(lockKey);
try {
/**
* 4.尝试获取锁
* waitTimeout 尝试获取锁的最大等待时间,超过这个值,则认为获取锁失败
* leaseTime 锁的持有时间,超过这个时间锁会自动失效(值应设置为大于业务处理的时间,确保在锁有效期内业务能处理完)
*/
boolean res = rLock.tryLock((long)waitTimeout, (long)leaseTime, TimeUnit.SECONDS);
System.out.println(res);
if (res) {
//成功获得锁,在这里处理业务
}
} catch (Exception e) {
throw new RuntimeException("aquire lock fail");
}finally{
//无论如何, 最后都要解锁
rLock.unlock();
}
}
}
springboot管理方式 Redisson 分布式重入锁用法
RedissonConfig 配置类
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.ClusterServersConfig;
import org.redisson.config.Config;
import org.redisson.config.SentinelServersConfig;
import org.redisson.config.SingleServerConfig;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
import javax.annotation.Resource;
@Configuration
@EnableConfigurationProperties(RedissonProperties.class)
public class RedissonConfig {
@Resource
private RedissonProperties redissonProperties;
@Bean
@ConditionalOnMissingBean
RedissonClient redissonSingle() {
// 1.构造redisson实现分布式锁必要的Config redis 的连接地址和密码以及选择的数据库
Config config = new Config();
//单机 redis地址
String host = this.redissonProperties.getHost();
//哨兵地址
String[] sentinelAddr = this.redissonProperties.getSentinelAddresses();
//其他节点地址
String[] nodeAddresses = this.redissonProperties.getNodeAddresses();
if (!StringUtils.isEmpty(host)) {
config.setLockWatchdogTimeout((long)this.redissonProperties.getLockWatchdogTimeout());
SingleServerConfig serverConfig = ((SingleServerConfig)config.useSingleServer().setAddress("redis://" + this.redissonProperties.getHost() + ":" + this.redissonProperties.getPort()).setTimeout((int)this.redissonProperties.getTimeout().getSeconds() * 1000)).setConnectionPoolSize(this.redissonProperties.getConnectionPoolSize()).setConnectionMinimumIdleSize(this.redissonProperties.getConnectionMinimumIdleSize().setDatabase(this.redissonProperties.getDatabase()));
if (!StringUtils.isEmpty(this.redissonProperties.getPassword())) {
serverConfig.setPassword(this.redissonProperties.getPassword());
}
} else if (sentinelAddr != null && sentinelAddr.length > 0) {
config.setLockWatchdogTimeout((long)this.redissonProperties.getLockWatchdogTimeout());
SentinelServersConfig serverConfig = (SentinelServersConfig)((SentinelServersConfig)((SentinelServersConfig)config.useSentinelServers().addSentinelAddress(this.redissonProperties.getSentinelAddresses()).setMasterName(this.redissonProperties.getMasterName()).setTimeout((int)this.redissonProperties.getTimeout().getSeconds() * 1000)).setMasterConnectionPoolSize(this.redissonProperties.getMasterConnectionPoolSize())).setSlaveConnectionPoolSize(this.redissonProperties.getSlaveConnectionPoolSize());
if (!StringUtils.isEmpty(this.redissonProperties.getPassword())) {
serverConfig.setPassword(this.redissonProperties.getPassword());
}
} else if (nodeAddresses != null && nodeAddresses.length > 0) {
config.setLockWatchdogTimeout((long)this.redissonProperties.getLockWatchdogTimeout());
ClusterServersConfig serverConfig = config.useClusterServers().addNodeAddress(this.redissonProperties.getNodeAddresses()).setScanInterval(this.redissonProperties.getScanInterval());
if (!StringUtils.isEmpty(this.redissonProperties.getPassword())) {
serverConfig.setPassword(this.redissonProperties.getPassword());
}
}
// 2.根据config构造RedissonClient
RedissonClient redissonClient = Redisson.create(config);
return redissonClient;
}
@Bean
@ConditionalOnClass(RedissonClient.class)
RedisLock redisLock(){
RedisLock redisLock = new RedisLock();
return redisLock;
}
}
RedissonProperties 属性类 (根据前缀去yml启动配置文件配置属性)
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.time.Duration;
@ConfigurationProperties(
prefix = "spring.redis"
)
@Data
public class RedissonProperties {
private Duration timeout;
private String host;
private int port = 6379;
private String password;
private int database = 0;
private int connectionPoolSize = 64;
private int connectionMinimumIdleSize = 10;
private int slaveConnectionPoolSize = 250;
private int masterConnectionPoolSize = 250;
private int lockWatchdogTimeout = 30000;
private String[] sentinelAddresses;
private String masterName;
private int scanInterval;
private String[] nodeAddresses;
private int maxPushCount = 6;
}
RedisLock (自己编写redission锁工具类)
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.redisson.api.RLock;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
@Component
public class RedisLock {
@Autowired(
required = false
)
private RedissonClient redissonClient;
/**
* @param lockKey
* @param task 这里用的是Supplier,可以根据业务自己定制
* @return
*/
public void tryLock(String lockKey, Supplier<?> task,int num){
// 获取锁对象实例(无法保证是按线程的顺序获取到)
RLock rLock = redissonClient.getLock(lockKey);
try {
boolean res = rLock.tryLock();
//多次尝试获得
while(--num>0 && !res){
res = rLock.tryLock();
}
if (res) {
//成功获得锁,在这里处理业务
task.get();
}else{
throw new Exception("aquire lock fail");
}
} catch (Exception e) {
//根据业务业务情况来确定是抛异常还是返回结果
throw new RuntimeException(e);
}finally{
//当前线程还占领着锁,则释锁
RLock lock = redissonClient.getLock(lockKey);
if (lock != null && lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
public void tryLock(String lockKey, Supplier<?> task){
tryLock(lockKey,task,1);
}
/**
*
* @param lockKey
* @param task 这里用的是Supplier,可以根据业务自己定制
* @param waitTimeout 尝试获取锁的最大等待时间,超过这个值,则认为获取锁失败
* @param leaseTime 锁的持有时间,超过这个时间锁会自动失效(值应设置为大于业务处理的时间,确保在锁有效期内业务能处理完)
* @param unit
* @param num 总共尝试获得锁的次数
* @return
*/
public T <T> tryLock(String lockKey, Supplier<T> task, Long waitTimeout, Long leaseTime,TimeUnit unit,int num){
// 获取锁对象实例(无法保证是按线程的顺序获取到)
RLock rLock = redissonClient.getLock(lockKey);
try {
/**
* 尝试获取锁
* waitTimeout 尝试获取锁的最大等待时间,超过这个值,则认为获取锁失败
* leaseTime 锁的持有时间,超过这个时间锁会自动失效(值应设置为大于业务处理的时间,确保在锁有效期内业务能处理完)
*/
boolean res = rLock.tryLock((long)waitTimeout, (long)leaseTime, unit);
//多次尝试获得
while(--num>0 && !res){
res = rLock.tryLock((long)waitTimeout, (long)leaseTime, unit);
}
if (res) {
long start = System.currentTimeMillis();
log.info("aquire lock success [{}]",lockKey);
//成功获得锁,在这里处理业务
T result = task.get();
log.info("execute task success [{}] and cost time {} ",lockKey,System.currentTimeMillis()-start);
return result;
}else{
log.error("aquire lock fail [{}]",lockKey);
throw new Exception("aquire lock fail");
}
//抛出业务本身抛出的异常(根据你实际项目定义)
}catch (BusinessException e){
log.error("execute task fail [{}]",lockKey);
throw e;
}
catch (Exception e) {
throw new RuntimeException(e);
}
finally{
//当前线程还占领着锁,则释放锁
RLock lock = redissonClient.getLock(lockKey);
if (lock != null && lock.isHeldByCurrentThread()) {
lock.unlock();
}
log.info("unlock [{}]",lockKey);
}
}
public T <T> tryLock(String lockKey, Supplier<T> task, Long waitTimeout, Long leaseTime,TimeUnit unit){
return tryLock(lockKey,task,waitTimeout,leaseTime,unit,1);
}
/**
* 批量加锁
* @param lockKeyList 需要加锁的key
* @param task
* @return
* @param <T>
*/
public <T> T multiTryLock(List<String> lockKeyList, Supplier<T> task) {
return multiTryLock(lockKeyList, task, 5000L, 30000L, TimeUnit.MILLISECONDS, 1);
}
public <T> T multiTryLock(List<String> lockKeyList, Supplier<T> task, Long waitTimeout, Long leaseTime, TimeUnit unit) {
return multiTryLock(lockKeyList, task, waitTimeout, leaseTime, unit, 1);
}
public <T> T multiTryLock(List<String> lockKeyList, Supplier<T> task, Long waitTimeout, Long leaseTime, TimeUnit unit, int num) {
RedissonMultiLock multiLock = getMultiLock(lockKeyList);
try {
boolean res = multiLock.tryLock(waitTimeout, leaseTime, unit);
while (--num > 0 && !res) {
res = multiLock.tryLock(waitTimeout, leaseTime, unit);
}
if (res) {
long start = System.currentTimeMillis();
log.info("acquire multiLock success keyListSize:[{}]", lockKeyList.size());
T result = task.get();
log.info("execute multiLock success keyListSize:[{}] and cost time {} ", lockKeyList.size(), System.currentTimeMillis() - start);
return result;
} else {
log.error("acquire multiLock fail keyListSize:[{}]", lockKeyList.size());
throw new BusinessException(BlogisWmsResultStatusEnum.SYS_BUSY);
}
} catch (BusinessException e) {
log.error("execute task fail keyListSize:[{}] msg:[{}]", lockKeyList.size(),e.getMessage()==null?e.getCause():e.getMessage());
throw e;
} catch (Exception e) {
log.error("execute task fail keyListSize:[{}] msg:[{}]", lockKeyList.size(),e.getMessage()==null?e.getCause():e.getMessage());
throw new RuntimeException(e);
} finally {
unMultiLock(lockKeyList);
log.info("unMultiLock [{}]", lockKeyList.size());
}
}
/**
* 获得连锁
* @param lockKeyList
* @return
*/
private RedissonMultiLock getMultiLock(List<String> lockKeyList) {
List<RLock> rLockList = new ArrayList<>(lockKeyList.size());
for (String key : lockKeyList) {
RLock rLock = redissonClient.getLock(key);
rLockList.add(rLock);
}
RLock[] rLocks = rLockList.toArray(new RLock[0]);
return (RedissonMultiLock)redissonClient.getMultiLock(rLocks);
}
/**
* 释放连锁
* @param lockKeyList
*/
private void unMultiLock(List<String> lockKeyList) {
for (String key : lockKeyList) {
RLock lock = redissonClient.getLock(key);
if (lock != null && lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
}