首页 / REDIS / redis分布式锁的应用
redis分布式锁的应用
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了redis分布式锁的应用,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含8153字,纯文字阅读大概需要12分钟。
内容图文
![redis分布式锁的应用](/upload/InfoBanner/zyjiaocheng/868/d0bedde0246b43cb8cae84a3e9dfd8df.jpg)
前言:
项目需求,搞了搞
- 实现了锁的重入
- 参考了别人的博文实现了AOP注解形式的锁、统一配置
参考博文地址:
https://www.cnblogs.com/lijiasnong/p/9952494.html
这边看了下比较主流几个分布式锁的应用,最终选择的redis
原因是:
1、懒(服务器已有redis做缓存,不想再去安装zuukeeper)
2、评估认为redis的分布式锁已能满足当下应用
正文 - 摘录核心代码:
- RedisReentrantLock
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* redis分布式锁 - 使用 ThreadLocal 做线程隔离,实现锁的重入机制
* @date 2020/12/11 9:13
* @author wei.heng
*/
@Component
public class RedisReentrantLock {
/** 锁的失效时间 */
private static final Integer EXPIRE_SECONDS = 20;
/** 锁的前缀,方便统一查看 */
private static final String LOCK_PREFIX = "LOCK:";
private ThreadLocal<Map<String, Integer>> lockers = new ThreadLocal<>();
private RedisService redisService;
@Autowired
public RedisReentrantLock(RedisService redisService) {
this.redisService = redisService;
}
/**
*
* 添加分布式锁
* @param key 键
* @return boolean 请求锁是否成功
* @date 2020/12/11 10:52
* @author wei.heng
*/
public boolean lock(String key){
key = LOCK_PREFIX + key;
Map<String, Integer> refs = currentLockers();
Integer refCnt = refs.get(key);
if(refCnt != null){
// 当前线程已经加锁,这里属于锁的重入,计数器加1
refs.put(key, refCnt + 1);
return true;
}
boolean ok = this._lock(key);
if(!ok){
return false;
}
refs.put(key, 1);
return true;
}
/**
*
* 释放锁
* @param key 键
* @return boolean 释放是否成功
* @date 2020/12/11 10:53
* @author wei.heng
*/
public boolean unlock(String key){
key = LOCK_PREFIX + key;
Map<String, Integer> refs = currentLockers();
Integer refCnt = refs.get(key);
if(refCnt == null){
return false;
}
refCnt -= 1;
if(refCnt > 0){
refs.put(key, refCnt);
} else {
refs.remove(key);
this._unlock(key);
}
return true;
}
private boolean _lock(String key){
return redisService.setIfAbsent(key, "", EXPIRE_SECONDS);
}
private void _unlock(String key){
redisService.deleteObject(key);
}
private Map<String, Integer> currentLockers(){
Map<String, Integer> refs = lockers.get();
if(refs != null){
return refs;
}
lockers.set(new HashMap<>());
return lockers.get();
}
}
- RedisService 核心代码
/**
*
* 如果key不存在,则设值
* @param key 键
* @param value 值
* @param exptime 过期时间 - 单位:秒
* @return boolean 设值是否成功(如果已有该key存在,则返回false)
* @date 2020/12/11 10:42
* @author wei.heng
*/
public boolean setIfAbsent(final String key, final Serializable value, final long exptime) {
Boolean result = (Boolean) redisTemplate.execute((RedisCallback<Boolean>) connection -> {
RedisSerializer valueSerializer = redisTemplate.getValueSerializer();
RedisSerializer keySerializer = redisTemplate.getKeySerializer();
Object obj = connection.execute("set", keySerializer.serialize(key),
valueSerializer.serialize(value),
"NX".getBytes(StandardCharsets.UTF_8),
"EX".getBytes(StandardCharsets.UTF_8),
String.valueOf(exptime).getBytes(StandardCharsets.UTF_8));
return obj != null;
});
return result;
}
到这里,redis的分布式锁已经可以使用了。
看到网上别人的帖子,为了方便,
下面继续做了下抄袭、修改
再次重申,抄袭的地址:
https://www.cnblogs.com/lijiasnong/p/9952494.html
- RedisLockable 新建注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* redis分布式锁
* @date 2020/12/11 11:48
* @author wei.heng
*/
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface RedisLockable {
/** redis锁的关键字(keys),可使用SpEL表达式代表参数值 */
String[] value() default "";
}
- ReflectParamNames
这个类需要引入jar包,我这里搜了个最新的
<dependency>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.12.1.GA</version>
</dependency>
import javassist.*;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 反射获取参数名
* @date 2020/12/11 14:03
* @author wei.heng
*/
public class ReflectParamNames {
private static Logger log = LoggerFactory.getLogger(ReflectParamNames.class);
private static ClassPool pool = ClassPool.getDefault();
static{
ClassClassPath classPath = new ClassClassPath(ReflectParamNames.class);
pool.insertClassPath(classPath);
}
public static String[] getNames(String className,String methodName) {
CtClass cc = null;
try {
cc = pool.get(className);
CtMethod cm = cc.getDeclaredMethod(methodName);
// 使用javaassist的反射方法获取方法的参数名
MethodInfo methodInfo = cm.getMethodInfo();
CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
if (attr == null) return new String[0];
int begin = 0;
String[] paramNames = new String[cm.getParameterTypes().length];
int count = 0;
int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
for (int i = 0; i < attr.tableLength(); i++){
// 为什么 加这个判断,发现在windows 跟linux执行时,参数顺序不一致,通过观察,实际的参数是从this后面开始的
if (attr.variableName(i).equals("this")){
begin = i;
break;
}
}
for (int i = begin+1; i <= begin+paramNames.length; i++){
paramNames[count] = attr.variableName(i);
count++;
}
return paramNames;
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
if(cc != null) cc.detach();
} catch (Exception e2) {
log.error(e2.getMessage());
}
}
return new String[0];
}
}
- RedisLockAspect
import com.google.common.base.Joiner;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
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;
/**
* 注解形式的redis分布式锁
* @date 2020/12/11 14:10
* @author wei.heng
*/
@Aspect
@Component
public class RedisLockAspect {
private RedisReentrantLock redisLock;
@Autowired
public RedisLockAspect(RedisReentrantLock redisLock) {
this.redisLock = redisLock;
}
@Around("@annotation(com.ruoyi.common.security.annotation.RedisLockable)")
public Object around(ProceedingJoinPoint point) throws Throwable {
Signature signature = point.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
String targetName = point.getTarget().getClass().getName();
String methodName = point.getSignature().getName();
Object[] arguments = point.getArgs();
RedisLockable redisLockable = method.getAnnotation(RedisLockable.class);
String redisKey = getLockKey(targetName, methodName, redisLockable.value(), arguments);
boolean isLock = redisLock.lock(redisKey);
if(isLock) {
try {
return point.proceed();
} finally {
redisLock.unlock(redisKey);
}
} else {
System.out.println("===未获取到锁:" + redisKey);
throw new GetRedisLockFailureException();
}
}
private String getLockKey(String targetName, String methodName, String[] keys, Object[] arguments) {
StringBuilder sb = new StringBuilder();
sb.append(targetName).append(".").append(methodName);
if(keys != null) {
String keyStr = Joiner.on(".").skipNulls().join(keys);
String[] parameters = ReflectParamNames.getNames(targetName, methodName);
ExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression(keyStr);
EvaluationContext context = new StandardEvaluationContext();
int length = parameters.length;
if (length > 0) {
for (int i = 0; i < length; i++) {
context.setVariable(parameters[i], arguments[i]);
}
}
String keysValue = expression.getValue(context, String.class);
sb.append("#").append(keysValue);
}
return sb.toString();
}
}
到这里全部结束,可以直接使用@RedisLockable 进行分布式锁的同步控制了(▽)
测试:
发出两个postman请求,参数相同,第一个OK,第二个返回409
修改其中某个参数、导致不是同一把锁,两个请求均通过(通过的场景就不截图了)
内容总结
以上是互联网集市为您收集整理的redis分布式锁的应用全部内容,希望文章能够帮你解决redis分布式锁的应用所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。