使用自定義注解實現(xiàn)redisson分布式鎖
自定義注解實現(xiàn)redisson分布式鎖
自定義注解
package com.example.demo.annotation;
import java.lang.annotation.*;
/**
* desc: 自定義 redisson 分布式鎖注解
*
* @author: 邢陽
* @mail: xydeveloper@126.com
* @create 2021-05-28 16:50
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface Lock {
/**
* 鎖的key spel 表達式
*
* @return
*/
String key();
/**
* 持鎖時間
*
* @return
*/
long keepMills() default 20;
/**
* 沒有獲取到鎖時,等待時間
*
* @return
*/
long maxSleepMills() default 30;
}
aop解析注解
package com.example.demo.utils;
import com.example.demo.annotation.Lock;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
* desc: 解析 自定義 redisson 分布式鎖注解
*
* @author: 邢陽
* @mail: xydeveloper@126.com
* @create 2021-05-28 16:50
*/
@Aspect
@Component
public class LockAspect {
@Autowired
private RedissonClient redissonClient;
/**
* 用于SpEL表達式解析.
*/
private final SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
/**
* 用于獲取方法參數(shù)定義名字.
*/
private final DefaultParameterNameDiscoverer defaultParameterNameDiscoverer = new DefaultParameterNameDiscoverer();
@Around("@annotation(com.example.demo.annotation.Lock)")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
Object object = null;
RLock lock = null;
try {
// 獲取注解實體信息
Lock lockEntity = (((MethodSignature) proceedingJoinPoint.getSignature()).getMethod())
.getAnnotation(Lock.class);
// 根據(jù)名字獲取鎖實例
lock = redissonClient.getLock(getKeyBySpeL(lockEntity.key(), proceedingJoinPoint));
if (Objects.nonNull(lock)) {
if (lock.tryLock(lockEntity.maxSleepMills(), lockEntity.keepMills(), TimeUnit.SECONDS)) {
object = proceedingJoinPoint.proceed();
} else {
throw new RuntimeException();
}
}
} finally {
if (Objects.nonNull(lock) && lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
return object;
}
/**
* 獲取緩存的key
*
* key 定義在注解上,支持SPEL表達式
*
* @return
*/
public String getKeyBySpeL(String spel, ProceedingJoinPoint proceedingJoinPoint) {
MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
String[] paramNames = defaultParameterNameDiscoverer.getParameterNames(methodSignature.getMethod());
EvaluationContext context = new StandardEvaluationContext();
Object[] args = proceedingJoinPoint.getArgs();
for (int i = 0; i < args.length; i++) {
context.setVariable(paramNames[i], args[i]);
}
return String.valueOf(spelExpressionParser.parseExpression(spel).getValue(context));
}
}
service中使用注解加鎖使用
/**
* desc: 鎖
*
* @author: 邢陽
* @mail: xydeveloper@126.com
* @create 2021-05-28 17:58
*/
@Service
public class LockService {
@Lock(key = "#user.id", keepMills = 10, maxSleepMills = 15)
public String lock(User user) {
System.out.println("持鎖");
return "";
}
}
redisson分布式鎖應用
分布式架構(gòu)一定會用到分布式鎖。目前公司使用的基于redis的redisson分布式鎖。
應用場景
1.訂單修改操作,首先要獲取該訂單的分布式鎖,能取到才能去操作。lockey可以是訂單的主鍵id。
2.庫存操作,也要按照客戶+倉庫+sku維護鎖定該庫存,進行操作。
代碼:
Redisson管理類
public class RedissonManager {
? ? private static RedissonClient redisson;
? ? static {
? ? ? ? Config config = new Config();
? ? ? ? config.useSentinelServers()
? ? ? ? ? ? ? ? .addSentinelAddress("redis://127.0.0.1:26379","redis://127.0.0.1:7301", "redis://127.0.0.1:7302")
? ? ? ? ? ? ? ? .setMasterName("mymaster")
? ? ? ? ? ? ? ? .setReadMode(ReadMode.SLAVE)
? ? ? ? ? ? ? ? .setTimeout(10000).setDatabase(0).setPassword("123***");
? ? ? ? redisson = Redisson.create(config);
? ? }
?
? ? /**
? ? ?* 獲取Redisson的實例對象
? ? ?* @return
? ? ?*/
? ? public static RedissonClient getRedisson(){ return redisson;}
}分布式鎖
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import java.util.concurrent.TimeUnit;
public class DistributedLock {
? ? private static RedissonClient redissonClient = RedissonManager.getRedisson();
? ? public static boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime) {
? ? ? ? RLock lock = redissonClient.getLock(lockKey);
? ? ? ? try {
? ? ? ? ? ? return lock.tryLock(waitTime, leaseTime, unit);
? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? return false;
? ? ? ? }
? ? }
? ? public static void unlock(String lockKey) {
? ? ? ? RLock lock = redissonClient.getLock(lockKey);
? ? ? ? lock.unlock();
? ? }
}測試類
public class RedissonTest {
? ? public static void main(String[] args) throws Exception{
? ? ? ? Thread.sleep(2000L);
? ? ? ? for (int i = 0; i < 3; i++) {
? ? ? ? ? ? new Thread(() -> {
? ? ? ? ? ? ? ? try {
? ? ? ? ? ? ? ? ? ? //tryLock,第三個參數(shù)是等待時間,5秒內(nèi)獲取不到鎖,則直接返回。 第四個參數(shù) 30是30秒后強制釋放
? ? ? ? ? ? ? ? ? ? boolean hasLock = DistributedLock.tryLock("lockKey", TimeUnit.SECONDS,5,30);
? ? ? ? ? ? ? ? ? ? //獲得分布式鎖
? ? ? ? ? ? ? ? ? ? if(hasLock){
? ? ? ? ? ? ? ? ? ? ? ? System.out.println("idea1: " + Thread.currentThread().getName() + "獲得了鎖");
? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? /**
? ? ? ? ? ? ? ? ? ? ? ? ?* 由于在DistributedLock.tryLock設(shè)置的等待時間是5s,
? ? ? ? ? ? ? ? ? ? ? ? ?* 所以這里如果休眠的小于5秒,這第二個線程能獲取到鎖,
? ? ? ? ? ? ? ? ? ? ? ? ?* ?如果設(shè)置的大于5秒,則剩下的線程都不能獲取鎖??梢苑謩e試試2s,和8s的情況
? ? ? ? ? ? ? ? ? ? ? ? ?*/
? ? ? ? ? ? ? ? ? ? ? ? Thread.sleep(10000L);
? ? ? ? ? ? ? ? ? ? ? ? DistributedLock.unlock("lockKey");
? ? ? ? ? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? ? ? ? ? System.out.println("idea1: " + Thread.currentThread().getName() + "無法獲取鎖");
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? } catch (Exception e) {
? ? ? ? ? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? ? ? }?
? ? ? ? ? ? }) .start();
? ? ? ? }
? ? }
}我們再打開一個idea,可以把代碼復制一份。同事啟動兩個RedissonTest ,模擬了并發(fā)操作。
測試結(jié)果:
idea2: Thread-1獲得了鎖
idea2: Thread-0無法獲取鎖
idea2: Thread-2無法獲取鎖
idea1: Thread-2無法獲取鎖
idea1: Thread-0無法獲取鎖
idea1: Thread-1無法獲取鎖
從測試結(jié)果發(fā)現(xiàn),最后是只能有一個idea的一個線程能獲取到鎖。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
springboot接收前端參數(shù)的四種方式圖文詳解
Spring Boot可以通過多種方式接收前端傳遞的數(shù)據(jù),下面這篇文章主要給大家介紹了關(guān)于springboot接收前端參數(shù)的四種方式,文中通過圖文介紹的非常詳細,需要的朋友可以參考下2023-11-11
SpringBoot在IDEA中實現(xiàn)熱部署(JRebel實用版)
這篇文章主要介紹了SpringBoot在IDEA中實現(xiàn)熱部署(JRebel實用版),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-05-05

