Redisson的正确使用以及封装分布式锁
ztj100 2024-11-04 15:16 24 浏览 0 评论
设置过期时间方式
//注入RedissonClient对象
@Resource
private RedissonClient redissonClient;
public void testLock5() {
RLock lock = redissonClient.getLock("redis:qsl:test");
try {
boolean triedLock = lock.tryLock(1, 3, TimeUnit.SECONDS);
if (triedLock) {
System.out.println(String.format("线程 %s 获取到锁, 执行业务中...", Thread.currentThread().getName()));
TimeUnit.SECONDS.sleep(5);
System.out.println(String.format("线程 %s 业务执行完成", Thread.currentThread().getName()));
} else {
System.out.println("thread-id=" + Thread.currentThread().getId() + " 锁获取失败"
);
testLock5();
}
} catch (Exception e) {
e.printStackTrace();
Thread.currentThread().interrupt();
} finally {
//释放锁
if (lock.isHeldByCurrentThread()) {
System.out.println("thread-id=" + Thread.currentThread().getId()
+ "开始释放锁");
lock.unlock();
System.out.println("thread-id=" + Thread.currentThread().getId()
+ "锁释放成功");
}
}
}
public void multiThreadLock5() throws InterruptedException {
for (int i = 0; i < 2; i++) {
new Thread(this::testLock5).start();
}
TimeUnit.SECONDS.sleep(10);
System.out.println("main 退出");
}
执行结果
线程 Thread-3 获取到锁, 执行业务中...
thread-id=51 锁获取失败
thread-id=51 锁获取失败
thread-id=51 锁获取失败
线程 Thread-4 获取到锁, 执行业务中...
线程 Thread-3 业务执行完成
线程 Thread-4 业务执行完成
main 退出
线程Thread-3首先获得了锁,thread-id=51后进来,取不到锁只能重试,因为代码需要执行5秒,所以到第四秒的时候,线程 Thread-3释放了锁,所以线程 Thread-4 获取到锁, 执行自己的业务,
接着5秒过后线程 Thread-3执行完成,但是他的锁已经在第三秒后被释放了,所以不用执行lock.unlock();到第8秒结束后,线程 Thread-4 业务执行完成,同样的它在第6秒结束后被释放了,所以不用执行lock.unlock();
不设置过期时间的方式
public void multiThreadLock6() throws InterruptedException {
for (int i = 0; i < 2; i++) {
new Thread(this::testLock6).start();
}
TimeUnit.SECONDS.sleep(10);
System.out.println("main 退出");
}
public void testLock6() {
RLock lock = redissonClient.getLock("redis:qsl:test");
try {
boolean triedLock = lock.tryLock(1, TimeUnit.SECONDS);
if (triedLock) {
System.out.println(String.format("线程 %s 获取到锁, 执行业务中...", Thread.currentThread().getName()));
try {
System.out.println("业务处理中...");
} catch (Exception e) {
System.out.println("业务处理异常:" + e.getMessage());
} finally {
lock.unlock();
}
} else {
System.out.println("thread-id=" + Thread.currentThread().getId() + " 锁获取失败"
);
}
} catch (Exception e) {
System.out.println("获职锁或释放锁异常:"+e.getMessage());
}
}
执行结果
线程 Thread-3 获取到锁, 执行业务中...
业务处理中...
线程 Thread-4 获取到锁, 执行业务中...
业务处理中...
main 退出
可以发现Thread-3先获得了锁,在业务执行完成后,释放了锁,Thread-4才能获得锁,依次执行代码。
缺陷:这种方式没有设置锁的过期时间,所以在服务器宕机且没有释放锁的的情况下会出现死锁
我个人其实是更加推荐第一种方式,我们尽量把锁的粒度放到最小,这样出现代码执行时间超过锁超时时间的情况会比较少
自定义封装redisson
pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- <parent>-->
<!-- <groupId>com.cloud.based</groupId>-->
<!-- <artifactId>cloud-based-core</artifactId>-->
<!-- <version>1.0-SNAPSHOT</version>-->
<!-- </parent>-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<artifactId>redis-distributed-lock-core</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.projectlombok</groupId>-->
<!-- <artifactId>lombok</artifactId>-->
<!-- <version>1.16.20</version>-->
<!-- <scope>provided</scope>-->
<!-- </dependency>-->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.5.4</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>27.0.1-jre</version>
</dependency>
</dependencies>
</project>
定义RedissonProperties
/**
* @Description: 读取redis配置信息,封装到当前实体中
*
* @author xub
* @date 2019/6/19 下午9:35
*/
@PropertySource({"classpath:redisson.properties"})
@ConfigurationProperties(prefix = "redisson.lock.server")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class RedissonProperties {
/**
* redis主机地址,ip:port,有多个用半角逗号分隔
*/
private String address;
/**
* 连接类型,支持standalone-单机节点,sentinel-哨兵,cluster-集群,masterslave-主从
*/
private String type;
/**
* redis 连接密码
*/
private String password;
/**
* 选取那个数据库
*/
private int database;
public RedissonProperties setPassword(String password) {
this.password = password;
return this;
}
public RedissonProperties setDatabase(int database) {
this.database = database;
return this;
}
}
定义GlobalConstant全局常量枚举
package com.cloud.based.redisson.constant;
/**
* @author xub
* @Description: 全局常量枚举 用来拼接完整的URL
* @date 2019/6/19 下午9:09
*/
public enum GlobalConstant {
REDIS_CONNECTION_PREFIX("redis://", "Redis地址配置前缀");
private final String constant_value;
private final String constant_desc;
GlobalConstant(String constant_value, String constant_desc) {
this.constant_value = constant_value;
this.constant_desc = constant_desc;
}
public String getConstant_value() {
return constant_value;
}
public String getConstant_desc() {
return constant_desc;
}
}
定义RedisConnectionTypeRedis连接方式
package com.cloud.based.redisson.constant;
/**
* @desc Redis连接方式
* 包含:standalone-单节点部署方式
* sentinel-哨兵部署方式
* cluster-集群方式
* masterslave-主从部署方式
*
* @author xub
* @date 2019/6/20 下午4:21
*/
public enum RedisConnectionType {
STANDALONE("standalone", "单节点部署方式"),
SENTINEL("sentinel", "哨兵部署方式"),
CLUSTER("cluster", "集群方式"),
MASTERSLAVE("masterslave", "主从部署方式");
private final String connection_type;
private final String connection_desc;
private RedisConnectionType(String connection_type, String connection_desc) {
this.connection_type = connection_type;
this.connection_desc = connection_desc;
}
public String getConnection_type() {
return connection_type;
}
public String getConnection_desc() {
return connection_desc;
}
}
定义RedissonManager管理
package com.cloud.based.redisson;
import com.cloud.based.redisson.constant.RedisConnectionType;
import com.cloud.based.redisson.properties.RedissonProperties;
import com.cloud.based.redisson.strategy.RedissonConfigService;
import com.cloud.based.redisson.strategy.impl.ClusterConfigImpl;
import com.cloud.based.redisson.strategy.impl.MasterslaveConfigImpl;
import com.cloud.based.redisson.strategy.impl.SentineConfigImpl;
import com.cloud.based.redisson.strategy.impl.StandaloneConfigImpl;
import com.google.common.base.Preconditions;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.config.Config;
/**
* @Description: Redisson核心配置,用于提供初始化的redisson实例
*
* @author xub
* @date 2019/6/19 下午10:16
*/
@Slf4j
public class RedissonManager {
private Config config = new Config();
private Redisson redisson = null;
public RedissonManager() {
}
public RedissonManager(RedissonProperties redissonProperties) {
try {
//通过不同部署方式获得不同cofig实体
config = RedissonConfigFactory.getInstance().createConfig(redissonProperties);
redisson = (Redisson) Redisson.create(config);
} catch (Exception e) {
log.error("Redisson init error", e);
throw new IllegalArgumentException("please input correct configurations," +
"connectionType must in standalone/sentinel/cluster/masterslave");
}
}
public Redisson getRedisson() {
return redisson;
}
/**
* Redisson连接方式配置工厂
* 双重检查锁
*/
static class RedissonConfigFactory {
private RedissonConfigFactory() {
}
private static volatile RedissonConfigFactory factory = null;
public static RedissonConfigFactory getInstance() {
if (factory == null) {
synchronized (Object.class) {
if (factory == null) {
factory = new RedissonConfigFactory();
}
}
}
return factory;
}
/**
* 根据连接类型获取对应连接方式的配置,基于策略模式
*
* @param redissonProperties redis连接信息
* @return Config
*/
Config createConfig(RedissonProperties redissonProperties) {
Preconditions.checkNotNull(redissonProperties);
Preconditions.checkNotNull(redissonProperties.getAddress(), "redisson.lock.server.address cannot be NULL!");
Preconditions.checkNotNull(redissonProperties.getType(), "redisson.lock.server.password cannot be NULL");
Preconditions.checkNotNull(redissonProperties.getDatabase(), "redisson.lock.server.database cannot be NULL");
String connectionType = redissonProperties.getType();
//声明配置上下文
RedissonConfigService redissonConfigService = null;
if (connectionType.equals(RedisConnectionType.STANDALONE.getConnection_type())) {
redissonConfigService = new StandaloneConfigImpl();
} else if (connectionType.equals(RedisConnectionType.SENTINEL.getConnection_type())) {
redissonConfigService = new SentineConfigImpl();
} else if (connectionType.equals(RedisConnectionType.CLUSTER.getConnection_type())) {
redissonConfigService = new ClusterConfigImpl();
} else if (connectionType.equals(RedisConnectionType.MASTERSLAVE.getConnection_type())) {
redissonConfigService = new MasterslaveConfigImpl();
} else {
throw new IllegalArgumentException("创建Redisson连接Config失败!当前连接方式:" + connectionType);
}
return redissonConfigService.createRedissonConfig(redissonProperties);
}
}
}
定义RedissonConfigService Redisson配置构建接口
package com.cloud.based.redisson.strategy;
import com.cloud.based.redisson.properties.RedissonProperties;
import org.redisson.config.Config;
/**
* @Description: Redisson配置构建接口
*
* @author xub
* @date 2019/6/20 下午3:35
*/
public interface RedissonConfigService {
/**
* 根据不同的Redis配置策略创建对应的Config
* @param redissonProperties
* @return Config
*/
Config createRedissonConfig(RedissonProperties redissonProperties);
}
定义ClusterConfigImpl cluster连接方式
package com.cloud.based.redisson.strategy.impl;
import com.cloud.based.redisson.constant.GlobalConstant;
import com.cloud.based.redisson.properties.RedissonProperties;
import com.cloud.based.redisson.strategy.RedissonConfigService;
import jodd.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.redisson.config.Config;
/**
* @Description: 集群方式Redisson部署
* 地址格式:
* cluster方式至少6个节点(3主3从,3主做sharding,3从用来保证主宕机后可以高可用)
* 格式为: 127.0.0.1:6379,127.0.0.1:6380,127.0.0.1:6381,127.0.0.1:6382,127.0.0.1:6383,127.0.0.1:6384
*
* @author xub
* @date 2019/6/19 下午4:24
*/
@Slf4j
public class ClusterConfigImpl implements RedissonConfigService {
@Override
public Config createRedissonConfig(RedissonProperties redissonProperties) {
Config config = new Config();
try {
String address = redissonProperties.getAddress();
String password = redissonProperties.getPassword();
String[] addrTokens = address.split(",");
//设置cluster节点的服务IP和端口
for (int i = 0; i < addrTokens.length; i++) {
config.useClusterServers()
.addNodeAddress(GlobalConstant.REDIS_CONNECTION_PREFIX.getConstant_value() + addrTokens[i]);
if (StringUtil.isNotBlank(password)) {
config.useClusterServers().setPassword(password);
}
}
log.info("初始化[集群部署]方式Config,redisAddress:" + address);
} catch (Exception e) {
log.error("集群部署 Redisson init error", e);
e.printStackTrace();
}
return config;
}
}
定义MasterslaveConfigImpl 主从部署
package com.cloud.based.redisson.strategy.impl;
import com.cloud.based.redisson.constant.GlobalConstant;
import com.cloud.based.redisson.properties.RedissonProperties;
import com.cloud.based.redisson.strategy.RedissonConfigService;
import jodd.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.redisson.config.Config;
import java.util.ArrayList;
import java.util.List;
/**
* @Description: 主从部署Redisson配置
* 连接方式: 主节点,子节点,子节点
* 格式为: 127.0.0.1:6379,127.0.0.1:6380,127.0.0.1:6381
* @author xub
* @date 2019/6/19 下午9:21
*/
@Slf4j
public class MasterslaveConfigImpl implements RedissonConfigService {
@Override
public Config createRedissonConfig(RedissonProperties redissonProperties) {
Config config = new Config();
try {
String address = redissonProperties.getAddress();
String password = redissonProperties.getPassword();
int database = redissonProperties.getDatabase();
String[] addrTokens = address.split(",");
String masterNodeAddr = addrTokens[0];
//设置主节点ip
config.useMasterSlaveServers().setMasterAddress(masterNodeAddr);
if (StringUtil.isNotBlank(password)) {
config.useMasterSlaveServers().setPassword(password);
}
config.useMasterSlaveServers().setDatabase(database);
//设置从节点,移除第一个节点,默认第一个为主节点
List<String> slaveList = new ArrayList<>();
for (String addrToken : addrTokens) {
slaveList.add(GlobalConstant.REDIS_CONNECTION_PREFIX.getConstant_value() + addrToken);
}
slaveList.remove(0);
config.useMasterSlaveServers().addSlaveAddress((String[]) slaveList.toArray());
log.info("初始化[主从部署]方式Config,redisAddress:" + address);
} catch (Exception e) {
log.error("主从部署 Redisson init error", e);
e.printStackTrace();
}
return config;
}
}
定义SentineConfigImpl 哨兵集群
package com.cloud.based.redisson.strategy.impl;
import com.cloud.based.redisson.constant.GlobalConstant;
import com.cloud.based.redisson.properties.RedissonProperties;
import com.cloud.based.redisson.strategy.RedissonConfigService;
import jodd.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.redisson.config.Config;
/**
* @Description: 哨兵集群部署Redis连接配置
*
* @author xub
* @date 2019/6/19 下午9:17
*/
@Slf4j
public class SentineConfigImpl implements RedissonConfigService {
@Override
public Config createRedissonConfig(RedissonProperties redissonProperties) {
Config config = new Config();
try {
String address = redissonProperties.getAddress();
String password = redissonProperties.getPassword();
int database = redissonProperties.getDatabase();
String[] addrTokens = address.split(",");
String sentinelAliasName = addrTokens[0];
//设置redis配置文件sentinel.conf配置的sentinel别名
config.useSentinelServers().setMasterName(sentinelAliasName);
config.useSentinelServers().setDatabase(database);
if (StringUtil.isNotBlank(password)) {
config.useSentinelServers().setPassword(password);
}
//设置sentinel节点的服务IP和端口
for (int i = 1; i < addrTokens.length; i++) {
config.useSentinelServers().addSentinelAddress(GlobalConstant.REDIS_CONNECTION_PREFIX.getConstant_value() + addrTokens[i]);
}
log.info("初始化[哨兵部署]方式Config,redisAddress:" + address);
} catch (Exception e) {
log.error("哨兵部署 Redisson init error", e);
}
return config;
}
}
定义StandaloneConfigImpl 单机部署
package com.cloud.based.redisson.strategy.impl;
import com.cloud.based.redisson.constant.GlobalConstant;
import com.cloud.based.redisson.properties.RedissonProperties;
import com.cloud.based.redisson.strategy.RedissonConfigService;
import jodd.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.redisson.config.Config;
/**
* @Description: 单机部署Redisson配置
*
* @author xub
* @date 2019/6/19 下午10:04
*/
@Slf4j
public class StandaloneConfigImpl implements RedissonConfigService {
@Override
public Config createRedissonConfig(RedissonProperties redissonProperties) {
Config config = new Config();
try {
String address = redissonProperties.getAddress();
String password = redissonProperties.getPassword();
int database = redissonProperties.getDatabase();
String redisAddr = GlobalConstant.REDIS_CONNECTION_PREFIX.getConstant_value() + address;
config.useSingleServer().setAddress(redisAddr);
config.useSingleServer().setDatabase(database);
//密码可以为空
if (StringUtil.isNotBlank(password)) {
config.useSingleServer().setPassword(password);
}
log.info("初始化[单机部署]方式Config,redisAddress:" + address);
} catch (Exception e) {
log.error("单机部署 Redisson init error", e);
}
return config;
}
}
定义RedissonAutoConfiguration
package com.cloud.based.redisson.config;
import com.cloud.based.redisson.RedissonLock;
import com.cloud.based.redisson.RedissonManager;
import com.cloud.based.redisson.properties.RedissonProperties;
import org.redisson.Redisson;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.core.annotation.Order;
/**
* @Description: Redisson自动化配置
*
* @author xub
* @date 2019/6/19 下午11:55
*/
@Configuration
@ConditionalOnClass(Redisson.class)
@EnableConfigurationProperties(RedissonProperties.class)
//@ComponentScan("com.jincou.redisson.annotation")
public class RedissonAutoConfiguration {
private static final Logger LOGGER = LoggerFactory.getLogger(RedissonAutoConfiguration.class);
@Bean
@ConditionalOnMissingBean
@Order(value = 2)
public RedissonLock redissonLock(RedissonManager redissonManager) {
RedissonLock redissonLock = new RedissonLock(redissonManager);
LOGGER.info("[RedissonLock]组装完毕");
return redissonLock;
}
@Bean
@ConditionalOnMissingBean
@Order(value = 1)
public RedissonManager redissonManager(RedissonProperties redissonProperties) {
RedissonManager redissonManager =
new RedissonManager(redissonProperties);
LOGGER.info("[RedissonManager]组装完毕,当前连接方式:" + redissonProperties.getType() +
",连接地址:" + redissonProperties.getAddress());
return redissonManager;
}
}
定义RedissonLock api
package com.cloud.based.redisson;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import java.util.concurrent.TimeUnit;
/**
* @Description: 针对源码Redisson进行一层封装
*
* @author xub
* @date 2019/6/19 下午10:26
*/
@Slf4j
public class RedissonLock {
private RedissonManager redissonManager;
private Redisson redisson;
public RedissonLock(RedissonManager redissonManager) {
this.redissonManager = redissonManager;
this.redisson = redissonManager.getRedisson();
}
public RedissonLock() {}
/**
* 加锁操作 (设置锁的有效时间)
* @param lockName 锁名称
* @param leaseTime 锁有效时间
*/
public void lock(String lockName, long leaseTime) {
RLock rLock = redisson.getLock(lockName);
rLock.lock(leaseTime,TimeUnit.SECONDS);
}
/**
* 加锁操作 (锁有效时间采用默认时间30秒)
* @param lockName 锁名称
*/
public void lock(String lockName) {
RLock rLock = redisson.getLock(lockName);
rLock.lock();
}
/**
* 加锁操作(tryLock锁,没有等待时间)
* @param lockName 锁名称
* @param leaseTime 锁有效时间
*/
public boolean tryLock(String lockName, long leaseTime) {
RLock rLock = redisson.getLock(lockName);
boolean getLock = false;
try {
getLock = rLock.tryLock( leaseTime, TimeUnit.SECONDS);
} catch (InterruptedException e) {
log.error("获取Redisson分布式锁[异常],lockName=" + lockName, e);
e.printStackTrace();
return false;
}
return getLock;
}
/**
* 加锁操作(tryLock锁,有等待时间)
* @param lockName 锁名称
* @param leaseTime 锁有效时间
* @param waitTime 等待时间
*/
public boolean tryLock(String lockName, long leaseTime,long waitTime) {
RLock rLock = redisson.getLock(lockName);
boolean getLock = false;
try {
getLock = rLock.tryLock( waitTime,leaseTime, TimeUnit.SECONDS);
} catch (InterruptedException e) {
log.error("获取Redisson分布式锁[异常],lockName=" + lockName, e);
e.printStackTrace();
return false;
}
return getLock;
}
/**
* 解锁
* @param lockName 锁名称
*/
public void unlock(String lockName) {
redisson.getLock(lockName).unlock();
}
/**
* 判断该锁是否已经被线程持有
* @param lockName 锁名称
*/
public boolean isLock(String lockName) {
RLock rLock = redisson.getLock(lockName);
return rLock.isLocked();
}
/**
* 判断该线程是否持有当前锁
* @param lockName 锁名称
*/
public boolean isHeldByCurrentThread(String lockName) {
RLock rLock = redisson.getLock(lockName);
return rLock.isHeldByCurrentThread();
}
public RedissonManager getRedissonManager() {
return redissonManager;
}
public void setRedissonManager(RedissonManager redissonManager) {
this.redissonManager = redissonManager;
}
}
自定义注解DistributedLock
package com.cloud.based.redisson.annotation;
import java.lang.annotation.*;
/**
* @Description: 基于注解的分布式式锁
*
* @author xub
* @date 2019/6/19 下午9:22
*/
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DistributedLock {
/**
* 锁的名称
*/
String value() default "redisson";
/**
* 锁的有效时间
*/
int leaseTime() default 10;
/**
* 锁的等待时间
* @return
*/
int waitTime() default 0;
}
自定义aop DistributedLockHandler
package com.cloud.based.redisson.annotation;
import com.cloud.based.redisson.RedissonLock;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @Description: Redisson分布式锁注解解析器
*
* @author xub
* @date 2019/6/20 下午9:34
*/
@Aspect
@Component
@Slf4j
public class DistributedLockHandler {
@Autowired
RedissonLock redissonLock;
@Around("@annotation(distributedLock)")
public void around(ProceedingJoinPoint joinPoint, DistributedLock distributedLock) {
log.info("[开始]执行RedisLock环绕通知,获取Redis分布式锁开始");
//获取锁名称
String lockName = distributedLock.value();
//获取超时时间,默认10秒
int leaseTime = distributedLock.leaseTime();
int waitTime = distributedLock.waitTime();
if(0==waitTime){
//如果没有设置等待时间,则直接加锁
redissonLock.lock(lockName, leaseTime);
}else {
//加锁并设置锁的最大等待时间
redissonLock.tryLock(lockName, leaseTime,waitTime);
}
try {
log.info("获取Redis分布式锁[成功],加锁完成,开始执行业务逻辑...");
joinPoint.proceed();
} catch (Throwable throwable) {
log.error("获取Redis分布式锁[异常],加锁失败", throwable);
throwable.printStackTrace();
} finally {
//如果该线程还持有该锁,那么释放该锁。如果该线程不持有该锁,说明该线程的锁已到过期时间,自动释放锁
if (redissonLock.isHeldByCurrentThread(lockName)) {
redissonLock.unlock(lockName);
}
}
log.info("释放Redis分布式锁[成功],解锁完成,结束业务逻辑...");
}
}
在resources->META-INF新增spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.cloud.based.redisson.config.RedissonAutoConfiguration
在需要的服务yml中新增
redisson:
lock:
server:
address: xxx:xxx
type: standalone
password: xxxxx
database: 1
代码中直接添加注解使用
package com.cloud.based.controller;
import com.cloud.based.redisson.RedissonLock;
import com.cloud.based.redisson.annotation.DistributedLock;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author xub
* @Description: 基于注解的方式 加锁
* @date 2019/6/19 下午11:01
*/
@RestController
@Slf4j
@Api(description = "redisson注解管理", tags = "AnnotatinLockController")
@AllArgsConstructor
public class AnnotatinLockController {
private final RedissonLock redissonLock;
/**
* 模拟这个是商品库存
*/
public static volatile Integer TOTAL = 10;
@ApiOperation("注释性锁定减少库存")
@GetMapping("annotatin-lock-decrease-stock")
@DistributedLock(value="goods", leaseTime=5)
public String lockDecreaseStock() throws InterruptedException {
if (TOTAL > 0) {
TOTAL--;
}
log.info("===注解模式=== 减完库存后,当前库存===" + TOTAL);
return "=================================";
}
}
大功告成
参考代码:
https://gitee.com/youzhibing/qsl-project/tree/master/redisson-spring-boot-demo
https://gitee.com/yangxixi_323/spring-boot-distributed-redisson?_from=gitee_search
参考博客:
https://juejin.cn/post/7394094789270372403
https://www.cnblogs.com/qdhxhz/p/11059200.html
相关推荐
- Jquery 详细用法
-
1、jQuery介绍(1)jQuery是什么?是一个js框架,其主要思想是利用jQuery提供的选择器查找要操作的节点,然后将找到的节点封装成一个jQuery对象。封装成jQuery对象的目的有...
- 前端开发79条知识点汇总
-
1.css禁用鼠标事件2.get/post的理解和他们之间的区别http超文本传输协议(HTTP)的设计目的是保证客户机与服务器之间的通信。HTTP的工作方式是客户机与服务器之间的请求-应答协议。...
- js基础面试题92-130道题目
-
92.说说你对作用域链的理解参考答案:作用域链的作用是保证执行环境里有权访问的变量和函数是有序的,作用域链的变量只能向上访问,变量访问到window对象即被终止,作用域链向下访问变量是不被允许的。...
- Web前端必备基础知识点,百万网友:牛逼
-
1、Web中的常见攻击方式1.SQL注入------常见的安全性问题。解决方案:前端页面需要校验用户的输入数据(限制用户输入的类型、范围、格式、长度),不能只靠后端去校验用户数据。一来可以提高后端处理...
- 事件——《JS高级程序设计》
-
一、事件流1.事件流描述的是从页面中接收事件的顺序2.事件冒泡(eventbubble):事件从开始时由最具体的元素(就是嵌套最深的那个节点)开始,逐级向上传播到较为不具体的节点(就是Docu...
- 前端开发中79条不可忽视的知识点汇总
-
过往一些不足的地方,通过博客,好好总结一下。1.css禁用鼠标事件...
- Chrome 开发工具之Network
-
经常会听到比如"为什么我的js代码没执行啊?","我明明发送了请求,为什么反应?","我这个网站怎么加载的这么慢?"这类的问题,那么问题既然存在,就需要去解决它,需要解决它,首先我们得找对导致问题的原...
- 轻量级 React.js 虚拟美化滚动条组件RScroll
-
前几天有给大家分享一个Vue自定义滚动条组件VScroll。今天再分享一个最新开发的ReactPC端模拟滚动条组件RScroll。...
- 一文解读JavaScript事件对象和表单对象
-
前言相信做网站对JavaScript再熟悉不过了,它是一门脚本语言,不同于Python的是,它是一门浏览器脚本语言,而Python则是服务器脚本语言,我们不光要会Python,还要会JavaScrip...
- Python函数参数黑科技:*args与**kwargs深度解析
-
90%的Python程序员不知道,可变参数设计竟能决定函数的灵活性和扩展性!掌握这些技巧,让你的函数适应任何场景!一、函数参数设计的三大进阶技巧...
- 深入理解Python3密码学:详解PyCrypto库加密、解密与数字签名
-
在现代计算领域,信息安全逐渐成为焦点话题。密码学,作为信息保护的关键技术之一,允许我们加密(保密)和解密(解密)数据。...
- 阿里Nacos惊爆安全漏洞,火速升级!(附修复建议)
-
前言好,我是threedr3am,我发现nacos最新版本1.4.1对于User-Agent绕过安全漏洞的serverIdentitykey-value修复机制,依然存在绕过问题,在nacos开启了...
- Python模块:zoneinfo时区支持详解
-
一、知识导图二、知识讲解(一)zoneinfo模块概述...
- Golang开发的一些注意事项(一)
-
1.channel关闭后读的问题当channel关闭之后再去读取它,虽然不会引发panic,但会直接得到零值,而且ok的值为false。packagemainimport"...
- Python鼠标与键盘自动化指南:从入门到进阶——键盘篇
-
`pynput`是一个用于控制和监控鼠标和键盘的Python库...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- idea eval reset (50)
- vue dispatch (70)
- update canceled (42)
- order by asc (53)
- spring gateway (67)
- 简单代码编程 贪吃蛇 (40)
- transforms.resize (33)
- redisson trylock (35)
- 卸载node (35)
- np.reshape (33)
- torch.arange (34)
- npm 源 (35)
- vue3 deep (35)
- win10 ssh (35)
- vue foreach (34)
- idea设置编码为utf8 (35)
- vue 数组添加元素 (34)
- std find (34)
- tablefield注解用途 (35)
- python str转json (34)
- java websocket客户端 (34)
- tensor.view (34)
- java jackson (34)
- vmware17pro最新密钥 (34)
- mysql单表最大数据量 (35)