基于redisson的分布式锁的简单注解实现

来源:互联网 发布:μtorrent mac 编辑:程序博客网 时间:2024/05/01 21:48

Redisson依赖:

<!--redisson-->
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>2.2.13</version>
</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<br/>     *     * @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();                SortedMap<Integer, String> keys = 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 <T>     * @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) @Inheritedpublic @interface RedisLock {    /**     * 锁的key     * 如果想添加非固定锁,可以在参数上添加@P4jSynKey注解,但是本参数是必写选项<br/>     * redis key的拼写规则为 "DistRedisLock+" + lockKey + @RedisLOckKey<br/>     */    String lockKey();    /**     * 持锁时间     * 单位毫秒,默认5<br/>     */    long keepMills() default 5 * 1000;    /**     * 没有获取到锁时,等待时间     * @return     */    long maxSleepMills() default 120 * 1000;}

参数级别:

@Target({ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Inheritedpublic @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;    }}

 

定义一个测试类:

@Componentpublic 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)@SpringBootTestpublic 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这些都能找到。
0 0