


Let's talk about how to implement distributed locks in Redis that support almost all locking scenarios.
This article will introduce you to the implementation method of Redis distributed lock. I hope it will be helpful to you!
Hello everyone, today I will share with you the multi-type lock implemented by redisson, the implementation of redis distributed lock that supports almost all locking scenarios, and also supports small MQ and redis various data operations. [Related recommendations: Redis video tutorial]
Theoretical part
In the previous article, two methods of implementing distributed locks through redis were introduced The methods are:
Through the command that comes with redis: setNX
Through the redis client: redisson
The author recommends using the redisson client, because redisson supports more lock types, such as interlock, red lock, read-write lock, fair lock, etc., and the implementation of redisson is more Simple, developers only need to call the corresponding API
, and do not need to worry about the underlying locking process and the atomicity
issue of unlocking.
In the Redis distributed lock, redisson's simple implementation of various lock types is listed, that is, programmatic implementation. Such an implementation can fully meet our daily development needs, but its shortcomings are also obvious.
For example:
- There are too many codes embedded, which is not elegant enough
- Duplicate code
- The use of lock parameters is not intuitive
- It is easy to forget the unlocking steps
Students who have used Spring must have Knowing the @Transactional annotation, Spring supports both programmatic transactions and annotated (declarative) transactions.
Can we also refer to such an implementation?
The answer is: completely OK!
AOP
is specialized in doing this kind of thing.
Practical part
1. Introduction of redisson dependency
<dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.16.2</version> </dependency>Copy to clipboardErrorCopied
2. Custom annotations
/** * 分布式锁自定义注解 */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Lock { /** * 锁的模式:如果不设置自动模式,当参数只有一个.使用 REENTRANT 参数多个 MULTIPLE */ LockModel lockModel() default LockModel.AUTO; /** * 如果keys有多个,如果不设置,则使用 联锁 * * @return */ String[] keys() default {}; /** * key的静态常量:当key的spel的值是LIST、数组时使用+号连接将会被spel认为这个变量是个字符串,只能产生一把锁,达不到我们的目的, * 而我们如果又需要一个常量的话。这个参数将会在拼接在每个元素的后面 * * @return */ String keyConstant() default ""; /** * 锁超时时间,默认30000毫秒(可在配置文件全局设置) * * @return */ long watchDogTimeout() default 30000; /** * 等待加锁超时时间,默认10000毫秒 -1 则表示一直等待(可在配置文件全局设置) * * @return */ long attemptTimeout() default 10000; }
3. Constant class
/** * Redisson常量类 */ public class RedissonConst { /** * redisson锁默认前缀 */ public static final String REDISSON_LOCK = "redisson:lock:"; /** * spel表达式占位符 */ public static final String PLACE_HOLDER = "#"; }
4. Enumeration
/** * 锁的模式 */ public enum LockModel { /** * 可重入锁 */ REENTRANT, /** * 公平锁 */ FAIR, /** * 联锁 */ MULTIPLE, /** * 红锁 */ RED_LOCK, /** * 读锁 */ READ, /** * 写锁 */ WRITE, /** * 自动模式,当参数只有一个使用 REENTRANT 参数多个 RED_LOCK */ AUTO }
5. Custom 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 aspects
/** * 分布式锁aop */ @Slf4j @Aspect public 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 { // 需要加锁的key数组 String[] keys = lock.keys(); if (ArrayUtil.isEmpty(keys)) { throw new ReddissonException("redisson lock keys不能为空"); } // 获取方法的参数名 String[] parameterNames = new LocalVariableTableParameterNameDiscoverer().getParameterNames(((MethodSignature) proceedingJoinPoint.getSignature()).getMethod()); Object[] args = proceedingJoinPoint.getArgs(); // 等待锁的超时时间 long attemptTimeout = lock.attemptTimeout(); if (attemptTimeout == 0) { attemptTimeout = redissonProperties.getAttemptTimeout(); } // 锁超时时间 long lockWatchdogTimeout = lock.watchdogTimeout(); if (lockWatchdogTimeout == 0) { lockWatchdogTimeout = redissonProperties.getLockWatchdogTimeout(); } // 加锁模式 LockModel lockModel = getLockModel(lock, keys); if (!lockModel.equals(LockModel.MULTIPLE) && !lockModel.equals(LockModel.RED_LOCK) && keys.length > 1) { throw new ReddissonException("参数有多个,锁模式为->" + lockModel.name() + ",无法匹配加锁"); } log.info("锁模式->{},等待锁定时间->{}毫秒,锁定最长时间->{}毫秒", lockModel.name(), attemptTimeout, lockWatchdogTimeout); boolean res = false; // 策略模式获取redisson锁对象 RLock rLock = lockStrategyFactory.createLock(lockModel, keys, parameterNames, args, lock.keyConstant(), redissonClient); //执行aop if (rLock != null) { try { if (attemptTimeout == -1) { res = true; //一直等待加锁 rLock.lock(lockWatchdogTimeout, TimeUnit.MILLISECONDS); } else { res = rLock.tryLock(attemptTimeout, lockWatchdogTimeout, TimeUnit.MILLISECONDS); } if (res) { return proceedingJoinPoint.proceed(); } else { throw new ReddissonException("获取锁失败"); } } finally { if (res) { rLock.unlock(); } } } throw new ReddissonException("获取锁失败"); } /** * 获取加锁模式 * * @param lock * @param keys * @return */ private LockModel getLockModel(Lock lock, String[] keys) { LockModel lockModel = lock.lockModel(); // 自动模式:优先匹配全局配置,再判断用红锁还是可重入锁 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; } }
Strategy pattern
is used here to provide implementation for different lock types.
7. Implementation of lock strategy
First define the abstract base class of the lock strategy(you can also use interface) :
/** * 锁策略抽象基类 */ @Slf4j abstract class LockStrategy { @Autowired private RedissonClient redissonClient; /** * 创建RLock * * @param keys * @param parameterNames * @param args * @param keyConstant * @return */ abstract RLock createLock(String[] keys, String[] parameterNames, Object[] args, String keyConstant, RedissonClient redissonClient); /** * 获取RLock * * @param keys * @param parameterNames * @param args * @param keyConstant * @return */ public RLock[] getRLocks(String[] keys, String[] parameterNames, Object[] args, String keyConstant) { List<RLock> rLocks = new ArrayList<>(); for (String key : keys) { List<String> 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; } /** * 通过spring Spel 获取参数 * * @param key 定义的key值 以#开头 例如:#user * @param parameterNames 形参 * @param args 形参值 * @param keyConstant key的常亮 * @return */ List<String> getValueBySpel(String key, String[] parameterNames, Object[] args, String keyConstant) { List<String> keys = new ArrayList<>(); if (!key.contains(PLACE_HOLDER)) { String s = REDISSON_LOCK + key + keyConstant; log.info("没有使用spel表达式value->{}", s); keys.add(s); return keys; } // spel解析器 ExpressionParser parser = new SpelExpressionParser(); // spel上下文 EvaluationContext context = new StandardEvaluationContext(); for (int i = 0; i < parameterNames.length; i++) { context.setVariable(parameterNames[i], args[i]); } Expression expression = parser.parseExpression(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表达式key={},value={}", key, keys); return keys; } }
Then provide the specific implementation of various lock modes:
- Reentrant lock:
/** * 可重入锁策略 */ public class ReentrantLockStrategy extends LockStrategy { @Override public RLock createLock(String[] keys, String[] parameterNames, Object[] args, String keyConstant, RedissonClient redissonClient) { List<String> valueBySpel = getValueBySpel(keys[0], parameterNames, args, keyConstant); //如果spel表达式是数组或者集合 则使用红锁 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:
/** * 公平锁策略 */ 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)); } }
- Interlocking
/** * 联锁策略 */ 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
/** * 红锁策略 */ 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
/** * 读锁策略 */ 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
/** * 写锁策略 */ 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 provides aStrategy FactoryInitialization lock strategy:
/** * 锁的策略工厂 */ @Service public class LockStrategyFactory { private LockStrategyFactory() { } private static final Map<LockModel, LockStrategy> 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. How to use
@Lock(keys = "#query.channel") // 支持spel @ApiOperation("分页列表") @GetMapping public ApiPageResult list(VendorProjectItemQuery query, Pagination pagination) { return ApiPageResult.success(pagination, vendorProjectItemService.list(query, pagination), vendorProjectItemService.count(query)); }
You’re done, eat chicken smoothly
For more programming-related knowledge, please visit: Programming Video! !
The above is the detailed content of Let's talk about how to implement distributed locks in Redis that support almost all locking scenarios.. For more information, please follow other related articles on the PHP Chinese website!

Redis goes beyond SQL databases because of its high performance and flexibility. 1) Redis achieves extremely fast read and write speed through memory storage. 2) It supports a variety of data structures, such as lists and collections, suitable for complex data processing. 3) Single-threaded model simplifies development, but high concurrency may become a bottleneck.

