1Foreword
In daily development, it is inevitable to encounter some concurrency scenarios. In order to ensure the consistency of interface execution, locking is usually used. Because the service is a distributed deployment mode, the local locks Reentrantlock and Synchnorized are put aside first. Redis’s The problem of setnx lock being unable to guarantee atomicity will be put aside for the time being, and I will go directly to the big move. Redisson is also the cache that I basically use in my recent development projects, and I also use its distributed lock mechanism.
2Redisson distributed lock general use
Regarding some basic concepts of Redisson, this chapter will not explain it in too much detail. Interested friends can learn about it by themselves. It mainly talks about the regular use of locking. Redisson distributed lock is based on Redis’s Rlock lock, which is implemented Lock interface under JavaJUC package.
Lock
public void getLock(){ //Get the lock RLock lock = redisson.getLock("Lxlxxx_Lock"); try { // 2. Lock lock.lock(); } catch (InterruptedException e) { e.getStackTrace(); } finally { // 3. Unlock lock.unlock(); System.out.println("Finally, lock released successfully"); }
getLock obtains the lock and lock.lock performs the locking. The problem that occurs is that lock waits until it cannot get the lock and enters a blocking state. Obviously this is not good.
TryLock
Returns a boolean type, which has the same meaning as Reentrantlock’s tryLock. Try to acquire the lock. If it is obtained, it will return true. If the acquisition fails, it will return false. It will not keep the thread that cannot obtain the lock in a waiting state. Returning false can continue to perform the following business. Logic, of course, the Ression lock also involves the watchDog watchdog mechanism. The main function is to renew the lock that is about to expire. The main purpose is to allow the limited time for the lock to be completed before the lock is released.
RLock lock = redisson.getLock(name); try { if (lock.tryLock(2, 10, TimeUnit.SECONDS)) { //Execute business logic } else { System.out.println("Already exists"); } } catch (InterruptedException e) { e.printStackTrace(); }finally { //Determine whether the lock held by the current thread is in the locked state, and then release it in the locked state. if (this.redissonLock.isHeldByCurrentThread(lockName)) { this.redissonLock.unlock(lockName); } }
3 Custom annotations to implement lock mechanism
Usually we inject the redisson instance into the method class, and then call the locking method to lock. If other business methods also need to be locked for execution, a lot of duplicate code will be generated. Therefore, using the AOP aspect method, you only need to pass annotations The method can be locked.
Custom annotations
@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 "The system is processing normally, please submit later"; int waitTime() default 1; }
Aspect class implementation
@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("Exception in executing business method", var12); throw var12; } finally { if (this.redissonLock.isHeldByCurrentThread(lockName)) { this.redissonLock.unlock(lockName); } } return var8; } /** * Get the locked 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 & amp; & amp; 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 & amp; & amp; 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 & amp; & amp; !"".equals(expressionValue.toString()) ? expressionValue.toString() : key; } catch (Exception var13) { return key; } } else { return key; } } } }
Specific use
Add custom annotations to the method header, the key parameter represents the key that needs to be locked, and errorDesc will prompt an error message if it fails to obtain the lock.
Here I started the project by modifying the port and started two services, namely 8460 and 8461.
Call these two services through postman to simulate the scenario where two services acquire a lock at the same time. One service acquires the lock, and the other service fails to acquire the lock.
You can see that the port 8460 service gets the lock first, and the 8461 service tryLock fails to get the lock, and the locking logic is implemented.
4Summary
The use scenarios of distributed locks still require more attention. According to the business scenario, if the amount of concurrency is not large, there is actually no need to add it. You may need to pay attention to concurrency when the mobile terminal operates more frequently. Currently, I am working on the b-side Projects can avoid repeated submissions through simple interface idempotence operations. Do not blindly lock, which will affect some performance.
Source: juejin.cn/post/7215142807861379109
Back-end exclusive technology group
To build a high-quality technical exchange community, HR personnel engaged in programming development and technical recruitment are welcome to join the group. Everyone is also welcome to share their own company’s internal information, help each other, and make progress together!
Speech in a civilized manner, focusing on
communication technology
,recommendation of positions
, andindustry discussion
Advertisers are not allowed to enter, and do not trust private messages to prevent being deceived.