您好,欢迎访问一九零五行业门户网

聊聊Redis中怎么实现支持几乎所有加锁场景的分布式锁

本篇文章给大家介绍一下redis分布式锁的实现方法,希望对大家有所帮助!
hello 大家好,今天给大家分享redisson实现的多类型锁、支持几乎所有加锁场景的redis分布式锁的实现,还支持小型mq和redis的各种数据操作。【相关推荐:redis视频教程】
理论部分在之前的文章中,介绍了通过redis实现分布锁的两种方式,分别是:
通过redis自带的命令:setnx
通过redis的客户端:redisson
作者更加推荐使用redisson客户端的方式,因为redisson支持更多的锁类型,譬如联锁、红锁、读写锁、公平锁等,而且redisson的实现更加简单,开发者只需要调用响应的api即可,无需关心底层加锁的过程和解锁的原子性问题。
在redis分布式锁中,列出了redisson对于多种的锁类型的简单实现,即编程式实现。这样的实现完全能够满足我们的日常开发需求,但是缺点也很明显。
譬如:
代码嵌入较多,不够优雅重复代码对锁的参数运用不直观容易忘掉解锁的步骤使用过spring的同学,肯定都知道@transactional注解,spring即支持编程式事务,也支持注解式(声明式)事务。
我们是否也可以参考这样的实现呢?
答案是:完全ok!
aop就是专门干这种事的。
实战部分1、引入redisson依赖<dependency> <groupid>org.redisson</groupid> <artifactid>redisson</artifactid> <version>3.16.2</version></dependency>copy to clipboarderrorcopied
2、自定义注解/** * 分布式锁自定义注解 */@target(elementtype.method)@retention(retentionpolicy.runtime)@documentedpublic @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、常量类/** * redisson常量类 */public class redissonconst { /** * redisson锁默认前缀 */ public static final string redisson_lock = "redisson:lock:"; /** * spel表达式占位符 */ public static final string place_holder = "#";}
4、枚举/** * 锁的模式 */public enum lockmodel { /** * 可重入锁 */ reentrant, /** * 公平锁 */ fair, /** * 联锁 */ multiple, /** * 红锁 */ red_lock, /** * 读锁 */ read, /** * 写锁 */ write, /** * 自动模式,当参数只有一个使用 reentrant 参数多个 red_lock */ auto}
5、自定义异常/** * 分布式锁异常 */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切面 /** * 分布式锁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 { // 需要加锁的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; }}
这里使用了策略模式来对不同的锁类型提供实现。
7、锁策略的实现先定义锁策略的抽象基类(也可以用接口):
/** * 锁策略抽象基类 */@slf4jabstract 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; }}
再提供各种锁模式的具体实现:
可重入锁:/** * 可重入锁策略 */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); } }}
公平锁:/** * 公平锁策略 */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)); }}
联锁/** * 联锁策略 */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); }}
红锁/** * 红锁策略 */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); }}
读锁/** * 读锁策略 */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(); }}
写锁/** * 写锁策略 */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(); }}
最后提供一个策略工厂初始化锁策略:
/** * 锁的策略工厂 */@servicepublic 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、使用方式 @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)); }
大功告成,顺利吃鸡
更多编程相关知识,请访问:编程视频!!
以上就是聊聊redis中怎么实现支持几乎所有加锁场景的分布式锁的详细内容。
其它类似信息

推荐信息