注解的方式实现redis分布式锁

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: 创建redisLock注解:import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.

创建redisLock注解:

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.concurrent.TimeUnit;

/**
 * redis锁注解
 * @author MAZHEN
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface RedisLock {
    String lockPrefix() default "";
    long timeOut() default 60;
    TimeUnit timeUnit() default TimeUnit.SECONDS;
}

拦截器逻辑:

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import org.aspectj.apache.bcel.classfile.Constant;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import com.spring.mongo.annotation.RedisLock;

/**
 * redis锁拦截器实现
 * @author MAZHEN
 */
@Aspect
@Component
public class RedisLockInterceptor {

    private static final Integer MAX_RETRY_COUNT = 10;
    private static final String LOCK_PRE_FIX = "lockPreFix";
    private static final String TIME_OUT = "timeOut";

    @Autowired
    private RedisManager redisManager;

    @Pointcut("@annotation(com.spring.mongo.annotation.RedisLock)")
    public void redisLockAspect(){}

    @Around("redisLockAspect()")
    public Map<String, Object> lockAroundAction(ProceedingJoinPoint proceeding){
        //获取redis锁
        Map<String, Object> getLockResult = this.getLock(proceeding,0,System.currentTimeMillis());
    }

    /**
     * 获取锁
     * @param proceeding
     * @return
     */
    private Map<String, Object> getLock(ProceedingJoinPoint proceeding,int count,long currentTime){
        //获取注解中的参数
        Map<String, Object> annotationArgs = this.getAnnotationArgs(proceeding);
        String lockPrefix = (String) annotationArgs.get(LOCK_PRE_FIX);
        long expire = (long) annotationArgs.get(TIME_OUT);
        String key = this.getFirstArg(proceeding);
        if(StringUtils.isEmpty(lockPrefix) || StringUtils.isEmpty(key)){
            return this.argErrResult("锁前缀或业务参数不能为空");
        }
        String lockName = lockPrefix+"_"+key;
        String value = String.valueOf(currentTime);
        if(CommonRedisUtils.setNx(lockName,value) == 1){
            //获取锁成功
            CommonRedisUtils.expire(lockName,expire);
            return this.buildSuccessResult();
        }else {
            //获取锁失败,为防止其它线程正在设置过时时间时误删,添加第一个条件
            if((System.currentTimeMillis()-currentTime>5000)
                    &&(CommonRedisUtils.ttl(lockName)<0
                            ||System.currentTimeMillis()-currentTime>expire)){
                //强制删除锁,并尝试再次获取锁
                CommonRedisUtils.delete(lockName);
                if(count<MAX_RETRY_COUNT){
                    return getLock(proceeding,count++,currentTime);
                }
            }
            return this.buildGetLockErrorResult("请重试!!!");
        }
    }

    /**
     * 获取锁参数
     * @param proceeding
     * @return
     */
    private Map<String, Object> getAnnotationArgs(ProceedingJoinPoint proceeding){
        Class target = proceeding.getTarget().getClass();
        Method[] methods = target.getMethods();
        String methodName = proceeding.getSignature().getName();
        for (Method method : methods) {
            if(method.getName().equals(methodName)){
                Map<String, Object> result = new HashMap<String, Object>();
                RedisLock redisLock = method.getAnnotation(RedisLock.class);
                result.put(LOCK_PRE_FIX,redisLock.lockPrefix());
                result.put(TIME_OUT, redisLock.timeUnit().toSeconds(redisLock.timeOut()));
                return result;
            }
        }
        return null;
    }

    /**
     * 获取第一个String类型的参数为锁的业务参数
     * @param proceeding
     * @return
     */
    public String getFirstArg(ProceedingJoinPoint proceeding){
        Object[] args = proceeding.getArgs();
        if(args != null && args.length>0){
            for (Object object : args) {
                String type = object.getClass().getName();
                if("java.lang.String".equals(type)){
                    return (String)object;
                }
            }
        }
        return null;
    }

    public Map<String, Object> argErrResult(String mes){
        Map<String, Object> result = new HashMap<String, Object>();
        //TODO
        //result.put("code", "9");
        result.put("msg", mes);
        return result;
    }

