-
说明
-
配置方式
-
使用
-
优化
-
实现类总览
-
分布式锁抽象接口
-
添加分布式锁注解
-
抽象分布式锁接口
-
定义切面
-
测试
-
controller
说明
redisson不用多说,都知道是解决redis分布式锁的一个jar。
配置方式
redisson配置方法有两种:
-
基于JavaBean配置
Config config = new Config();
config.setTransportMode(TransportMode.EPOLL);
config.useClusterServers()
// use "rediss://" for SSL connection
.addNodeAddress("perredis://127.0.0.1:7181");
RedissonClient redisson = Redisson.create(config);
-
基于YAML配置
Yaml 也有如下两种方式
Config config = new Config();
// ... many settings are set here
String yamlFormat = config.toYAML();
// 或者使用如下格式
Config config = Config.fromYAML(new File("config-file.yaml"));
RedissonClient redisson = Redisson.create(config);
yaml配置文件配置如下:
singleServerConfig:
address: "redis://127.0.0.1:${REDIS_PORT:-6379}"
这是基础的配置,一般使用这些基础配置就可以简单的使用了,当然还有一些其他配置项目如下:
看源码看到还是有很多其他配置项的,看源码和注释大概都能知道这些配置都是干嘛的,这里就不作过多的解释,如果看源码不懂还可以去看官方文档,这里给出官方文档地址:https://github.com/redisson/redisson/wiki/2.-Configuration
这里我使用的是单机的配置,因为现在大多的是用的云redis。所以在我们使用就是单机的,但是云产商是给我们做了高可用的,所以我们不需要使用集群配置,具体集群配置可以看官网wiki
使用
我这里使用的springboot,配置方式和上面没什么差别
@Configuration
public class RedissonConfig {
@Value("${redis.host}")
private String redisLoginHost;
@Value("${redis.port}")
private Integer redisLoginPort;
@Value("${redis.password}")
private String redisLoginPassword;
@Bean
public RedissonClient redissonClient() {
return createRedis(redisLoginHost, redisLoginPort, redisLoginPassword);
}
private RedissonClient createRedis(String redisHost, Integer redisPort, String redisPassword) {
Config config = new Config();
SingleServerConfig singleServerConfig = config.useSingleServer();
singleServerConfig.setAddress("redis://" + redisHost + ":" + redisPort + "");
if (DataUtils.isNotEmpty(redisPassword)) {
singleServerConfig.setPassword(redisPassword);
}
config.setCodec(FastjsonCodec.INSTANCE);
return Redisson.create(config);
}
}
配置完了使用就很简单了,一般是这种方式
@Autowired
RedissonClient redissonClient;
public void test(){
RLock lock = redissonClient.getLock("order:1");
lock.lock(10, TimeUnit.SECONDS);
try {
} finally {
lock.unlock();
}
}
这是最简单的使用,可以看到每次使用还是比较麻烦的。我们就基于Spring AOP做一个优化,加一个注解就能实现分布式锁。
优化
实现类总览
分布式锁抽象接口
-
DistributedLock.java
public interface DistributedLock<T> {
/**
* 分布式锁逻辑 代码块
*/
T process();
/**
* 分布式锁名
*/
String lockName();
}
这是后面再aop使用的,抽象出了 分布式锁代码块和分布式锁名,面向接口编程
添加分布式锁注解
-
CiderDistributedLock.java
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CiderDistributedLock {
/**
* 锁名字
*/
String lockName() default "";
/**
* 锁前缀
*/
String lockNamePre() default "";
/**
* 锁后缀
*/
String lockNamePost() default "";
/**
* 锁前后缀拼接分隔符
*/
String separator() default "_";
/**
* 是否使用公平锁
*/
boolean fairLock() default false;
/**
* 是否使用尝试锁
*/
boolean tryLock() default false;
/**
* 尝试锁最长等待时间
*/
long tryWaitTime() default 30L;
/**
* 锁超时时间,超时自动释放锁
*/
long outTime() default 20L;
/**
* 时间单位 默认秒
*/
TimeUnit timeUnit() default TimeUnit.SECONDS;
}
抽象分布式锁接口
-
DistributedLockTemplate.java
public interface DistributedLockTemplate {
/**
* 默认超时锁释放时间
*/
long DEFAULT_OUT_TIME = 5;
/**
* 默认尝试加锁时间
*/
long DEFAULT_TRY_OUT_TIME = 30;
/**
* 默认时间单位
*/
TimeUnit DEFAULT_TIME_UNIT = TimeUnit.SECONDS;
/**
* 加锁
* @param distributedLock
* @param fairLock 是否使用公平锁
* @param <T>
* @return
*/
<T> T lock(DistributedLock<T> distributedLock, boolean fairLock);
/**
*
* @param distributedLock
* @param outTime 锁超时时间。超时后自动释放锁
* @param timeUnit 时间单位
* @param fairLock 是否使用公平锁
* @param <T>
* @return
*/
<T> T lock(DistributedLock<T> distributedLock, long outTime, TimeUnit timeUnit, boolean fairLock);
/**
* 尝试加锁
* @param distributedLock
* @param fairLock 是否使用公平锁
* @param <T>
* @return
*/
<T> T tryLock(DistributedLock<T> distributedLock, boolean fairLock);
/**
*
* @param distributedLock
* @param tryOutTime 尝试获取锁时间
* @param outTime 锁超时时间
* @param timeUnit 时间单位
* @param fairLock 是否使用公平锁
* @param <T>
* @return
*/
<T> T tryLock(DistributedLock<T> distributedLock, long tryOutTime, long outTime, TimeUnit timeUnit, boolean fairLock);
}
这里有了分布式锁接口,后续可能有其他实现方式,都是基于这个接口来的,我们这次就直说先实现一个单机版本的分布式锁实现类
-
SingleDistributedLockTemplate.java
@Service
public class SingleDistributedLockTemplate implements DistributedLockTemplate{
@Autowired
private RedissonClient redisson;
@Override
public <T> T lock(DistributedLock<T> distributedLock, boolean fairLock) {
return lock(distributedLock, DEFAULT_OUT_TIME, DEFAULT_TIME_UNIT, fairLock);
}
@Override
public <T> T lock(DistributedLock<T> distributedLock, long outTime, TimeUnit timeUnit, boolean fairLock) {
RLock lock = getLock(distributedLock.lockName(), fairLock);
try {
lock.lock(outTime, timeUnit);
return distributedLock.process();
} finally {
if (lock != null && lock.isLocked()) {
lock.unlock();
}
}
}
@Override
public <T> T tryLock(DistributedLock<T> distributedLock, boolean fairLock) {
return tryLock(distributedLock, DEFAULT_TRY_OUT_TIME, DEFAULT_OUT_TIME, DEFAULT_TIME_UNIT, fairLock);
}
@Override
public <T> T tryLock(DistributedLock<T> distributedLock, long tryOutTime, long outTime, TimeUnit timeUnit, boolean fairLock) {
RLock lock = getLock(distributedLock.lockName(), fairLock);
try {
if (lock.tryLock(tryOutTime, outTime, timeUnit)) {
return distributedLock.process();
}
} catch (InterruptedException ignored) {
} finally {
if (lock != null && lock.isLocked()) {
lock.unlock();
}
}
return null;
}
private RLock getLock(String lockName, boolean fairLock) {
return fairLock ? redisson.getFairLock(lockName) : redisson.getLock(lockName);
}
}
定义切面
-
CiderDistributedLockAspect.java
package com.shopcider.plutus.component.aop;
import com.shopcider.plutus.component.annotation.CiderDistributedLock;
import com.shopcider.plutus.component.lock.DistributedLock;
import com.shopcider.plutus.component.lock.DistributedLockTemplate;
import com.shopcider.plutus.component.util.DataUtils;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.Objects;
/**
* @author : wh
* @date : 2021/7/18 12:52
* @description: 分布式锁切面
*/
@Aspect
@Component
public class CiderDistributedLockAspect {
@Autowired
private DistributedLockTemplate lock;
@Pointcut("@annotation(com.shopcider.plutus.component.annotation.CiderDistributedLock)")
public void ciderDistributedLockAspect() {
}
@Around(value = "ciderDistributedLockAspect()")
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
//切点所在的类
MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
Method method = methodSignature.getMethod();
//方法参数
Object[] args = pjp.getArgs();
CiderDistributedLock annotation = method.getAnnotation(CiderDistributedLock.class);
String lockName = getLockName(annotation, args, method);
boolean fairLock = annotation.fairLock();
return lock.tryLock(new DistributedLock<Object>() {
@Override
public Object process() {
return proceed(pjp);
}
@Override
public String lockName() {
return lockName;
}
}, annotation.outTime(), annotation.tryWaitTime(), annotation.timeUnit(), fairLock);
}
public Object proceed(ProceedingJoinPoint pjp) {
try {
return pjp.proceed();
} catch (Throwable throwable) {
throw new RuntimeException(throwable);
}
}
/**
* 获取锁名字,优先获取注解中锁名
*
* @param ciderDistributedLock
* @param args
* @param method
* @return
*/
private String getLockName(CiderDistributedLock ciderDistributedLock, Object[] args, Method method) {
if (DataUtils.isNotEmpty(ciderDistributedLock.lockName())) {
return ciderDistributedLock.lockName();
}
String lockNamePre = ciderDistributedLock.lockNamePre();
String lockNamePost = ciderDistributedLock.lockNamePost();
String separator = ciderDistributedLock.separator();
String preExpression = parseExpression(lockNamePre, method, args);
String postExpression = parseExpression(lockNamePost, method, args);
StringBuilder sb = new StringBuilder();
if (DataUtils.isNotEmpty(preExpression)) {
sb.append(preExpression);
} else {
sb.append(lockNamePre);
}
sb.append(separator);
if (DataUtils.isNotEmpty(postExpression)) {
sb.append(postExpression);
} else {
sb.append(lockNamePost);
}
return sb.toString();
}
/**
* el表达式解析
*
* @param expressionString 解析值
* @param method 方法
* @param args 参数
* @return
*/
private String parseExpression(String expressionString, Method method, Object[] args) {
//获取被拦截方法参数名列表
LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
String[] paramNameArr = discoverer.getParameterNames(method);
//SPEL解析
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
for (int i = 0; i < Objects.requireNonNull(paramNameArr).length; i++) {
context.setVariable(paramNameArr[i], args[i]);
}
return parser.parseExpression(expressionString).getValue(context, String.class);
}
}
测试
controller
public void getList(WarehouseReturnDTO warehouseReturnDTO) {
return warehouseQueryApplicationService.getReturnList(warehouseReturnDTO);
}
private int i = 10;
@CiderDistributedLock(lockNamePre = "#query.userName")
public void getReturnList(WarehouseReturnDTO query) {
i = i + 1;
System.out.println(i);
}
这里测试方法很简单就是定义一个全局变量i,没有使用数据库,然后就对i进行累加(这里只是作了单机的测试)
然后我们使用jmeter设置30个线程进行并发调用测试(jmeter教程)
看看没加@CiderDistributedLock注解前的结果:
加了注解后的结果:
后续需要源码可以留言上传github参考
原文始发于微信公众号(小奏技术):springboot之Redisson使用及使用Aop优化注解实现
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/29730.html