Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to implement distributed locks that support almost all locking scenarios in Redis

2025-01-19 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Database >

Share

Shulou(Shulou.com)05/31 Report--

Editor to share with you how to implement distributed locks that support almost all locking scenarios in Redis. I believe most people don't know much about it, so share this article for your reference. I hope you can learn a lot after reading this article. Let's learn about it together.

Practical part 1. Introduce redisson dependency org.redisson redisson 3.16.2Copy to clipboardErrorCopied2, custom annotation / * * distributed lock custom annotation * / @ Target (ElementType.METHOD) @ Retention (RetentionPolicy.RUNTIME) @ Documentedpublic @ interface Lock {/ * lock mode: if the automatic mode is not set, there is only one parameter. Use REENTRANT parameter multiple MULTIPLE * / LockModel lockModel () default LockModel.AUTO; / * if there is more than one keys, if not set, use interlock * * @ return * / String [] keys () default {} / * key static constant: when the spel of key is LIST, using the + sign to concatenate the array will be considered by spel as a string, which can only generate a lock, which does not achieve our purpose, * and if we need another constant. This parameter will be concatenated after each element * * @ return * / String keyConstant () default "; / * lock timeout. Default is 30000 milliseconds (which can be set globally in the configuration file) * * @ return * / long watchDogTimeout () default 30000 / * * timeout waiting for locking. By default, 10000 milliseconds-1 means waiting (which can be set globally in the configuration file) * * @ return * / long attemptTimeout () default 10000;} 3, constant class / * Redisson constant class * / public class RedissonConst {/ * redisson lock default prefix * / public static final String REDISSON_LOCK = "redisson:lock:" / * * spel expression placeholder * / public static final String PLACE_HOLDER = "#" 4. Enumerate / * lock mode * / public enum LockModel {/ * reentrant lock * / REENTRANT, / * fair lock * / FAIR, / * interlock * / MULTIPLE, / * red lock * / RED_LOCK, / * read lock * / READ / * write lock * / WRITE, / * automatic mode When there is only one parameter using REENTRANT parameter multiple RED_LOCK * / AUTO} 5, custom exception / * * distributed lock exception * / public class ReddissonException extends RuntimeException {public ReddissonException () {} public ReddissonException (String message) {super (message) } public ReddissonException (String message, Throwable cause) {super (message, cause);} public ReddissonException (Throwable cause) {super (cause);} public ReddissonException (String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {super (message, cause, enableSuppression, writableStackTrace);}} 6, AOP section / * * distributed lock aop * / @ Slf4j@Aspectpublic class LockAop {@ Autowired private RedissonClient redissonClient @ Autowired private RedissonProperties redissonProperties; @ Autowired private LockStrategyFactory lockStrategyFactory; @ Around ("@ annotation (lock)") public Object aroundAdvice (ProceedingJoinPoint proceedingJoinPoint, Lock lock) throws Throwable {/ / locked key array String [] keys = lock.keys (); if (ArrayUtil.isEmpty (keys)) {throw new ReddissonException ("redisson lock keys cannot be empty") } / / get the parameter name of the method String [] parameterNames = new LocalVariableTableParameterNameDiscoverer (). GetParameterNames (MethodSignature) proceedingJoinPoint.getSignature ()). GetMethod ()); Object [] args = proceedingJoinPoint.getArgs (); / / timeout waiting for the lock long attemptTimeout = lock.attemptTimeout (); if (attemptTimeout = = 0) {attemptTimeout = redissonProperties.getAttemptTimeout () } / / lock timeout long lockWatchdogTimeout = lock.watchdogTimeout (); if (lockWatchdogTimeout = = 0) {lockWatchdogTimeout = redissonProperties.getLockWatchdogTimeout ();} / / lock mode LockModel lockModel = getLockModel (lock, keys) If (! lockModel.equals (LockModel.MULTIPLE) & &! lockModel.equals (LockModel.RED_LOCK) & & keys.length > 1) {throw new ReddissonException ("there are multiple parameters, lock mode is->" + lockModel.name () + ", cannot match lock") } log.info ("lock mode-> {}, waiting lock time-> {} millisecond, maximum lock time-> {} millisecond", lockModel.name (), attemptTimeout, lockWatchdogTimeout); boolean res = false; / / Policy mode acquires redisson lock object RLock rLock = lockStrategyFactory.createLock (lockModel, keys, parameterNames, args, lock.keyConstant (), redissonClient) / / execute aop if (rLock! = null) {try {if (attemptTimeout = =-1) {res = true; / / wait for rLock.lock (lockWatchdogTimeout, TimeUnit.MILLISECONDS) to be locked } else {res = rLock.tryLock (attemptTimeout, lockWatchdogTimeout, TimeUnit.MILLISECONDS);} if (res) {return proceedingJoinPoint.proceed ();} else {throw new ReddissonException ("failed to acquire lock") }} finally {if (res) {rLock.unlock ();} throw new ReddissonException ("failed to acquire lock") } / * get locking mode * * @ param lock * @ param keys * @ return * / private LockModel getLockModel (Lock lock, String [] keys) {LockModel lockModel = lock.lockModel () / / automatic mode: match the global configuration first, and then determine whether to use red lock or reentrant lock if (lockModel.equals (LockModel.AUTO)) {LockModel globalLockModel = redissonProperties.getLockModel (); if (globalLockModel! = null) {lockModel = globalLockModel;} else if (keys.length > 1) {lockModel = LockModel.RED_LOCK } else {lockModel = LockModel.REENTRANT;}} return lockModel;}}

The policy pattern is used here to provide implementations for different lock types.

7. The realization of locking strategy.

First define the abstract base class of the lock policy (you can also use the interface):

/ * Lock policy abstract base class * / @ Slf4jabstract class LockStrategy {@ Autowired private RedissonClient redissonClient; / * create RLock * * @ param keys * @ param parameterNames * @ param keyConstant * @ return * / abstract RLock createLock (String [] keys, String [] parameterNames, Object [] args, String keyConstant, RedissonClient redissonClient) / * get RLock * * @ param keys * @ param parameterNames * @ param args * @ return * / public RLock [] getRLocks (String [] keys, String [] parameterNames, Object [] args, String keyConstant) {List rLocks = new ArrayList (); for (String key: keys) {List valueBySpel = getValueBySpel (key, parameterNames, args, keyConstant) For (String s: valueBySpel) {rLocks.add (redissonClient.getLock (s));}} RLock [] locks = new RLock [rLocks.size ()]; int index = 0; for (RLock r: rLocks) {locks [index++] = r;} return locks } / * * get the parameter * * @ param key through spring Spel the key value defined begins with # for example: # user * @ param parameterNames parameter * @ param args parameter value * @ param keyConstant key always bright * @ return * / List getValueBySpel (String key, String [] parameterNames, Object [] args String keyConstant) {List keys = new ArrayList () If (! key.contains (PLACE_HOLDER)) {String s = REDISSON_LOCK + key + keyConstant; log.info ("No spel expression value- > {}", s); keys.add (s); return keys;} / / spel parser ExpressionParser parser = new SpelExpressionParser () / / spel context EvaluationContext context = new StandardEvaluationContext (); for (int I = 0; I < parameterNames.length; iTunes +) {context.setVariable (parameterNames [I], args [I]);} Expression expression = parser.parse_Expression (key); Object value = expression.getValue (context) If (value! = null) {if (value instanceof List) {List valueList = (List) value; for (Object o: valueList) {keys.add (REDISSON_LOCK + o.toString () + keyConstant) }} else if (value.getClass () .isArray ()) {Object [] objects = (Object []) value; for (Object o: objects) {keys.add (REDISSON_LOCK + o.toString () + keyConstant) }} else {keys.add (REDISSON_LOCK + value.toString () + keyConstant);}} log.info ("spel expression key= {}, value= {}", key, keys); return keys;}}

Then provide the specific implementation of various lock modes:

Reentrant lock:

/ * reentrant lock policy * / public class ReentrantLockStrategy extends LockStrategy {@ Override public RLock createLock (String [] keys, String [] parameterNames, Object [] args, String keyConstant, RedissonClient redissonClient) {List valueBySpel = getValueBySpel (keys [0], parameterNames, args, keyConstant) / / if the spel expression is an array or collection, use the red lock if (valueBySpel.size () = = 1) {return redissonClient.getLock (valueBySpel.get (0));} else {RLock [] locks = new RLock [valueBySpel.size ()]; int index = 0 For (String s: valueBySpel) {locks [index++] = redissonClient.getLock (s);} return new RedissonRedLock (locks);}

Fair Lock:

/ * Fair locking policy * / public class FairLockStrategy extends LockStrategy {@ Override public RLock createLock (String [] keys, String [] parameterNames, Object [] args, String keyConstant, RedissonClient redissonClient) {return redissonClient.getFairLock (getValueBySpel (keys [0], parameterNames, args, keyConstant) .get (0));}}

Interlock

/ * interlocking policy * / public class MultipleLockStrategy extends LockStrategy {@ Override public RLock createLock (String [] keys, String [] parameterNames, Object [] args, String keyConstant, RedissonClient redissonClient) {RLock [] locks = getRLocks (keys, parameterNames, args, keyConstant); return new RedissonMultiLock (locks);}}

Red lock

/ * Red Lock Policy * / public class RedLockStrategy extends LockStrategy {@ Override public RLock createLock (String [] keys, String [] parameterNames, Object [] args, String keyConstant, RedissonClient redissonClient) {RLock [] locks = getRLocks (keys, parameterNames, args, keyConstant); return new RedissonRedLock (locks);}}

Read lock

/ * read locking policy * / public class ReadLockStrategy extends LockStrategy {@ Override public RLock createLock (String [] keys, String [] parameterNames, Object [] args, String keyConstant, RedissonClient redissonClient) {RReadWriteLock rwLock = redissonClient.getReadWriteLock (getValueBySpel (keys [0], parameterNames, args, keyConstant) .get (0)); return rwLock.readLock ();}}

Write lock

/ * write locking policy * / public class WriteLockStrategy extends LockStrategy {@ Override public RLock createLock (String [] keys, String [] parameterNames, Object [] args, String keyConstant, RedissonClient redissonClient) {RReadWriteLock rwLock = redissonClient.getReadWriteLock (getValueBySpel (keys [0], parameterNames, args, keyConstant) .get (0)); return rwLock.writeLock ();}}

Finally, a policy factory is provided to initialize the lock policy:

/ * Policy factory for locks * / @ Servicepublic class LockStrategyFactory {private LockStrategyFactory () {} private static final Map STRATEGIES = new HashMap (6); static {STRATEGIES.put (LockModel.FAIR, new FairLockStrategy ()); STRATEGIES.put (LockModel.REENTRANT, new ReentrantLockStrategy ()); STRATEGIES.put (LockModel.RED_LOCK, new RedLockStrategy ()); STRATEGIES.put (LockModel.READ, new ReadLockStrategy ()) STRATEGIES.put (LockModel.WRITE, new WriteLockStrategy ()); STRATEGIES.put (LockModel.MULTIPLE, new MultipleLockStrategy ());} public RLock createLock (LockModel lockModel, String [] keys, String [] parameterNames, Object [] args, String keyConstant, RedissonClient redissonClient) {return STRATEGIES.get (lockModel) .createLock (keys, parameterNames, args, keyConstant, redissonClient) }} 8. Usage @ Lock (keys = "# query.channel") / / support spel @ ApiOperation ("pagination list") @ GetMapping public ApiPageResult list (VendorProjectItemQuery query, Pagination pagination) {return ApiPageResult.success (pagination, vendorProjectItemService.list (query, pagination), vendorProjectItemService.count (query)) } these are all the contents of the article "how to implement distributed locks that support almost all locking scenarios in Redis". Thank you for reading! I believe we all have a certain understanding, hope to share the content to help you, if you want to learn more knowledge, welcome to follow the industry information channel!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Database

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report