前言
分布式锁想必大家并不生疏:管制分布式系统之间同步访问共享资源的一种形式。
如果不同的零碎或是同一个零碎的不同主机之间共享了一个或一组资源,那么拜访这些资源的时候,往往须要互斥来避免彼此烦扰来保障一致性,在这种状况下,便须要应用到分布式锁。
实现分布式锁的形式多种多样,但一般来说都是应用编码的形式在业务代码中交叉加锁解锁逻辑。
这种形式长处是非常模版化,简直不会出错,每一套加锁解锁逻辑都是截然不同。
毛病也是这个,虽说ctrl c, ctrl v很爽,但始终ctrl c, ctrl v也会让人感到痛苦。
所以,如何基于该思维实现一把注解版的分布式锁呢?
实现目标
业务举例:下单功能为避免用户反复下单,短时间内只容许用户一次点击胜利。
@RedisLock(name = "order", keys = {"#userId"})public String order(Long userId){ return "ok";}
如代码所示:只需在办法上加上注解,即可实现分布式锁。
name: 锁的名称
keys: 该业务唯一性资源标识,比方这里是短时间内某个用户只能下一次单,所以keys为用户id
name + keys 组合成redis中的key
设计
大多数由注解设计的性能都是通过切面实现的,这个也不例外。
最简略的实现形式就是将原始代码的加锁解锁逻辑拷贝到切面中实现。
如原始代码如下:
@Resourceprivate RedissonClient redissonClient;public String order(Long userId){ String key = "order" + userId; RLock lock = redissonClient.getLock(key); try { // 尝试加锁, true示意加锁胜利 if (lock.tryLock()) { // 业务代码 return "ok"; } return "fail"; }finally { // 是否为该线程持有锁 if (lock.isHeldByCurrentThread()) { lock.unlock(); } }}
显然,除了return ok
这行代码,其余的都是模板代码。
redissonClient: redisson框架的客户端,官网文档:https://github.com/redisson/r...
AOP切面设计如下:
编码
1、定义注解
@Target({ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)public @interface RedisLock { /** * key的名称前缀,与keys字段独特拼接出一个redis key * 如 name = user keys = 123(userId) * 最终应用的key为 user123 */ String name(); /** * 可能确定出零碎中唯一性资源的key * 如 用户, 应用用户id为key * 或者应用 用户名+手机号 * 必须为spel表达式 如 #id #user.id #user.name */ String[] keys();}
2、编写切面
@Aspectpublic class RedisLockAspect { @Resource private RedissonClient redissonClient; @Around(value = "@annotation(redisLock)") public Object lock(ProceedingJoinPoint joinPoint, RedisLock redisLock) throws Throwable { // 拼接出key 应用spel解析注解中定义的key String key = getRedisKey(joinPoint, redisLock); // 获取锁 RLock lock = redissonClient.getLock(key); try { // 加锁 if (lock.tryLock()) { return joinPoint.proceed(); } throw new RuntimeException("加锁失败"); }finally { if (lock.isHeldByCurrentThread()) { lock.unlock(); } } }}
一个超级简略的注解版Redis锁就实现了
读者:“啪!你就写个这破玩意搪塞我呢!”
“别别别,我错了,你听我持续说呀!“
组件库
以上内容只是流传思维,对,思维。
好用的注解版Redis锁我曾经实现了。并且曾经将它公布到了Maven地方仓库。
对于它的具体文档还请各位移步:https://github.com/lzj960515/...
如果你想晓得其中的实现,欢送clone代码浏览或者与我交换
同时,我还想给大伙介绍一下组件库:https://github.com/lzj960515/...
该组件库为我司外部的后端组件库——局部组件,外面的组件曾经久经考验,能够放心使用。
应用这些组件,能够将你的编码效率晋升...反正总是会有晋升的,我用着是特爽。
如果你想参加进来,欢送之至,如果你发现外面有bug,感激大佬!