Redis is superior to traditional databases in high concurrency and low latency scenarios, but is not suitable for complex queries and transaction processing. 1.Redis uses memory storage, fast read and write speed, suitable for high concurrency and low latency requirements. 2. Traditional databases are based on disk, support complex queries and transaction processing, and have strong data consistency and persistence. 3. Redis is suitable as a supplement or substitute for traditional databases, but it needs to be selected according to specific business needs.

Redisisahigh-performancein-memorydatastructurestorethatexcelsinspeedandversatility.1)Itsupportsvariousdatastructureslikestrings,lists,andsets.2)Redisisanin-memorydatabasewithpersistenceoptions,ensuringfastperformanceanddatasafety.3)Itoffersatomicoper

Redis is primarily a database, but it is more than just a database. 1. As a database, Redis supports persistence and is suitable for high-performance needs. 2. As a cache, Redis improves application response speed. 3. As a message broker, Redis supports publish-subscribe mode, suitable for real-time communication.

Redisisamultifacetedtoolthatservesasadatabase,server,andmore.Itfunctionsasanin-memorydatastructurestore,supportsvariousdatastructures,andcanbeusedasacache,messagebroker,sessionstorage,andfordistributedlocking.

Redisisanopen-source,in-memorydatastructurestoreusedasadatabase,cache,andmessagebroker,excellinginspeedandversatility.Itiswidelyusedforcaching,real-timeanalytics,sessionmanagement,andleaderboardsduetoitssupportforvariousdatastructuresandfastdataacces

