當前位置:
首頁 >
前端技术
> javascript
>内容正文
javascript
在SpringBoot中使用redis实现分布式锁
生活随笔
收集整理的這篇文章主要介紹了
在SpringBoot中使用redis实现分布式锁
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
在企業的項目中,經常會碰到多線程安全的問題。特別是在涉及到金錢方面的,安全問題更是重中之重。如何保證多線程下的安全就成了必須要解決的問題。
在之前負責的某個項目中,有幾個地方就被人惡意攻擊過。用戶申請提現的時候,通過接口快速訪問,可以跳過錢包余額的校驗達到多次提現。在微信小程序支付訂單的時候,小程序支付完之后,瞬時調用多次檢查訂單狀態的接口,也會導致多線程的問題,導致錢包余額增加多次。
最開始是用synchronized鎖來解決這一問題的。不過synchronized鎖意味的同一時刻該接口只能被一個人訪問,在用戶量很大的時候,有可能會造成用戶等待時間過長,體驗不好。所以使用了redis的分布式鎖來解決高并發下的線程安全問題。
1.RedisLock.java
@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface RedisLock { ?/** 鎖的資源,redis的key, */String value() default "default";/** 持鎖時間,單位毫秒*/long keepMills() default 2000;/** 當獲取失敗時候動作*/LockFailAction action() default LockFailAction.CONTINUE;public enum LockFailAction{/** 放棄 */GIVEUP,/** 繼續 */CONTINUE;}/** 重試的間隔時間,設置GIVEUP忽略此項*/long sleepMills() default 150;/** 重試次數*/int retryTimes() default 30; }2.RedisLockAspect.java
@Aspect @Component public class RedisLockAspect {public static Logger logger = LoggerFactory.getLogger(RedisLockAspect.class);@Pointcut("@annotation(xin.dayukeji.common.annotation.RedisLock)")public void point() {}@Autowiredprivate RedisLockService orderLockService;@Around(value = "point()")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {Object[] args = joinPoint.getArgs();// 獲取方法名稱String methodName = joinPoint.getSignature().getName();// 獲取所有參數類型Class<?>[] par = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();// 獲取目標類Class<?> classTarget = joinPoint.getTarget().getClass();// 獲取目標方法Method methodTarget = classTarget.getMethod(methodName, par);RedisLock redisLock = methodTarget.getDeclaredAnnotation(RedisLock.class);if (redisLock != null) {String key = (String) AnnotationResolver.newInstance().resolver(joinPoint, redisLock.value());System.out.println(key);int retryTimes = redisLock.action().equals(RedisLock.LockFailAction.CONTINUE) ? redisLock.retryTimes() : 0;boolean lock = orderLockService.lock(key, redisLock.keepMills(), retryTimes, redisLock.sleepMills());if (!lock) {logger.info("get lock failed : " + key);return null;//獲取鎖失敗,不執行邏輯}try {logger.debug("get lock success : " + key);//得到鎖,執行方法return joinPoint.proceed();} finally {//釋放鎖boolean releaseLock = orderLockService.releaseLock(key);logger.error("release lock : " + key + (releaseLock ? " success" : " failed"));}}return null;} }3.RedisLockService.java
@Service public class RedisLockService {@Autowiredprivate RedisTemplate<String,Object> template;@Autowiredprivate Env env; ?private static final long LOCK_EXPIRE = 600; ? ?/*** 鎖操作,對某個key加鎖,* @param key 需要加鎖的key* @param keepTime 保持鎖存在的時間(即多少時間后會失效),* @param retryTimes 線程沒有取到鎖需要重試去取鎖,的重試次數* @param sleepTime 線程沒有取到鎖后,距離下次取鎖操作的時間間隔。* @return*/public boolean lock(String key, long keepTime, int retryTimes, long sleepTime) {boolean lock = lock(key, keepTime);while (!lock && retryTimes-- > 0){//當沒有取到鎖,并且還有重試次數,繼續取鎖。取到鎖或沒有重試次數,跳出循環。try {Thread.sleep(sleepTime);lock = lock(key, keepTime);} catch (InterruptedException e) {e.printStackTrace();return false;}}return lock;} ? ?public boolean lock(String key){return lock(key, LOCK_EXPIRE);} ?/*** 加鎖并設置失效時間* @param key 需要加鎖的key* @param keepTime 保持鎖存在的時間(即多少時間后會失效),* @return*/public boolean lock(String key, Long keepTime){String lock = ?env.getProject() + ":" + key;return template.execute(new RedisCallback<Boolean>() {@Overridepublic Boolean doInRedis(RedisConnection connection) throws DataAccessException {//取當前時間戳,存入valuelong expire = System.currentTimeMillis();//取出redis原始操作JedisCommands commands = (JedisCommands) connection.getNativeConnection();/* NX: 表示只有當此key值不存在時才能存入成功* PX: 設置此key值的失效時間,單位為毫秒* 這樣當key不存在時,第一個線程能取到鎖,其他線程無法set key,取不到鎖,* 等到第一個線程被釋放或者鎖過了失效時間,才可以取到鎖*/String acquire = commands.set(lock, String.valueOf(expire + keepTime), "NX", "PX", keepTime);return "OK".equals(acquire);}});} ?/*** 當業務邏輯完成后,需要將鎖釋放。* @param key* @return*/public boolean releaseLock(String key){String lock = env.getProject() + ":" + key;Boolean delete = template.delete(lock);return delete==null?false:delete;}/*** 用于測試的方法*/@RedisLock(value = "#{id}")public void testLock(String id) {for (int i = 1; i < 10; i++) {System.out.println(i);try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}}} ? }4.AnnotationResolver.java
package xin.dayukeji.common.util;import org.aspectj.lang.JoinPoint; import org.aspectj.lang.reflect.MethodSignature;import java.lang.reflect.Method;/*** 該類的作用可以把方法上的參數綁定到注解的變量中,注解的語法#{變量名}* 能解析類似#{task}或者#{task.taskName}或者{task.project.projectName}** @author liuxg* date 2016年4月13日 下午8:42:34*/ public class AnnotationResolver {private static AnnotationResolver resolver;public static AnnotationResolver newInstance() {if (resolver == null) {return resolver = new AnnotationResolver();} else {return resolver;}}/*** 解析注解上的值** @param joinPoint 就是joinPoint* @param str 需要解析的字符串* @return object*/public Object resolver(JoinPoint joinPoint, String str) {if (str == null) return null;Object value = null;if (str.matches(".*#\\{.*\\}.*")) {// 如果name匹配上了#{},則把內容當作變量String newStr = str.replaceAll(".*#\\{", "").replaceAll("\\}.*", "");if (newStr.contains(".")) { // 復雜類型try {value = complexResolver(joinPoint, newStr);} catch (Exception e) {e.printStackTrace();}} else {value = simpleResolver(joinPoint, newStr);}String[] split = str.split("#\\{.*\\}");switch (split.length) {case 1:value = split[0] + value;break;case 2:value = split[0] + value + split[1];break;default:}} else { //非變量value = str;}return value;}private Object complexResolver(JoinPoint joinPoint, String str) throws Exception {MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();String[] names = methodSignature.getParameterNames();Object[] args = joinPoint.getArgs();String[] strs = str.split("\\.");for (int i = 0; i < names.length; i++) {if (strs[0].equals(names[i])) {Object obj = args[i];Method dmethod = obj.getClass().getDeclaredMethod(getMethodName(strs[1]), null);Object value = dmethod.invoke(args[i]);return getValue(value, 1, strs);}}return null;}private Object getValue(Object obj, int index, String[] strs) {try {if (obj != null && index < strs.length - 1) {Method method = obj.getClass().getDeclaredMethod(getMethodName(strs[index + 1]), null);obj = method.invoke(obj);getValue(obj, index + 1, strs);}return obj;} catch (Exception e) {e.printStackTrace();return null;}}private String getMethodName(String name) {return "get" + name.replaceFirst(name.substring(0, 1), name.substring(0, 1).toUpperCase());}private Object simpleResolver(JoinPoint joinPoint, String str) {MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();String[] names = methodSignature.getParameterNames();Object[] args = joinPoint.getArgs();for (int i = 0; i < names.length; i++) {if (str.equals(names[i])) {return args[i];}}return null;}}5.TestController.java
@RestController @RequestMapping("/test") public class TestController {@Autowiredprivate RedisLockService redisLockService; ?/*** 獲取分布式redis*/@GetMapping("/redisLock")@ResponseBody@ExcludeInterceptorpublic Report testRedisLock() {for (int i = 0; i < 10; i++) {new Thread(new Runnable() {@Overridepublic void run() {redisLockService.testLock("test Lock");}}).start();}return ReportFactory.S_0_OK.report();} }啟動項目,調用/test/redisLock接口測試該注解,控制臺打印
test Lock test Lock test Lock test Lock test Lock test Lock test Lock test Lock 1 2 3 4 5 6 7 8 9 2019-09-04 19:03:29.864 ERROR 5313 --- [ ? ? Thread-46] x.d.common.aspect.RedisLockAspect ? ? ? : release lock : test Lock success test Lock 1 2 3 4 5 6 7 8 9 2019-09-04 19:03:29.993 ERROR 5313 --- [ ? ? Thread-53] x.d.common.aspect.RedisLockAspect ? ? ? : release lock : test Lock success test Lock ... ... ...總結
以上是生活随笔為你收集整理的在SpringBoot中使用redis实现分布式锁的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Activiti工作流之实现一个简单的流
- 下一篇: Activiti工作流之业务标识和流程的