    public Map<String, Object> buildGetLockErrorResult(String mes){
        Map<String, Object> result = new HashMap<String, Object>();
        //TODO
        //result.put("code", "9");
        result.put("msg", mes);
        return result;
    }

    public Map<String, Object> buildSuccessResult(){
        Map<String, Object> result = new HashMap<String, Object>();
        //TODO
        //result.put("code", "1");
        result.put("msg", "处理成功");
        return result;
    }
}

使用方式:只需要在需要使用redis锁的方法上添加@RedisLock注解,并输入redis锁的前缀字段,过时时间和时间单位有默认值,而方法上的第一个String类型的参数为锁的key的第二段。

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
2月前
|
NoSQL Redis
基于Redis的高可用分布式锁——RedLock
这篇文章介绍了基于Redis的高可用分布式锁RedLock的概念、工作流程、获取和释放锁的方法,以及RedLock相比单机锁在高可用性上的优势,同时指出了其在某些特殊场景下的不足,并提到了ZooKeeper作为另一种实现分布式锁的方案。
81 2
基于Redis的高可用分布式锁——RedLock
|
27天前
|
存储 NoSQL Redis
SpringCloud基础7——Redis分布式缓存,RDB,AOF持久化+主从+哨兵+分片集群
Redis持久化、RDB和AOF方案、Redis主从集群、哨兵、分片集群、散列插槽、自动手动故障转移
SpringCloud基础7——Redis分布式缓存,RDB,AOF持久化+主从+哨兵+分片集群
|
6天前
|
缓存 NoSQL Java
Springboot自定义注解+aop实现redis自动清除缓存功能
通过上述步骤,我们不仅实现了一个高度灵活的缓存管理机制,还保证了代码的整洁与可维护性。自定义注解与AOP的结合,让缓存清除逻辑与业务逻辑分离,便于未来的扩展和修改。这种设计模式非常适合需要频繁更新缓存的应用场景,大大提高了开发效率和系统的响应速度。
21 2
|
10天前
|
NoSQL 安全 关系型数据库
20)用 Redis 实现分布式锁
20)用 Redis 实现分布式锁
22 0
|
2月前
|
NoSQL Go Redis
用 Go + Redis 实现分布式锁
用 Go + Redis 实现分布式锁
|
26天前
|
canal 缓存 NoSQL
Redis缓存与数据库如何保证一致性?同步删除+延时双删+异步监听+多重保障方案
根据对一致性的要求程度,提出多种解决方案:同步删除、同步删除+可靠消息、延时双删、异步监听+可靠消息、多重保障方案
Redis缓存与数据库如何保证一致性?同步删除+延时双删+异步监听+多重保障方案
|
2月前
|
缓存 NoSQL Java
Redis深度解析:解锁高性能缓存的终极武器,让你的应用飞起来
【8月更文挑战第29天】本文从基本概念入手,通过实战示例、原理解析和高级使用技巧,全面讲解Redis这一高性能键值对数据库。Redis基于内存存储,支持多种数据结构,如字符串、列表和哈希表等,常用于数据库、缓存及消息队列。文中详细介绍了如何在Spring Boot项目中集成Redis,并展示了其工作原理、缓存实现方法及高级特性,如事务、发布/订阅、Lua脚本和集群等,帮助读者从入门到精通Redis,大幅提升应用性能与可扩展性。
61 0
|
2天前
|
缓存 NoSQL 算法
解决Redis缓存雪崩问题的有效方法
解决Redis缓存雪崩问题的有效方法
9 1
|
2天前
|
缓存 NoSQL 关系型数据库
redis和缓存及相关问题和解决办法 什么是缓存预热、缓存穿透、缓存雪崩、缓存击穿
本文深入探讨了Redis缓存的相关知识,包括缓存的概念、使用场景、可能出现的问题(缓存预热、缓存穿透、缓存雪崩、缓存击穿)及其解决方案。
10 0
redis和缓存及相关问题和解决办法 什么是缓存预热、缓存穿透、缓存雪崩、缓存击穿
|
10天前
|
存储 缓存 NoSQL
解决Redis缓存击穿问题的技术方法
解决Redis缓存击穿问题的技术方法
30 2