本文正在参加「金石计划」
前言
日常开发中,难免遇到一些并发的场景,为了确保接口履行的一致性,通常采用加锁的办法,因为服务是分布式部署形式,本地锁Reentrantlock和Synchnorized这些就先放到一边了,Redis的setnx锁存在无法抱确保原子性的问题就暂时搁且到一边,直接上大招Ression也是我最近开发项目中根本都在用的缓存,并且也都是用它的分布式锁机制。
Redisson分布式锁惯例运用
关于Redisson的一些根本概念,本章就不做太详细的说明了,有爱好的小伙伴能够自己去了解下,首要说下加锁的惯例运用,Redisson分布式锁是基于Redis的Rlock锁,完成了JavaJUC包下的Lock接口。
Lock
public void getLock(){
//获取锁
RLock lock = redisson.getLock("Lxlxxx_Lock");
try {
// 2.加锁
lock.lock();
} catch (InterruptedException e) {
e.getStackTrace();
} finally {
// 3.解锁
lock.unlock();
System.out.println("Finally,开释锁成功");
}
getLock获取锁,lock.lock进行加锁,会呈现的问题便是lock拿不到锁一向等候,会进入阻塞状况,明显这样是不好的。
TryLock
返回boolean类型,和Reentrantlock的tryLock是一个意思,测验获取锁,获取到就返回true,获取失利就返回false,不会使获不到锁的线程一向处于等候状况,返回false能够继续履行下面的业务逻辑,当然Ression锁内部也涉及到watchDog看门狗机制,首要作用便是给快过期的锁进行续期,首要用途便是使拿到锁的有限时间让业务履行完,再进行锁开释。
RLock lock = redisson.getLock(name);
try {
if (lock.tryLock(2, 10, TimeUnit.SECONDS)) {
//履行业务逻辑
} else {
System.out.println("已存在");
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//判别当时线程持有的锁是不是处于确定状况,确定状况再进行开释
if (this.redissonLock.isHeldByCurrentThread(lockName)) {
this.redissonLock.unlock(lockName);
}
}
自定义注解完成锁机制
通常咱们都会将redisson实例注入到办法类里面,然后调用加锁办法进行加锁,假如其他业务办法也需求加锁履行,将会发生很多重复代码,由此采用AOP切面的办法,只需求经过注解的办法就能将办法进行加锁处理。
自定义注解
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface DistributedLock {
String key() default "";
int leaseTime() default 10;
boolean autoRelease() default true;
String errorDesc() default "体系正常处理,请稍后提交";
int waitTime() default 1;
}
切面类完成
@Aspect
@Component
public class DistributedLockHandler {
private static final Logger log = LoggerFactory.getLogger(DistributedLockHandler.class);
@Autowired
RedissonLock redissonLock;
public DistributedLockHandler() {
}
@Around("@annotation(distributedLock)")
public Object around(ProceedingJoinPoint joinPoint, DistributedLock distributedLock) throws Throwable {
String lockName = this.getRedisKey(joinPoint, distributedLock);
int leaseTime = distributedLock.leaseTime();
String errorDesc = distributedLock.errorDesc();
int waitTime = distributedLock.waitTime();
Object var8;
try {
boolean lock = this.redissonLock.tryLock(lockName, (long)leaseTime, (long)waitTime);
if (!lock) {
throw new RuntimeException(errorDesc);
}
var8 = joinPoint.proceed();
} catch (Throwable var12) {
log.error("履行业务办法反常", var12);
throw var12;
} finally {
if (this.redissonLock.isHeldByCurrentThread(lockName)) {
this.redissonLock.unlock(lockName);
}
}
return var8;
}
/**
* 获取加锁的key
* @param joinPoint
* @param distributedLock
* @return
*/
private String getRedisKey(ProceedingJoinPoint joinPoint, DistributedLock distributedLock) {
String key = distributedLock.key();
Object[] parameterValues = joinPoint.getArgs();
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
Method method = signature.getMethod();
DefaultParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer();
String[] parameterNames = nameDiscoverer.getParameterNames(method);
if (StringUtils.isEmpty(key)) {
if (parameterNames != null && parameterNames.length > 0) {
StringBuffer sb = new StringBuffer();
int i = 0;
for(int len = parameterNames.length; i < len; ++i) {
sb.append(parameterNames[i]).append(" = ").append(parameterValues[i]);
}
key = sb.toString();
} else {
key = "redissionLock";
}
return key;
} else {
SpelExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression(key);
if (parameterNames != null && parameterNames.length != 0) {
EvaluationContext evaluationContext = new StandardEvaluationContext();
for(int i = 0; i < parameterNames.length; ++i) {
evaluationContext.setVariable(parameterNames[i], parameterValues[i]);
}
try {
Object expressionValue = expression.getValue(evaluationContext);
return expressionValue != null && !"".equals(expressionValue.toString()) ? expressionValue.toString() : key;
} catch (Exception var13) {
return key;
}
} else {
return key;
}
}
}
}
具体运用
办法头加自定义注解,key参数代表需求加锁的key,errorDesc获取锁失利提示报错信息。
这边我将项目经过修改端口启动了两个服务,分别是8460和8461
经过postman调用这两个服务,模拟两个服务同时获取一把锁的场景,其中一个服务拿到锁,别的一个服务获取锁失利。
能够看到端口8460服务先拿到锁,8461服务tryLock获取锁失利,完成了加锁逻辑。
总结
分布式锁的运用场景还是需求多注意下,根据业务场景来,并发量不大的情况下,其实没有必要加,可能在移动端操作比较频繁的情况下需求注意并发,现在我做的b端项目,经过简单接口幂等性操作就能够防止重复提交,切勿不要盲目加锁,多少会影响一些功能。