Redis is an open source memory data structure storage used as a database, cache and message broker, suitable for scenarios where fast response and high concurrency are required. 1.Redis uses memory to store data and provides microsecond read and write speed. 2. It supports a variety of data structures, such as strings, lists, collections, etc. 3. Redis realizes data persistence through RDB and AOF mechanisms. 4. Use single-threaded model and multiplexing technology to handle requests efficiently. 5. Performance optimization strategies include LRU algorithm and cluster mode.

Redis's functions mainly include cache, session management and other functions: 1) The cache function stores data through memory to improve reading speed, and is suitable for high-frequency access scenarios such as e-commerce websites; 2) The session management function shares session data in a distributed system and automatically cleans it through an expiration time mechanism; 3) Other functions such as publish-subscribe mode, distributed locks and counters, suitable for real-time message push and multi-threaded systems and other scenarios.


Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

EditPlus Chinese cracked version
Small size, syntax highlighting, does not support code prompt function

SublimeText3 Linux new version
SublimeText3 Linux latest version

mPDF
mPDF is a PHP library that can generate PDF files from UTF-8 encoded HTML. The original author, Ian Back, wrote mPDF to output PDF files "on the fly" from his website and handle different languages. It is slower than original scripts like HTML2FPDF and produces larger files when using Unicode fonts, but supports CSS styles etc. and has a lot of enhancements. Supports almost all languages, including RTL (Arabic and Hebrew) and CJK (Chinese, Japanese and Korean). Supports nested block-level elements (such as P, DIV),

Safe Exam Browser
Safe Exam Browser is a secure browser environment for taking online exams securely. This software turns any computer into a secure workstation. It controls access to any utility and prevents students from using unauthorized resources.

Dreamweaver Mac version
Visual web development tools
