当前位置: 首页 > news >正文

注解方式优雅的实现 Redisson 分布式锁

1前言

日常开发中,难免遇到一些并发的场景,为了保证接口执行的一致性,通常采用加锁的方式,因为服务是分布式部署模式,本地锁Reentrantlock和Synchnorized这些就先放到一边了,Redis的setnx锁存在无法抱保证原子性的问题就暂时搁且到一边,直接上大招Redisson也是我最近开发项目中基本都在用的缓存,并且也都是用它的分布式锁机制。

2Redisson分布式锁常规使用

关于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


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);}
}

3自定义注解实现锁机制

通常我们都会将redisson实例注入到方法类里面,然后调用加锁方法进行加锁,如果其他业务方法也需要加锁执行,将会产生很多重复代码,由此采用AOP切面的方式,只需要通过注解的方式就能将方法进行加锁处理。另外,搜索公众号Linux就该这样学后台回复“猴子”,获取一份惊喜礼包。

自定义注解

@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);@AutowiredRedissonLock 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获取锁失败,实现了加锁逻辑

分布式锁的使用场景还是需要多注意下,根据业务场景来,并发量不大的情况下,其实没有

http://www.lryc.cn/news/237592.html

相关文章:

  • PHP/Laravel通过经纬度计算距离获取附近商家
  • grafana面板介绍
  • 实验三 循环结构程序设计(Python)
  • Flutter笔记:目录与文件存储以及在Flutter中的使用(上)
  • 注意了!申请流量卡时地址一定不要填写学校,不好下卡哦!
  • minio使用shell上传文件
  • LeetCode538. Convert BST to Greater Tree
  • iPaaS和RPA,企业自动化应该如何选择?
  • AI实践与学习1_Milvus向量数据库实践与原理分析
  • 3Dexcite deltgen 2022x 新功能
  • 代码随想录算法训练营第六十天 | LeetCode 84. 柱状图中最大的矩形
  • 【2023云栖】陈守元:阿里云开源大数据产品年度发布
  • Element UI 禁用数字输入框组件添加鼠标滚动事件
  • 担忧CentOS停服?KeyarchOS系统来支撑
  • 聚观早报 |联想集团Q2财季业绩;小鹏汽车Q3营收
  • SAP ABAP权限控制中常用TCODE
  • 云计算赛项容器云2023搭建
  • 11.1 文件拷贝移动与删除
  • redhat下使用CentOS yum源,并安装docker
  • 基于单片机体温脉搏检测控制系统及源程序
  • MyBatis-Plus逻辑删@TableLogic
  • 本地私域线上线下 线上和线下的小程序
  • 【前端学java】java中的Object类(8)
  • TensorFlow实战教程(二十六)-什么是生成对抗网络GAN?基础原理和代码普及
  • IDEA版SSM入门到实战(Maven+MyBatis+Spring+SpringMVC) -Maven依赖管理,版本号管理,继承和聚合
  • OpenVPN Connect使用连接公网VPN服务器实现内网穿透
  • Redis(集合Set和有序集合SortedSet)
  • 黔院长 | 《黄帝内经》——奇病论!
  • 手撕单链表(C语言)
  • 60 权限提升-MYMSORA等SQL数据库提权