Redisson依赖:
<dependency>
<groupId>org.redissongroupId>
<artifactId>redissonartifactId>
<version>2.2.13version>
dependency>
网上关于redis分布式锁实现的文章很多,本文也参考了很多网上的代码,不过我做的是再封一层,利用AOP与注解实现注解形式的分布式锁,首先定义一个util类,
public class RedisUtils { private static Logger logger= LoggerFactory.getLogger(RedisUtils.class); private static RedisUtils redisUtils; private static RedissonClient redissonClient; private RedisUtils(){} /** * 提供单例模式 * @return */ public static RedisUtils getInstance(){ if(redisUtils==null) synchronized (RedisUtils.class) { if(redisUtils==null) redisUtils=new RedisUtils(); } return redisUtils; } /** * 使用config创建Redisson * Redisson是用于连接Redis Server的基础类 * @param config * @return */ public static RedissonClient getRedisson(Config config){ RedissonClient redisson= Redisson.create(config); logger.info("成功连接Redis Server"); return redisson; } /** * 使用ip地址和端口创建Redisson * @param ip * @param port * @return */ public static RedissonClient getRedisson(String ip,String port){ Config config=new Config(); config.useSingleServer().setAddress(ip+":"+port); RedissonClient redisson=Redisson.create(config); logger.info("成功连接Redis Server"+"\t"+"连接"+ip+":"+port+"服务器"); return redisson; } /** * 关闭Redisson客户端连接 * @param redisson */ public static void closeRedisson(RedissonClient redisson){ redisson.shutdown(); logger.info("成功关闭Redis Client连接"); } /** * 获取字符串对象 * @param redisson * @param objectName * @return */ public static <T> RBucket<T> getRBucket(RedissonClient redisson,String objectName){ RBucket<T> bucket=redisson.getBucket(objectName); return bucket; } /** * 获取Map对象 * @param redisson * @param objectName * @return */ public static <K,V> RMap<K, V> getRMap(RedissonClient redisson,String objectName){ RMap<K, V> map=redisson.getMap(objectName); return map; } /** * 获取有序集合 * @param redisson * @param objectName * @return */ public static <V> RSortedSet<V> getRSortedSet(RedissonClient redisson,String objectName){ RSortedSet<V> sortedSet=redisson.getSortedSet(objectName); return sortedSet; } /** * 获取集合 * @param redisson * @param objectName * @return */ public static <V> RSet<V> getRSet(RedissonClient redisson,String objectName){ RSet<V> rSet=redisson.getSet(objectName); return rSet; } /** * 获取列表 * @param redisson * @param objectName * @return */ public static <V> RList<V> getRList(RedissonClient redisson,String objectName){ RList<V> rList=redisson.getList(objectName); return rList; } /** * 获取队列 * @param redisson * @param objectName * @return */ public <V> RQueue<V> getRQueue(RedissonClient redisson,String objectName){ RQueue<V> rQueue=redisson.getQueue(objectName); return rQueue; } /** * 获取双端队列 * @param redisson * @param objectName * @return */ public static <V> RDeque<V> getRDeque(RedissonClient redisson,String objectName){ RDeque<V> rDeque=redisson.getDeque(objectName); return rDeque; } /** * 此方法不可用在Redisson 1.2 中 * 在1.2.2版本中可用 * @param redisson * @param objectName * @return */ public static <V> RBlockingQueue<V> getRBlockingQueue(RedissonClient redisson,String objectName){ RBlockingQueue rb=redisson.getBlockingQueue(objectName); return rb; } /** * 获取锁 * @param redisson * @param objectName * @return */ public static RLock getRLock(RedissonClient redisson,String objectName){ RLock rLock=redisson.getLock(objectName); return rLock; } /** * 获取原子数 * @param redisson * @param objectName * @return */ public static RAtomicLong getRAtomicLong(RedissonClient redisson,String objectName){ RAtomicLong rAtomicLong=redisson.getAtomicLong(objectName); return rAtomicLong; } /** * 获取记数锁 * @param redisson * @param objectName * @return */ public static RCountDownLatch getRCountDownLatch(RedissonClient redisson,String objectName){ RCountDownLatch rCountDownLatch=redisson.getCountDownLatch(objectName); return rCountDownLatch; } /** * 获取消息的Topic * @param redisson * @param objectName * @return */ public static <M> RTopic<M> getRTopic(RedissonClient redisson,String objectName){ RTopic<M> rTopic=redisson.getTopic(objectName); return rTopic; } /** * 获取包括方法参数上的key * redis key的拼写规则为 "DistRedisLock+" + lockKey + @DistRedisLockKey
* * @param point * @param lockKey * @return */ public static String getLockKey(ProceedingJoinPoint point, String lockKey) { try { lockKey = "DistRedisLock:" + lockKey; Object[] args = point.getArgs(); if (args != null && args.length > 0) { MethodSignature methodSignature = (MethodSignature)point.getSignature(); Annotation[][] parameterAnnotations = methodSignature.getMethod().getParameterAnnotations(); SortedMapkeys = new TreeMap<>(); for (int i = 0; i < parameterAnnotations.length; i ++) { RedisLockKey redisLockKey = getAnnotation(RedisLockKey.class, parameterAnnotations[i]); if (redisLockKey != null) { Object arg = args[i]; if (arg != null) { keys.put(redisLockKey.order(), arg.toString()); } } } if (keys != null && keys.size() > 0){ for (String key : keys.values()) { lockKey += key; } } } return lockKey; } catch (Exception e) { logger.error("getLockKey error.", e); } return null; } /** * 获取注解类型 * @param annotationClass * @param annotations * @param * @return */ private static <T extends Annotation> T getAnnotation(final Class<T> annotationClass, final Annotation[] annotations) { if (annotations != null && annotations.length > 0) { for (final Annotation annotation : annotations) { if (annotationClass.equals(annotation.annotationType())) { return (T) annotation; } } } return null; } public static RedissonClient createClient(String address , String pass) { if(redissonClient == null) { synchronized (RedisUtils.class) { if(redissonClient == null) { Config config = new Config(); SingleServerConfig singleSerververConfig = config.useSingleServer(); singleSerververConfig.setAddress(address); singleSerververConfig.setPassword(pass); redissonClient = RedisUtils.getInstance().getRedisson(config); } } } return redissonClient; } }
这个类主要做的是创建于redis的连接与获取注解的相关信息,里面关于redisson的方法是参考的网上其他文章的,很多方法并没有用上,不过大家可以参考一下自己组合使用。接下来定义两个注解,一个方法级别的,一个参数级别的,方法级别的作用是标记需要加锁的方法,参数级别的是标记锁的key,
方法级别:
@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Inherited public @interface RedisLock { /** * 锁的key * 如果想添加非固定锁,可以在参数上添加@P4jSynKey注解,但是本参数是必写选项
* redis key的拼写规则为 "DistRedisLock+" + lockKey + @RedisLOckKey
*/ String lockKey(); /** * 持锁时间 * 单位毫秒,默认5秒
*/ long keepMills() default 5 * 1000; /** * 没有获取到锁时,等待时间 * @return */ long maxSleepMills() default 120 * 1000; }
参数级别:
@Target({ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Inherited public @interface RedisLockKey { /** * key的拼接顺序规则 */ int order() default 0; }
接下来就是AOP的实现了,
@Component @Aspect public class RedisLockAspect { private static Logger logger= LoggerFactory.getLogger(RedisLockAspect.class); @Value("${spring.redis.host}:${spring.redis.port}") String address; @Around("execution(* exercise..*(..))&& @annotation(RedisLock)") public Object lock(ProceedingJoinPoint point) throws Throwable { RLock lock = null; Object object = null; logger.info("into Aspect!"); try { RedisLock redisLock = getDistRedisLockInfo(point); RedisUtils redisUtils = RedisUtils.getInstance(); RedissonClient redissonClient = RedisUtils.createClient(address, null); String lockKey = redisUtils.getLockKey(point, redisLock.lockKey()); lock = redisUtils.getRLock(redissonClient, lockKey); if (lock != null) { Boolean status = lock.tryLock(redisLock.maxSleepMills(), redisLock.keepMills(), TimeUnit.MILLISECONDS); if (status) { object = point.proceed(); } } } finally { if (lock != null) { lock.unlock(); } } return object; } private RedisLock getDistRedisLockInfo(ProceedingJoinPoint point) { try { MethodSignature methodSignature = (MethodSignature) point.getSignature(); Method method = methodSignature.getMethod(); return method.getAnnotation(RedisLock.class); } catch (Exception e) { logger.info(e.getMessage()); } return null; } }
定义一个测试类:
@Component public class LockTest { private static int i = 0; @RedisLock(lockKey = "lockKey") public void add(@RedisLockKey(order = 1) String key, @RedisLockKey(order = 0) int key1) { i++; System.out.println("i=***************************************" + i); } }
用junit写个测试启用100个线程调用测试方法:
@RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest public class AOPTest { @Autowired LockTest lockTest; @Test public void testDistLockAop() throws InterruptedException { for (int i = 0; i < 100; i++) { new Thread(() -> { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } lockTest.add("***********testDistLockAop", 99); }).start(); } TimeUnit.SECONDS.sleep(20); } @Test public void testDistLock() throws InterruptedException { lockTest.add("============testDistLock", 111111); TimeUnit.SECONDS.sleep(10); } }
执行结果:
perfect! 一个简单的redis分布式锁已经完成了,整个项目是一个spring-boot的maven工程。另外因为我用的都是本地的数据库与redis,所以代码中连接redis的地方密码传的是null,有密码的需要换成密码。
个人习惯,喜欢看代码不喜欢看文字,所以解释的不够详细,但主要就三点,redisson的使用,AOP原理,注解实现,稍微研究一下这三块,以上代码还是很容易看懂的,代码的改进的地方还很多,锁的功能并不全,可重入,并发连接数量等还没有完善。只要仔细研究一下redisson这些都能找到。