这里不介绍springboot集成和redis的配置集成只介绍redisson分布式锁
主要依赖
<!--redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--分布式锁redisson-->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.5.0</version>
</dependency>
<!--aop-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
redission配置文件
application-single-dev.yml
单机模式
singleServerConfig:
idleConnectionTimeout: 10000 #连接空闲超时(毫秒),默认10000
connectTimeout: 10000 #连接空闲超时(毫秒),默认10000
timeout: 3000 #命令等待超时(毫秒),默认3000
retryAttempts: 3 #命令失败重试次数
retryInterval: 1500 #命令重试发送时间间隔(毫秒),默认1500
password: null
subscriptionsPerConnection: 5 #单个连接最大订阅数量,默认5
clientName: null #客户端名称
address: "redis://127.0.0.1:6379"
subscriptionConnectionMinimumIdleSize: 1 #发布和订阅连接的最小空闲连接数,默认1
subscriptionConnectionPoolSize: 50 #发布和订阅连接池大小,默认50
connectionMinimumIdleSize: 24 #最小空闲连接数,默认32
connectionPoolSize: 64 #连接池大小,默认64
database: 0
dnsMonitoringInterval: 5000 #DNS监测时间间隔(毫秒),默认5000
threads: 16
nettyThreads: 32
codec: !<org.redisson.codec.JsonJacksonCodec> {}
#"transportMode": "NIO"
集群模式:
clusterServersConfig:
idleConnectionTimeout: 10000
pingTimeout: 1000
connectTimeout: 10000
timeout: 3000
retryAttempts: 3
retryInterval: 1500
reconnectionTimeout: 3000
failedAttempts: 3
password: null
subscriptionsPerConnection: 5
clientName: null
loadBalancer: !<org.redisson.connection.balancer.RoundRobinLoadBalancer> {}
slaveSubscriptionConnectionMinimumIdleSize: 1
slaveSubscriptionConnectionPoolSize: 50
slaveConnectionMinimumIdleSize: 32
slaveConnectionPoolSize: 64
masterConnectionMinimumIdleSize: 32
masterConnectionPoolSize: 64
readMode: "SLAVE"
nodeAddresses:
- "redis://127.0.0.1:7004"
- "redis://127.0.0.1:7001"
- "redis://127.0.0.1:7000"
scanInterval: 1000
threads: 0
nettyThreads: 0
codec: !<org.redisson.codec.JsonJacksonCodec> {}
#"transportMode":"NIO"
注入配置文件
这里读取配置文件根据环境读取的
RedissionConfig
package com.dzhjj.dzhjjapi.config;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.SingleServerConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.redisson.config.Config;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ClassPathResource;
import java.io.IOException;
/**
* @Auther: heng
* @Date: 2020/12/3 13:56
* @Description: redission分布式锁
* @Version 1.0.0
*/
@Order(2)
@Slf4j
@Configuration
public class RedissionConfig {
//@Autowired
// private RedisProperties redisProperties;
@Autowired
private Environment env;
/**
* 单机模式
* @return
*//*
@Bean
public RedissonClient redissonClient() {
RedissonClient redissonClient;
Config config = new Config();
String url = "redis://" + redisProperties.getHost() + ":" + redisProperties.getPort();
SingleServerConfig serverConfig = config.useSingleServer()
.setAddress(url)
.setTimeout(3000)
.setDatabase(redisProperties.getDatabase())
.setConnectionPoolSize(64)
.setConnectionMinimumIdleSize(50);
if (StringUtils.isNotBlank(redisProperties.getPassword())) {
serverConfig.setPassword(redisProperties.getPassword());
}
try {
redissonClient = Redisson.create(config);
return redissonClient;
} catch (Exception e) {
log.error("RedissonClient init redis url:[{}], Exception:", url, e);
return null;
}
}*/
@Bean(destroyMethod = "shutdown")
public RedissonClient redisson() throws IOException {
Config config = Config.fromYAML(new ClassPathResource("application-single-"+env.getActiveProfiles()[0]+".yml").getInputStream());
RedissonClient redisson = Redisson.create(config);
return redisson;
}
}
测试分布式锁代码
@Autowired
private RedissonClient redissonClient;
public Object testRedisLoak(){
Boolean result=false;
final String lockKey= "RedissonLock";
RLock lock=redissonClient.getLock(lockKey);
try {
//TODO:第一个参数30s=表示尝试获取分布式锁,并且最大的等待获取锁的时间为30s
//TODO:第二个参数10s=表示上锁之后,10s内操作完毕将自动释放锁
Boolean cacheRes=lock.tryLock(10,10, TimeUnit.SECONDS);
return cacheRes;
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//TODO:释放锁
lock.unlock();
}
return result;
}
开始集成注解模式
定义注解: RsionLock
import org.springframework.core.annotation.AliasFor;
import java.lang.annotation.*;
/**
* Description:
*
* <p>
* 分布式锁的注解
* </p>
* 在同一个注解中成对使用即可,比如示例代码中,value和path就是互为别名。
* 但是要注意一点,@AliasFor标签有一些使用限制,但是这应该能想到的,比如要求互为别名的属性属性值类型,默认值,都是相同的,互为别名的注解必须成对出现,比如value属性添加了@AliasFor(“path”),
* 那么path属性就必须添加@AliasFor(“value”),另外还有一点,互为别名的属性必须定义默认值。
*
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface RsionLock {
/**
* 支持spring El表达式
* 锁的资源,key。
*/
//@AliasFor("value")
String key() default "";
/**
* 支持spring El表达式
* 锁的资源,value。
* 如果不为空这回拼接key
*/
//@AliasFor("key")
String value() default "";
/**
* 持锁时间,单位:秒
* TODO:10s=表示上锁之后,10s内操作完毕将自动释放锁
*/
long lockTime() default 10;
/**
* 当获取失败时候动作
*/
// LockFailAction action() default LockFailAction.CONTINUE;
/*public enum LockFailAction{
*//** 放弃 *//*
GIVEUP,
*//** 继续 *//*
CONTINUE;
}*/
/**
* 等待时间,单位:秒
* TODO:10s=表示尝试获取分布式锁,并且最大的等待获取锁的时间为10s
* @return
*/
int waitTime() default 10;
/**
* 是否自动延期机制
* 默认不自动延期
* @return
*/
boolean isWatchDog() default false;
/**
* 延期时间 单位:秒
* @return
*/
int watchDogTime() default 2;
}
定义该注解切面 LockAspectConfiguration
import com.dzhjj.dzhjjapi.annotations.RsionLock;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;
/**
* @Auther: heng
* @Date: 2020/12/3 17:50
* @Description: LockAspectConfiguration
* @Version 1.0.0
*/
@Slf4j
@Aspect
@Configuration
public class LockAspectConfiguration {
private ExpressionParser parser = new SpelExpressionParser();
private LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
@Autowired
private RedissonClient redissonClient;
/**
* 定义切入点
*/
@Pointcut("@annotation(com.dzhjj.dzhjjapi.annotations.RsionLock)")
private void lockPoint() {
}
/**
* 环绕通知
*
* @param pjp pjp
* @return 方法返回结果
* @throws Throwable throwable
*/
@Around("lockPoint()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
Method method = ((MethodSignature) pjp.getSignature()).getMethod();
RsionLock lockAction = method.getAnnotation(RsionLock.class);
String logKey = getLogKey(lockAction, pjp, method);
RLock lock=redissonClient.getLock(logKey);
//TODO:第一个参数30s=表示尝试获取分布式锁,并且最大的等待获取锁的时间为30s
//TODO:第二个参数10s=表示上锁之后,10s内操作完毕将自动释放锁
Boolean cacheRes=lock.tryLock(lockAction.waitTime(),lockAction.lockTime(), TimeUnit.SECONDS);
if (!cacheRes) {
log.debug("get lock failed : " + logKey);
return null;
}
//得到锁,执行方法,释放锁
log.debug("get lock success : " + logKey);
try {
return pjp.proceed();
} catch (Exception e) {
log.error("execute locked method occured an exception", e);
} finally {
lock.unlock();
log.debug("release lock : " + logKey + (cacheRes ? " success" : " failed"));
}
return null;
}
/**
* 获得分布式缓存的key
*
* @param lockAction 注解对象
* @param pjp pjp
* @param method method
* @return String
*/
private String getLogKey(RsionLock lockAction, ProceedingJoinPoint pjp, Method method) {
String key = lockAction.key();
String value = lockAction.value();
Object[] args = pjp.getArgs();
return parse(key, method, args) + "_" + parse(value, method, args);
}
/**
* 解析spring EL表达式
*
* @param key key
* @param method method
* @param args args
* @return parse result
*/
private String parse(String key, Method method, Object[] args) {
String[] params = discoverer.getParameterNames(method);
if (null == params || params.length == 0 || !key.contains("#")) {
return key;
}
EvaluationContext context = new StandardEvaluationContext();
for (int i = 0; i < params.length; i++) {
if (null != args[i]){
context.setVariable(params[i], args[i]);
}else {
context.setVariable(params[i],"");
}
}
return parser.parseExpression(key).getValue(context, String.class);
}
}
测试代码 理应在service层测试的 我这就在controller层测试了
@RsionLock(key = "TestNewController_readlock", value = "#key",lockTime = 6,waitTime = 6)
@RequestMapping("/readlock")
public Object readlock(String key){
return key;
}
访问url
http://localhost:8010/dzhjjapi/testnew/readlock?key=test
ps: 访问之后测试锁
可以把时间加大一点把 释放锁注释掉
然后看看redis持锁状态和url访问拿锁状态 主要是一个是等待锁的时间一个是释放锁的时间
结果
版权声明:本文为qq_39313596原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。